论坛首页 Java企业应用论坛

发布IK Analyzer 3.0 中文分词器

浏览 64762 次
该帖已经被评为精华帖
作者 正文
   发表时间:2010-01-05  
细粒度分词,很是钦佩。能有最新的源代码吗?
0 请登录后投票
   发表时间:2010-01-05  
Rexwong 写道
细粒度分词,很是钦佩。能有最新的源代码吗?


有啊,开源当然有源码啊。google code地址上去就有了,可以使用svn,也有打包好的。
0 请登录后投票
   发表时间:2010-01-06   最后修改:2010-01-06
linliangyi2007 写道
引用
请教一个问题,比如我对一个字符串 "硬盘驱动器" 做索引了以后, 搜索 "硬" 或者 "盘" 都不能搜索到,但是如果搜索"硬盘"就可以,这是什么问题呢?


lucene是按照分词切分的结果进行完整匹配查询的, "硬盘驱动器"被切分成“硬盘”“驱动”“驱动器”,但没有“硬" 和 "盘",(那样是单字切分),因此搜索不到,这个是lucene的基础概念,切记。


要是今年多了一个新词叫“盘驱”,明年多了一个新词叫“动器”,怎么办?
如果客户强烈要求搜“硬”时能搜到记录,怎么办?
0 请登录后投票
   发表时间:2010-01-06  
fuwang 写道
linliangyi2007 写道
引用
请教一个问题,比如我对一个字符串 "硬盘驱动器" 做索引了以后, 搜索 "硬" 或者 "盘" 都不能搜索到,但是如果搜索"硬盘"就可以,这是什么问题呢?


lucene是按照分词切分的结果进行完整匹配查询的, "硬盘驱动器"被切分成“硬盘”“驱动”“驱动器”,但没有“硬" 和 "盘",(那样是单字切分),因此搜索不到,这个是lucene的基础概念,切记。


要是今年多了一个新词叫“盘驱”,明年多了一个新词叫“动器”,怎么办?
如果客户强烈要求搜“硬”时能搜到记录,怎么办?


我觉得你这个需求是为杜撰而杜撰的,首先就与中文的特性违背了。每种语言都有它的切分特性,英文是通过空格分隔,而中文的特性我们采用了词库。这就意味着当“硬盘”是一个词语的时候,“硬”与“硬盘”在词义上是毫无关系的。分词器只可能尽可能适应于中文的常规语法切分,而不可能你想怎么分就怎么分。

lucene索引的特性也决定了它的最高效率是“完全索引匹配”,当然你可以使用“前缀匹配”,但这个与lucene的倒排索引设计理念是背道而驰的。
0 请登录后投票
   发表时间:2010-01-06  
linliangyi2007 写道
Rexwong 写道
细粒度分词,很是钦佩。能有最新的源代码吗?


有啊,开源当然有源码啊。google code地址上去就有了,可以使用svn,也有打包好的。

晕了,在你的blog里下了个rar,没有源码,就没去code Google看看。懒了,懒了
另外,请教一下,在“神话电视连续剧”搜“神话电视”没有搜到。“神话电视”应该是切成“神话”“电视”了。导致这个结果是个什么原因呢?
0 请登录后投票
   发表时间:2010-01-06  
Rexwong 写道
linliangyi2007 写道
Rexwong 写道
细粒度分词,很是钦佩。能有最新的源代码吗?


有啊,开源当然有源码啊。google code地址上去就有了,可以使用svn,也有打包好的。

晕了,在你的blog里下了个rar,没有源码,就没去code Google看看。懒了,懒了
另外,请教一下,在“神话电视连续剧”搜“神话电视”没有搜到。“神话电视”应该是切成“神话”“电视”了。导致这个结果是个什么原因呢?


有一种可能,你的Analyzer设置成最大切分模式,“神话电视连续剧”可能被切分为
神话电视连续剧
0-2 : 神话 : 	CJK_NORMAL
2-7 : 电视连续剧 : 	CJK_NORMAL

