玩转搜索(一)-- 全文检索与Lucene实现

全文检索的概念

我们生活中的数据总体分为两种:结构化数据和非结构化数据。

  • 结构化数据:指具有固定格式或有限长度的数据,如关系数据库等。
  • 非结构化数据:指不定长或无固定格式的数据,如邮件、文档等。

非结构化数据又称为全文数据。

按照数据的分类,搜索也分为两种:

  • 对结构化数据的搜索:如数据库的搜索,windows的搜索。
  • 对非结构化数据的搜索:如利用搜索引擎搜索大量内容。

对非结构化数据也即全文数据的搜索主要有两种方法:顺序扫描法和反向索引法。

  • 顺序扫描法:顺序的扫描每个文档内容,看看是否有要搜索的关键字,实现查找文档的功能,也就是根据文档找词。
  • 反向索引法:就是提前将搜索的关键字建成索引,然后再根据索引查找文档,也就是根据词找文档。

这种先建立索引再对索引进行搜索文档的过程就叫全文检索(Full-text Search)。

全文检索技术

主流的java全文检索技术主要有:Lucene、Solr、ElasticSearch(ES)。

  • Lucene:是一个开源的全文检索引擎工具包,但它不是一个完整的全文检索引擎,需要使用者二次开发。如果使用该技术实现,需要对Lucene的API和底层原理非常了解,而且需要编写大量的java代码。
  • Solr:是一个基于Lucene实现的搜索应用服务器,可以使用rest方式的http请求,进行远程API的调用。
  • ElasticSearch:是一个基于Lucene的搜索服务器,可以使用rest方式的http请求,进行远程API的调用。

ElasticSearch与Solr的比较总结

  • 两者安装都很简单;
  • Solr利用Zookeeper进行分布式管理,而ES自身带有分布式协调管理功能;
  • Solr支持更多格式数据,而ES只支持json格式;
  • Solr官方提供的功能更多,而ES本身更注重核心功能,高级功能由很多第三方插件提供;
  • Solr在传统的搜索应用中表现较好,而ES在处理实时搜索时效率更好。

Solr是传统搜索应用的有力解决方案,但ES更适用于新兴的实时搜索应用。

全文检索的流程分析

主要分为两大流程:创建索引、搜索索引

  • 创建索引:将数据提取信息,创建索引的过程。
  • 搜索索引:根据查询请求,搜索创建的索引,返回结果的过程。

创建索引流程

获得文档

获取需要搜索的原始信息,这个过程就是信息采集。采集数据的目的是为了将原始内容存除到Document对象中。

创建文档

目的是统一数据格式(Document),方便文档分析。一个Document文档中包括多个Field域,Field中存储内容。

分析文档

对Field域进行分析。主要通过分词组件(Tokenizer)和语言处理组件(Linguistic Processor)完成。最终得到的结果为词(Term),Term也是索引库的最小单位。

索引文档

将Term传为给索引组件(Indexer)。Indexer主要实现创建Term字典,排序Term字典,合并Term字典。

搜索索引流程

查询语句格式如下:

域名:关键字,比如name:hello
域名:[min TO max],比如price:[100 TO 200]

多个查询语句之前,使用关键字AND、OR、NOT表示逻辑关系。

词法分析

最终得到搜索关键字列表。

语法分析

得到每个关键字之前的逻辑关联。

搜索索引

在索引表中,根据关键字查出文档链表。对文档链表进行逻辑合并计算操作。

排序

根据相关性对结果进行排序。影响相关度打分的因素:

  • Term Frequency(tf):tf越高,权重越高。
  • Document Frequency(df):df越高,权重越低。
  • 人为设置Field的Boost加权值。

Lucene的Java代码实现

创建索引

		// 1. 数据采集:模拟一个Item列表,包括id、name、price、pic、description这些字段List itemList = itemService.queryItemList();// 2. 创建Document文档对象List documents = new ArrayList<>();for (Item item : itemList) {Document document = new Document();// Document文档中添加Field域// 商品Id// Store.YES:表示存储到文档域中document.add(new StoredField("id", item.getId()));// 商品名称document.add(new TextField("name", item.getName(), Store.YES));// 商品价格document.add(new DoubleField("price", item.getPrice(), Store.YES));// 商品图片地址document.add(new StoredField("pic", item.getPic()));// 商品描述document.add(new TextField("description", item.getDescription(), Store.NO));// 把Document放到list中documents.add(document);}// 指定分词器:IKAnalyzer为中文分词器Analyzer analyzer = new IKAnalyzer();// 配置文件IndexWriterConfig iwc = new IndexWriterConfig(Version.LUCENE_4_10_4, analyzer);// 指定索引库路径String indexPath = "D:\\lucene\\";// 指定索引库对象Directory dir = FSDirectory.open(new File(indexPath));// 创建索引写对象// 3. 分析索引并创建索引IndexWriter writer = new IndexWriter(dir, iwc);writer.addDocuments(documents);// 释放资源writer.close();

搜索索引

		// 指定索引库路径String indexPath = "D:\\lucene\\";// 指定索引库对象Directory dir = FSDirectory.open(new File(indexPath));// 索引读对象IndexReader reader = DirectoryReader.open(dir);// 索引搜索器IndexSearcher searcher = new IndexSearcher(reader);// Analyzer analyzer = new StandardAnalyzer();Analyzer analyzer = new IKAnalyzer();// 通过QueryParser解析查询语法,获取Query对象QueryParser parser = new QueryParser("name", analyzer);// 参数是查询语法Query query = parser.parse("name:华为手机");TopDocs topDocs = searcher.search(query, 100);ScoreDoc[] scoreDocs = topDocs.scoreDocs;for (ScoreDoc scoreDoc : scoreDocs) {Document document = searcher.doc(scoreDoc.doc);System.out.println("name : " + document.get("name"));}reader.close();

Field域分析

Field是文档中的域,包括Field名和Field值两部分,一个文档可以包括多个Field,Document只是Field的一个承载体,Field值即为要索引的内容,也是要搜索的内容。

Field类型数据类型是否分词是否索引是否存储说明
StringField(FieldName, FieldValue, Store.YES)字符串NYY/N字符串类型Field, 不分词, 作为一个整体进行索引
(如: 身份证号, 订单编号),
是否需要存储由Store.YES或Store.NO决定

LongField(FieldName, FieldValue, Store.YES)

IntField(FieldName, FieldValue, Store.YES)

DoubleField(FieldName, FieldValue, Store.YES)

数值型代表YYY/NLong数值型Field代表, 分词并且索引(如: 价格),
是否需要存储由Store.YES或Store.NO决定
StoredField(FieldName, FieldValue)重载方法, 支持多种类型NNY构建不同类型的Field, 不分词, 不索引, 要存储.
(如: 商品图片路径)
TextField(FieldName, FieldValue, Store.NO)文本类型YYY/N

文本类型Field, 分词并且索引,
是否需要存储由Store.YES或Store.NO决定

 

  • 是否分词(tokenized)

    是:作分词处理,即将Field值进行分词,分词的目的是为了索引。

    否:不作分词处理。

  • 是否索引(indexed)

    是:进行索引。将Field分词后的词或整个Field值进行索引,存储到索引域。

    否:不索引,即不进行搜索。

  • 是否存储(stored)

    是:将Field值存储在文档域中,可以从Document中获取。

    否:不存储Field值。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部