miracle just wanna be better

lunece

2019-10-08
miracle

全文检索索引文件的结构

  • 分词:将一段,一句文本信息,计算整理成片段的过程叫做分词 例如:中华人民共和国在2019年举国欢庆
    中华,人民,共和国,中华人品共和国,欢庆,举国欢庆
  • 词项:分词计算之后的每个片段都是一个词项 (term)
  • 文档(document):将源数据整理成的索引文件中的单位数据结构,可以根据业务数据,去整理大量的文档对象用来存储数据源信息.
  • 域(field):每个document封装数据的基本字段,根据业务数据灵活拼接的.

    倒排索引的计算步骤

源数据:网页
国庆大阅兵,尽显中华雄风
10月1日在北京举行的国庆70周年阅兵备受各方关注
图片1http://image.jt.com/**1
出版社:新华网

  • 封装对象document id:1
    title:国庆大阅兵,尽显中华雄风
    content:10月1日在北京举行的国庆70周年阅兵备受各方关注
    image:http://image.jt.com/**
    publisher:出版社:新华网
  • 计算分词 对应每个不同的document对象中属性和文本值要进行分词计算(包含着大量的参数属性,例如,文档id,域名称,偏移量,位移,频率等)
    国庆(1,title),阅兵(1,title),中华(1,title) 10(1,content) 北京(1,content),国庆 (1,content),阅兵(1,content)
  • 分词合并 国庆([1,2],[title,content]),阅兵
    (1,[title,content]),北京
    ([1,2],content),出版社
    ([1,2],publisher)

  • 将document和分词合并输出到索引文件中

分词器

  • SimpleAnalyzer 简单分词器
  • StandardAnalyzer 标准分词器
  • SmartChineseAnalyzer 智能中文分词器
  • 常用的中文分词器IKAnalyzer,支持扩展和停用

分词器测试

pom导入依赖

IK分析器依赖,在本地文件中,用scope和systemPath导入,交给maven管理

<dependency> 
	<groupId>cn.tedu</groupId>
	<artifactId>ik-analyzer</artifactId>
	<version>u6</version>
	<scope>system</scope>
	<systemPath>D:\IKAnalyzer2012_u6.jar</systemPath>
</dependency>
<dependency> <!-- 查询相关jar包 -->
	<groupId>org.apache.lucene</groupId>
	<artifactId>lucene-queryparser</artifactId>
	<version>6.0.0</version>
</dependency>
<dependency> <!-- lucene自带只能中文分词器jar包 -->
	<groupId>org.apache.lucene</groupId>
	<artifactId>lucene-analyzers-smartcn</artifactId>
	<version>6.0.0</version>
</dependency>
<dependency> <!-- 测试用到的lucene工具包 -->
	<groupId>org.apache.lucene</groupId>
	<artifactId>lucene-analyzers-common</artifactId>
	<version>6.0.0</version>
</dependency>
<dependency> <!-- 测试用到的lucene核心包 -->
	<groupId>org.apache.lucene</groupId>
	<artifactId>lucene-core</artifactId>
	<version>6.0.0</version>
</dependency>

分词器

public class AnalyzerTest {
	/*处理分词器的分词过程
	 * 打印阐释文本属性 词项
	 */
	public void printTerm(Analyzer analyzer,String msg) throws IOException{
		//获取字符串流
		StringReader reader=new StringReader(msg);
		//可以通过传递的analyzer对象调用方法
		//不同实现类底层分词逻辑不同
		TokenStream tokenStream=analyzer.tokenStream("test",reader);
		//fieldName:域属性名称,分词器测试没有具体意义
		//将分词的文本属性打印此项的词,需要指针重置
		tokenStream.reset();
		CharTermAttribute attribute=tokenStream.getAttribute(CharTermAttribute.class);
		while(tokenStream.incrementToken()){
			System.out.println(attribute.toString());
			//词项
		}
	}
	//实现测试方法,传递使用不同的分词器
	@Test
	public void run() throws IOException{
		//准备一句计算分词的文本
		String msg="你喜欢 王者荣耀,还是 英雄联盟";
		//使用不同的分词器实现类
		Analyzer a1=new SimpleAnalyzer();//根据标点切分
		Analyzer a2=new WhitespaceAnalyzer();//根据空格,句,字,词
		Analyzer a3=new StandardAnalyzer();//根据字符切分
		Analyzer a4=new SmartChineseAnalyzer();//根据中文词进行切分
		Analyzer a5=new IKAnalyzer6x();
		System.out.println("***********简单*************");
		printTerm(a1,msg);
		System.out.println("***********空格*************");
		printTerm(a2,msg);
		System.out.println("***********标准*************");
		printTerm(a3,msg);
		System.out.println("***********中文*************");
		printTerm(a4,msg);
		System.out.println("***********IK*************");
		printTerm(a5,msg);
	}
}

2KI分词器

  • 扩展:根据语言环境配置新的词语
  • 停用:根据业务需求将一些无效的,非法的,侮辱性