这样你使用“神话“and”电视”搜索就搜不到,请使用IKAnalyzer默认的构造函数,或者IKAnalyzer(false),这样的切分结果是
神话电视连续剧
0-2 : 神话 : 	CJK_NORMAL
2-7 : 电视连续剧 : 	CJK_NORMAL
2-4 : 电视 : 	CJK_NORMAL
4-7 : 连续剧 : 	CJK_NORMAL
4-6 : 连续 : 	CJK_NORMAL

0 请登录后投票
   发表时间:2010-01-11  
楼主,我想问下,对于数据冗余是怎么处理的?

例如:
我对数据库一张表的每个字段都建立了索引,但我搜索的时候,不想针对一个字段就行搜索,是在所有字段中搜索,
这样搜索出来的结果就会出现冗余,数据库一条记录可能会被搜索到多次

我想问的是,有什么办法能过滤这些冗余的数据

例如:
在检搜索引1中搜索到了数据2(索引1中存在数据2中一个字段的索引),那么在检搜索引2的时候就过滤掉数据2(索引2中存在数据2中一个字段的索引),不对数据2进行搜索
0 请登录后投票
   发表时间:2010-01-11  
swprogrammer 写道
楼主,我想问下,对于数据冗余是怎么处理的?

例如:
我对数据库一张表的每个字段都建立了索引,但我搜索的时候,不想针对一个字段就行搜索,是在所有字段中搜索,
这样搜索出来的结果就会出现冗余,数据库一条记录可能会被搜索到多次

我想问的是,有什么办法能过滤这些冗余的数据

例如:
在检搜索引1中搜索到了数据2(索引1中存在数据2中一个字段的索引),那么在检搜索引2的时候就过滤掉数据2(索引2中存在数据2中一个字段的索引),不对数据2进行搜索


你说的是数据库中才会有这个问题的,Lucene本身就是全文搜索(全字段搜索)的,不会出现结果重复。
0 请登录后投票
   发表时间:2010-01-12  
package lucene.database;

import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TopDocs;
import org.wltea.analyzer.lucene.IKAnalyzer;
import org.wltea.analyzer.lucene.IKQueryParser;
import org.wltea.analyzer.lucene.IKSimilarity;

public class LuceneDemo {

	// 索引存放目录
	private String directory = "d:/index/";
	
	//根据哪个字段去查询
	public String searchField = "";
	
	//搜索相似度最高的几条记录
	public int n = 5;
	
	public LuceneDemo(String searchField, int n)
	{
		this.searchField = searchField;
		this.n = n;
	}

	/**
	 * 查询数据并创建索引
	 * 
	 * @param rs
	 *            数据集
	 */
	public void Index(ResultSet rs) {
		try {
			IndexWriter writer = new IndexWriter(directory, getAnalyzer(), true);
			int mergeFactor = 1000;
			int maxMergeDocs = Integer.MAX_VALUE; 
			writer.setMergeFactor(mergeFactor);
			writer.setMaxMergeDocs(maxMergeDocs);

			while (rs.next()) {
				Document doc = new Document();
				doc.add(new Field("id", rs.getString("au_id"), Field.Store.YES,
						Field.Index.ANALYZED));
				doc.add(new Field("name", rs.getString("au_name"),
						Field.Store.YES, Field.Index.ANALYZED));
				doc.add(new Field("phone", rs.getString("phone"),
						Field.Store.YES, Field.Index.ANALYZED));
				doc.add(new Field("address", rs.getString("address"),
						Field.Store.YES, Field.Index.ANALYZED));
				doc.add(new Field("City", rs.getString("city"),
						Field.Store.YES, Field.Index.ANALYZED));
				writer.addDocument(doc);
			}
			writer.optimize();
			writer.close();
		} catch (IOException e) {
			System.out.println(e);
		} catch (SQLException e) {
			System.out.println(e);
		}
	}

	public TopDocs seacher(String queryString) {
		TopDocs topDocs = null;
		try {
			
			//使用IKQueryParser查询分析器构造Query对象
			Query query = IKQueryParser.parse("city", queryString);
			if(searchField != null && !"".equals(searchField))
			{
				query = IKQueryParser.parse("city", queryString);
			}else
			{
				query = IKQueryParser.parseMultiField(new String[]{"id","name","phone","address","City"}, queryString);
			}
			
			//搜索相似度最高的5条记录
			topDocs = getIndexSearcher().search(query , n);
		} catch (Exception e) {
			System.out.print(e);
		}
		return topDocs;
	}

	/**
	 * 获取分词器
	 * 
	 * @return 分词器
	 */
	public Analyzer getAnalyzer() {
		// 创建中文分词器并返回
		return new IKAnalyzer();
	}
	
	/**
	 * 获取搜索器
	 * @return 搜索器
	 * @throws CorruptIndexException
	 * @throws IOException
	 */
	public IndexSearcher getIndexSearcher() throws CorruptIndexException, IOException
	{
		//实例化搜索器   
		IndexSearcher isearcher = new IndexSearcher(directory);			
		//在索引器中使用IKSimilarity相似度评估器
		isearcher.setSimilarity(new IKSimilarity());
		return isearcher;
	}
}


package lucene.database;

import java.io.IOException;
import java.util.Date;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;

public class Test {

	public static void main(String[] args) throws CorruptIndexException, IOException {

		DatabaseLuceneDemo dld = new DatabaseLuceneDemo();
		Date start = new Date();
		LuceneDemo luceneDemo = new LuceneDemo("", 5);
		luceneDemo.Index(dld.query("select * from Authors"));

		TopDocs topDocs = luceneDemo.seacher("023");

		Date end = new Date();
		System.out.println(end.getTime() - start.getTime());
		System.out.println("命中:" + topDocs.totalHits);
		//结果
		ScoreDoc[] scoreDocs = topDocs.scoreDocs;
		
		//获取查询分析器
		IndexSearcher isearcher = luceneDemo.getIndexSearcher();
		
		for (int i = 0; i < topDocs.totalHits; i++){
			Document targetDoc = isearcher.doc(scoreDocs[i].doc);
			//targetDoc.getField("id").setValue("fdafdf");
			System.out.println("内容:" + targetDoc.get("id") + " " + targetDoc.get("name") + " " + targetDoc.get("phone")
					 + " " + targetDoc.get("address") + " " + targetDoc.get("City"));
		}
	}
	
}



结果:
2172
命中:996
内容:1 周海诺 023-88888888 11111 aaaa
内容:3 彭珊珊 023-88833338 3333 cccc
内容:4 刘敏 023-88899999 4444 dddd
内容:9 999 023-99999999 9999 99999
内容:9 999 023-99999999 9999 99999
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
	at lucene.database.Test.main(Test.java:33)



  这里还有个下标越界错误,这个我能搞懂, 但这个冗余是能过滤吗?怎么过滤?
0 请登录后投票
   发表时间:2010-01-12  
swprogrammer 写道
package lucene.database;

import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TopDocs;
import org.wltea.analyzer.lucene.IKAnalyzer;
import org.wltea.analyzer.lucene.IKQueryParser;
import org.wltea.analyzer.lucene.IKSimilarity;

public class LuceneDemo {

	// 索引存放目录
	private String directory = "d:/index/";
	
	//根据哪个字段去查询
	public String searchField = "";
	
	//搜索相似度最高的几条记录
	public int n = 5;
	
	public LuceneDemo(String searchField, int n)
	{
		this.searchField = searchField;
		this.n = n;
	}