IKAnalyzer.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">  
<properties>  
	<comment>IK Analyzer 扩展配置</comment>
	<!--用户可以在这里配置自己的扩展字典 
	<entry key="ext_dict">ext.dic;</entry> 
	-->
	<!--用户可以在这里配置自己的扩展停止词字典-->
	<entry key="ext_dict">ext.dic;</entry> 
	<entry key="ext_stopwords">stopword.dic;</entry> 
</properties>

ext.dic

拓展字典

王者荣耀
英雄联盟

stopword.dic

停止字典

还是
喜欢

索引文件创建

public class CreateIndex {
	/*
	 * 1 确定索引的输出位置
	 * 2 准备一个具备使用Ik分词器输出流对象writer
	 * 3 内存的ducument(读取原数据封装)数据封装
	 * 4 输出写入到索引文件
	 * 域的类型(文本,数字,存储的索引数据)
	 */
	@Test
	public void createIndex() throws IOException{
		//确定一个输出的文件夹位置作为索引存在
		Path path=Paths.get("d://index02");
		//交给lucene的目录工具类
		FSDirectory dir=FSDirectory.open(path);
		//创建输出对象对应的配置对象
		IndexWriterConfig config=new IndexWriterConfig(new IKAnalyzer6x());
		//定义写出时的方式 append create create_or_append
		config.setOpenMode(OpenMode.CREATE);
		//配合dir路径创建writer
		IndexWriter writer= new IndexWriter(dir,config);
		//封装document
		Document doc1=new Document();
		Document doc2=new Document();
		//title content image publisher
		//name:域的名称
		//title:读取源数据的值
		//store:是否进行存储(document的数据输出到索引文件,并不是所有源数据都需要存储在索引中)
		doc1.add(new TextField("title","国庆大阅兵",Store.YES));
		doc1.add(new TextField("content","10月1日在北京举行的国庆周年",Store.YES));
		doc1.add(new TextField("image","http://image.jt.com",Store.YES));
		doc1.add(new TextField("publisher","新计划网",Store.YES));
		doc1.add(new DoublePoint("price",500));
		doc1.add(new StringField("price","500元",Store.YES));
		
		doc2.add(new TextField("title","国庆假期",Store.YES));
		doc2.add(new TextField("content","10月1日北京开始拥堵",Store.YES));
		doc2.add(new StringField("image","http://image.jt.com/adf",Store.YES));
		doc2.add(new TextField("publisher","娱乐网",Store.YES));
		doc2.add(new DoublePoint("price",6000));
		writer.addDocument(doc1);
		writer.addDocument(doc2);
		//直接写出
		writer.commit();
	}
}

索引文件搜索

public class SercherIndex {
	/*
	 * lucune中将查询的功能封装到一个规范(接口类)Query
	 * 可以根据不同的查询功能使用不同的实现类
	 */
	/*
	 * 1 指定查询的索引
	 * 2 搜索对象的创建,使用一个输入流reader
	 * 3 封装查询条件query,不同功能,不同的实现类
	 * 4 浅查询实现数据的遍历使用
	 */
	//词项查询
	@Test
	public void termQuery() throws IOException{
		//指定d://index01
		Path path=Paths.get("d://index02");
		FSDirectory dir=FSDirectory.open(path);
		//获取输入流
		IndexReader reader=DirectoryReader.open(dir);
		//利用输入流,获取搜索对象
		IndexSearcher search=new IndexSearcher(reader);
		//根据不同的查询条件,封装query实现类对象
		//TermQuery
		//先提供一个词项 fld域名,关键字条件
		Term term=new Term("content","北京");
		Query query=new TermQuery(term);
		
		//利用浅查询 获取结果集
		TopDocs topDocs=search.search(query,10);//查前10条
		System.out.println("总查询条数:"+topDocs.totalHits);
		System.out.println("其中最大评分:"+topDocs.getMaxScore());
		//返回的是一个封装搜索计算浅查询,和对应Document数据
		//评分的对象,通过topDocs获取封装了documentid的评分
		ScoreDoc[] docs=topDocs.scoreDocs;//查询的条数
		for(ScoreDoc scoreDoc:docs){
			//获取document的id值
			System.out.println("document的id值为"+scoreDoc.doc);
			//读取document
			Document doc=search.doc(scoreDoc.doc);
			//读取域属性
			System.out.println("title:"+doc.get("title"));
			System.out.println("content:"+doc.get("content"));
			System.out.println("price:"+doc.get("price"));
			System.out.println("publisher:"+doc.get("publisher"));
			System.out.println("image:"+doc.get("image"));
		}
	}
	//布尔查询条件
	@Test
	public void booleanQuery() throws Exception {
		//指定d://index01
				Path path=Paths.get("d://index02");
				FSDirectory dir=FSDirectory.open(path);
				//获取输入流
				IndexReader reader=DirectoryReader.open(dir);
				//利用输入流,获取搜索对象
				IndexSearcher search=new IndexSearcher(reader);
				
				//布尔查询
				Query query1=new TermQuery(new Term("title","国庆"));
				Query query2=new TermQuery(new Term("content","周年"));
				//封装布尔子条件
				BooleanClause bc1=new BooleanClause(query1,Occur.MUST);
				BooleanClause bc2=new BooleanClause(query2,Occur.MUST);
				//利用子条件bc1 bc2来创建布尔条件
				Query query=new BooleanQuery.Builder().add(bc1).add(bc2).build();
				
				//利用浅查询 获取结果集
				TopDocs topDocs=search.search(query,10);//查前10条
				System.out.println("总查询条数:"+topDocs.totalHits);
				System.out.println("其中最大评分:"+topDocs.getMaxScore());
				//返回的是一个封装搜索计算浅查询,和对应Document数据
				//评分的对象,通过topDocs获取封装了documentid的评分
				ScoreDoc[] docs=topDocs.scoreDocs;//查询的条数
				for(ScoreDoc scoreDoc:docs){
					//获取document的id值
					System.out.println("document的id值为"+scoreDoc.doc);
					//读取document
					Document doc=search.doc(scoreDoc.doc);
					//读取域属性
					System.out.println("title:"+doc.get("title"));
					System.out.println("content:"+doc.get("content"));
					System.out.println("price:"+doc.get("price"));
					System.out.println("publisher:"+doc.get("publisher"));
					System.out.println("image:"+doc.get("image"));
				}
	}
	//范围查询
	@Test
	public void rangeQuery()throws Exception{
		//指定d://index01
		Path path=Paths.get("d://index02");
		FSDirectory dir=FSDirectory.open(path);
		//获取输入流
		IndexReader reader=DirectoryReader.open(dir);
		//利用输入流,获取搜索对象
		IndexSearcher search=new IndexSearcher(reader);
		
		//范围查询,price doublepoint
		Query query =DoublePoint.newRangeQuery("price", 500, 1000);
		
		//利用浅查询 获取结果集
		TopDocs topDocs=search.search(query,10);//查前10条
		System.out.println("总查询条数:"+topDocs.totalHits);
		System.out.println("其中最大评分:"+topDocs.getMaxScore());
		//返回的是一个封装搜索计算浅查询,和对应Document数据
		//评分的对象,通过topDocs获取封装了documentid的评分
		ScoreDoc[] docs=topDocs.scoreDocs;//查询的条数
		for(ScoreDoc scoreDoc:docs){
			//获取document的id值
			System.out.println("document的id值为"+scoreDoc.doc);
			//读取document
			Document doc=search.doc(scoreDoc.doc);
			//读取域属性
			System.out.println("title:"+doc.get("title"));
			System.out.println("content:"+doc.get("content"));
			System.out.println("price:"+doc.get("price"));
			System.out.println("publisher:"+doc.get("publisher"));
			System.out.println("image:"+doc.get("image"));
		}
	}
	//多域查询
	@Test
	public void multiFieldQuery()throws Exception{
		//指定d://index01
		Path path=Paths.get("d://index02");
		FSDirectory dir=FSDirectory.open(path);
		//获取输入流
		IndexReader reader=DirectoryReader.open(dir);
		//利用输入流,获取搜索对象
		IndexSearcher search=new IndexSearcher(reader);
		//多域查询
		//指定多个域
		String[] fields={"title","content"};
		//封装查询字符串条件,利用多域查询的解析器,对条件先进性分词计算
		MultiFieldQueryParser parser=new MultiFieldQueryParser(fields, new IKAnalyzer6x());
		Query query=parser.parse("北京国庆大阅 兵"); 
		//底层封装多个TermQuery title 北京 title 国 庆 title 大 title 阅兵 
		//利用浅查询 获取结果集
		TopDocs topDocs=search.search(query,10);//查前10条
		System.out.println("总查询条数:"+topDocs.totalHits);
		System.out.println("其中最大评分:"+topDocs.getMaxScore());
		//返回的是一个封装搜索计算浅查询,和对应Document数据
		//评分的对象,通过topDocs获取封装了documentid的评分
		ScoreDoc[] docs=topDocs.scoreDocs;//查询的条数
		for(ScoreDoc scoreDoc:docs){
			//获取document的id值
			System.out.println("document的id值为"+scoreDoc.doc);
			//读取document
			Document doc=search.doc(scoreDoc.doc);
			//读取域属性
			System.out.println("title:"+doc.get("title"));
			System.out.println("content:"+doc.get("content"));
			System.out.println("price:"+doc.get("price"));
			System.out.println("publisher:"+doc.get("publisher"));
			System.out.println("image:"+doc.get("image"));
		}
	}
}
  • store.YES
    当业务需求不需要在查询数据时使用返回 的数据,可以使用Store.NO将数据不保存
  • TextField/StringField TextField的数据需要进行分词器的分词计算得到词项结果 StringField不需要进行分词计算(字符串的主键,web的连接地址,都不需要进行分词计算)
  • DoublePoint 可以利用数字特性进行范围搜索
  • MUST 必须包含
  • MUST_NOT 必须不包含
  • FILTER:=MUST 这个条件对应的所有结果不参加评分
  • SHOULD: 和MUST FILTER同时存在时,不起作用

上一篇 mycat

下一篇 elasticsearch

Comments

Content