	/**
	 * 查询数据并创建索引
	 * 
	 * @param rs
	 *            数据集
	 */
	public void Index(ResultSet rs) {
		try {
			IndexWriter writer = new IndexWriter(directory, getAnalyzer(), true);
			int mergeFactor = 1000;
			int maxMergeDocs = Integer.MAX_VALUE; 
			writer.setMergeFactor(mergeFactor);
			writer.setMaxMergeDocs(maxMergeDocs);

			while (rs.next()) {
				Document doc = new Document();
				doc.add(new Field("id", rs.getString("au_id"), Field.Store.YES,
						Field.Index.ANALYZED));
				doc.add(new Field("name", rs.getString("au_name"),
						Field.Store.YES, Field.Index.ANALYZED));
				doc.add(new Field("phone", rs.getString("phone"),
						Field.Store.YES, Field.Index.ANALYZED));
				doc.add(new Field("address", rs.getString("address"),
						Field.Store.YES, Field.Index.ANALYZED));
				doc.add(new Field("City", rs.getString("city"),
						Field.Store.YES, Field.Index.ANALYZED));
				writer.addDocument(doc);
			}
			writer.optimize();
			writer.close();
		} catch (IOException e) {
			System.out.println(e);
		} catch (SQLException e) {
			System.out.println(e);
		}
	}

	public TopDocs seacher(String queryString) {
		TopDocs topDocs = null;
		try {
			
			//使用IKQueryParser查询分析器构造Query对象
			Query query = IKQueryParser.parse("city", queryString);
			if(searchField != null && !"".equals(searchField))
			{
				query = IKQueryParser.parse("city", queryString);
			}else
			{
				query = IKQueryParser.parseMultiField(new String[]{"id","name","phone","address","City"}, queryString);
			}
			
			//搜索相似度最高的5条记录
			topDocs = getIndexSearcher().search(query , n);
		} catch (Exception e) {
			System.out.print(e);
		}
		return topDocs;
	}

	/**
	 * 获取分词器
	 * 
	 * @return 分词器
	 */
	public Analyzer getAnalyzer() {
		// 创建中文分词器并返回
		return new IKAnalyzer();
	}
	
	/**
	 * 获取搜索器
	 * @return 搜索器
	 * @throws CorruptIndexException
	 * @throws IOException
	 */
	public IndexSearcher getIndexSearcher() throws CorruptIndexException, IOException
	{
		//实例化搜索器   
		IndexSearcher isearcher = new IndexSearcher(directory);			
		//在索引器中使用IKSimilarity相似度评估器
		isearcher.setSimilarity(new IKSimilarity());
		return isearcher;
	}
}


package lucene.database;

import java.io.IOException;
import java.util.Date;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;

public class Test {

	public static void main(String[] args) throws CorruptIndexException, IOException {

		DatabaseLuceneDemo dld = new DatabaseLuceneDemo();
		Date start = new Date();
		LuceneDemo luceneDemo = new LuceneDemo("", 5);
		luceneDemo.Index(dld.query("select * from Authors"));

		TopDocs topDocs = luceneDemo.seacher("023");

		Date end = new Date();
		System.out.println(end.getTime() - start.getTime());
		System.out.println("命中:" + topDocs.totalHits);
		//结果
		ScoreDoc[] scoreDocs = topDocs.scoreDocs;
		
		//获取查询分析器
		IndexSearcher isearcher = luceneDemo.getIndexSearcher();
		
		for (int i = 0; i < topDocs.totalHits; i++){
			Document targetDoc = isearcher.doc(scoreDocs[i].doc);
			//targetDoc.getField("id").setValue("fdafdf");
			System.out.println("内容:" + targetDoc.get("id") + " " + targetDoc.get("name") + " " + targetDoc.get("phone")
					 + " " + targetDoc.get("address") + " " + targetDoc.get("City"));
		}
	}
	
}



结果:
2172
命中:996
内容:1 周海诺 023-88888888 11111 aaaa
内容:3 彭珊珊 023-88833338 3333 cccc
内容:4 刘敏 023-88899999 4444 dddd
内容:9 999 023-99999999 9999 99999
内容:9 999 023-99999999 9999 99999
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
	at lucene.database.Test.main(Test.java:33)



  这里还有个下标越界错误,这个我能搞懂, 但这个冗余是能过滤吗?怎么过滤?


哥哥,这个就是你取索引的方法有问题了啊。我不是很清楚你对结果的查询要求,所以没法给你完整的建议。但从分词器的角度上说,它已经圆满完成任务了。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics