热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

lucene添加扩展词需要重新索引_Lucene的索引详解

IndexWriter之前讲解了Lucene的分词器,这节记录下Lucene的索引。Lucene索引创建API示例Lucene索引创建代码示例创建使用的分词器Anal

IndexWriter

之前讲解了Lucene的分词器,这节记录下Lucene的索引。

Lucene索引创建API示例

8ead9b81c38f37e4d380dea95f8ef0d8.png

Lucene索引创建代码示例

// 创建使用的分词器Analyzer analyzer = new IKAnalyzer4Lucene7(true);// 索引配置对象IndexWriterConfig config = new IndexWriterConfig(analyzer);try (     // 索引存放目录     // 存放到文件系统中     Directory directory = FSDirectory           .open((new File("d:/test/indextest")).toPath());     // 也可以存放到内存中     // Directory directory = new RAMDirectory();     // 创建索引写对象     IndexWriter writer = new IndexWriter(directory, config);) {  // 准备document  Document doc = new Document();  // 商品id:字符串,不索引、但存储  String prodId = "p0001";  doc.add(new StoredField("prodId", prodId));// 往document中添加 商品名称字段 String name = "ThinkPad X1 Carbon 20KH0009CD/25CD 超极本轻薄笔记本电脑联想";doc.add(new TextField("name", name, Store.YES));.......

IndexWriter涉及类

66682f783654c7287f9d45a44d1186d8.png

IndexWriterConfig:写索引配置,装载着分词器,提供着配置信息

  • 使用的分词器,
  • 如何打开索引(是新建,还是追加)。
  • 还可配置缓冲区大小、或缓存多少个文档,再刷新到存储中。
  • 还可配置合并、删除等的策略

Directory: 索引存储的方式,文件系统或者内存或数据库

Document:索引存储的内容

IndexWriter 用来创建、维护一个索引 。它的API使用流程:

 // 创建索引写对象IndexWriter writer = new IndexWriter(directory, config);// 创建documentDocument doc = new Document();// 将文档添加到索引writer.addDocument(doc);// 删除文档//writer.deleteDocuments(terms);//修改文档//writer.updateDocument(term, doc);// 刷新writer.flush();// 提交writer.commit();

那Document是如何存储的呢?

Document

Document

Document即文档,要索引的数据记录、文档在lucene中的表示,是索引、搜索的基本单 元。一个Document由多个字段Field构成。就像数据库的记录-字段。

IndexWriter按加入的顺序为Document指定一个递增的id(从0开始),称为文档id。反向索引中存储的是这个id,文档存储中正向索引也是这个id。 业务数据的主键id只是文档的一个字段

Filed

Filed即字段:由字段名name、字段值value(fieldsData)、字段类型 type 三 部分构成。 字段值可以是文本(String、Reader 或 预分析的 TokenStream)、二进制值(byte[])或数值。

IndexableFieldType

字段类型:描述该如何索引存储该字段

字段可选择性地保存在索引中,这样在搜索结果中,这些保存的字段值就可获得。 一个Document应该包含一个或多个存储字段来唯一标识一个文档。 未存储的字段,从索引中取得的document中是没有这些字段的。

Document 类关系:

96a59b06e1d440a8c617f67b85849dfa.png

IndexableFieldType:提供了是否分词,是否存储,是否标准化,如何索引等方法。

包括:stored,tokenized,indexOptions,storeTermVectors,omitNorms,

docValueType,point

IndexOptions

IndexOptions: 索引选项

NONE: Not indexed 不索引

DOCS:反向索引中只存储了包含该词的 文档id,没有词频、位置

DOCS_AND_FREQS:反向索引中会存储 文档id、词频

DOCS_AND_FREQS_AND_POSITIONS :反向索引中存储文档id、词频、位置

DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS:反向索引中存储 文档id、词频、位置、偏移量

例如在百度搜索Lucene

8a1a9b7e424f5c9766b40ccaf6c56ccc.png

显示的信息:有一个标题,下面是正文的简单的描述,再下面是跳转的链接。搜索词Lucene的红色的高亮的显示。

当我们创建索引的时候,标题需要被索引,分词,存储。文章的链接,不是被索引,分词,只需要将它存储。文章的内容需要分词,建立索引,但不需要将部分描述存储。

有的情况下,如果要实现短语 查询、临近查询(跨度查询),例如:搜索包含“张三” “李四”,且两词之间跨度不超过5个字符 。

这时,需要存储分项的偏移量,位置等信息。

但是有的情况下某个字段不需要进行短语查询、临近查询,那么在反向索引中就 不需要保存位置、偏移数据。可以降低反向索引的数据量,提升效率。

为了提升反向索引的效率,这样的字段的位置、偏移数据是不应该保存到反向 索引中的。这也你前面看到 IndexOptions为什么有那些选项的原因。 在lucene4.0以前,反向索引中总会存储这些数据,4.0后改进为可选择的。

那对于做高亮显示,或者斜体,黑体(或得到搜索结果后需要使用这些信息)的字段怎么办?用IndexOptions可能不能满足需求,或者比较困难。

storeTermVectors

一个字段分词器分词后,每个词项会得到一系列属性信息,如出现频率、位 置、偏移量等,这些信息构成一个词项向量 termVectors。

对于不需要在搜索反向索引时用到,但在搜索结果处理时需要的位置、偏移 量、附加数据(payLoad) 的字段,我们可以单独为该字段存储(文档--->词项向量)的正向索引。

  • boolean storeTermVectors() 是否存储词项向量
  • boolean storeTermVectorPositions() 是否在词项向量中存储位置
  • boolean storeTermVectorOffsets() 是否在词项向量中存储偏移量
  • boolean storeTermVectorPayloads() 是否在词项向量中存储附加信息

FieldType实现类中有对应的set方法

什么是附加信息Payloads

45ae8dbac3febe76d5c17d236e586260.png

粉色:代表一个文档Id,DocId

绿色:词频,出现几次

橙色:词的位置

黑色:附加信息

a这个词,​DocId为2,在文章出现一个,位置索引为2的位置

is的词,在DocId为1的文章中,出现一次,位置为1,附加信息为下划线。

附加信息非常有用,可用它来存储特殊信息,及减少词项数等。

我们往往需要对搜索的结果支持按不同的字段进行排序,如商品搜索 结果按价格排序、按销量排序等。以及对搜索结果进行按某字段分组统计,如 按品牌统计。

假如我们按关键字“娃娃”搜索后得到相关的文档id列表 {10,21,18,48,29,…..} 要对它们进行按价格排序 有的人想看销量排序 有时需要按品牌统计数量…

反向索引对排序有用吗? 需得到每个id对应的价格或销售是多少、品牌是什么,再进行排序、统计。 这个价格、销量、品牌数据在哪里? 如果搜到的文档列表量很大,排序会有什么问题没?

空间换时间

对这种需要排序、分组、聚合的字段,为其建立独立的文档->字段值的正向索引、列式存储。这样我们要加载搜中文档的这个字段的数据就快很多, 耗内存少。

docValuesType

IndexableFieldType 中的 docValuesType方法 就是让你来为需要排序、分组、 聚合的字段指定如何为该字段创建文档->字段值的正向索引的。

DocValuesType 选项说明:

  • NONE 不开启docvalue
  • NUMERIC 单值、数值字段,用这个
  • BINARY 单值、字节数组字段用
  • SORTED 单值、字符字段用, 会预先对值字节进行排序、去重存储
  • SORTED_NUMERIC 单值、数值数组字段用,会预先对数值数组进行排序
  • SORTED_SET 多值字段用,会预先对值字节进行排序、去重存储

DocValuesType是强类型要求的: 字段的值必须保证同类型。需要排序、分组、聚合、分类查询(面查询)的字段才创建docValues。

具体使用选择:

  • 字符串+单值 会选择SORTED作为docvalue存储
  • 字符串+多值 会选择SORTED_SET作为docvalue存储
  • 数值或日期或枚举字段+单值 会选择NUMERIC 作为docvalue存储
  • 数值或日期或枚举字段+多值 会选择SORTED_SET作为docvalue存储

Point

IndexableFieldType中最后定义的的pointDimensionCount(), pointNumBytes() 是做何用的? Lucene6以后引入了点的概念来表示数值字段,废除了原来的IntField等。在Point 字段类中提供了精确、范围查询的便捷方法。 注意:只是引入点的概念,并未改变数值字段的本质。 既然是点,就有空间概念:维度。一维:一个值,二维:两个值的;…… pointDimensionCount() 返回点的维数 pointNumBytes() 返回点中数值类型的字节数。

以下为一个代码示例:

// 创建使用的分词器  Analyzer analyzer = new IKAnalyzer4Lucene7(true);  // 索引配置对象  IndexWriterConfig config = new IndexWriterConfig(analyzer);  try (        // 索引存放目录        // 存放到文件系统中        Directory directory = FSDirectory              .open((new File("d:/test/indextest")).toPath());        // 存放到内存中        // Directory directory = new RAMDirectory();        // 创建索引写对象        IndexWriter writer = new IndexWriter(directory, config);) {     // 准备document     Document doc = new Document();     // 商品id:字符串,不索引、但存储     String prodId = "p0001";     FieldType onlyStoredType = new FieldType();     onlyStoredType.setTokenized(false);     onlyStoredType.setIndexOptions(IndexOptions.NONE);     onlyStoredType.setStored(true);     onlyStoredType.freeze();     doc.add(new Field("prodId", prodId, onlyStoredType));     // 等同下一行     // doc.add(new StoredField("prodId", prodId));     // 商品名称:字符串,分词索引(存储词频、位置、偏移量)、存储     String name = "ThinkPad X1 Carbon 20KH0009CD/25CD 超极本轻薄笔记本电脑联想";     FieldType indexedAllStoredType = new FieldType();     indexedAllStoredType.setStored(true);     indexedAllStoredType.setTokenized(true);     indexedAllStoredType.setIndexOptions(           IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS);     indexedAllStoredType.freeze();     doc.add(new Field("name", name, indexedAllStoredType));     // 图片链接:仅存储     String imgUrl = "http://www.dongnao.com/aaa";     doc.add(new Field("imgUrl", imgUrl, onlyStoredType));     // 商品简介:文本,分词索引(不需要支持短语、临近查询)、存储,结果中支持高亮显示     String simpleIntro = "集成显卡 英特尔 酷睿 i5-8250U 14英寸";     FieldType indexedTermVectorsStoredType = new FieldType();     indexedTermVectorsStoredType.setStored(true);     indexedTermVectorsStoredType.setTokenized(true);     indexedTermVectorsStoredType           .setIndexOptions(IndexOptions.DOCS_AND_FREQS);     indexedTermVectorsStoredType.setStoreTermVectors(true);     indexedTermVectorsStoredType.setStoreTermVectorPositions(true);     indexedTermVectorsStoredType.setStoreTermVectorOffsets(true);     indexedTermVectorsStoredType.freeze();     doc.add(new Field("simpleIntro", simpleIntro,           indexedTermVectorsStoredType));     // 价格,整数,单位分,不索引、存储、要支持排序     int price = 999900;     FieldType numericDocValuesType = new FieldType();     numericDocValuesType.setTokenized(false);     numericDocValuesType.setIndexOptions(IndexOptions.NONE);     numericDocValuesType.setStored(true);     numericDocValuesType.setDocValuesType(DocValuesType.NUMERIC);     numericDocValuesType.setDimensions(1, Integer.BYTES);     numericDocValuesType.freeze();     doc.add(new MyIntField("price", price, numericDocValuesType));     // 与下两行等同     // doc.add(new StoredField("price", price));     // doc.add(new NumericDocValuesField("price", price));     // 类别:字符串,索引不分词,不存储、支持分类统计,多值     FieldType indexedDocValuesType = new FieldType();     indexedDocValuesType.setTokenized(false);     indexedDocValuesType.setIndexOptions(IndexOptions.DOCS);     indexedDocValuesType.setDocValuesType(DocValuesType.SORTED_SET);     indexedDocValuesType.freeze();     doc.add(new Field("type", "电脑", indexedDocValuesType) {        @Override        public BytesRef binaryValue() {           return new BytesRef((String) this.fieldsData);        }     });     doc.add(new Field("type", "笔记本电脑", indexedDocValuesType) {        @Override        public BytesRef binaryValue() {           return new BytesRef((String) this.fieldsData);        }     });     // 等同下四行     // doc.add(new StringField("type", "电脑", Store.NO));     // doc.add(new SortedSetDocValuesField("type", new BytesRef("电脑")));     // doc.add(new StringField("type", "笔记本电脑", Store.NO));     // doc.add(new SortedSetDocValuesField("type", new     // BytesRef("笔记本电脑")));     // 商家 索引(不分词),存储、按面(分类)查询     String fieldName = "shop";     String value = "联想官方旗舰店";     doc.add(new StringField(fieldName, value, Store.YES));     doc.add(new SortedDocValuesField(fieldName, new BytesRef(value)));     // 上架时间:数值,排序需要     long upShelfTime = System.currentTimeMillis();     doc.add(new NumericDocValuesField("upShelfTime", upShelfTime));     writer.addDocument(doc);  } catch (IOException e) {     e.printStackTrace();  }}public static class MyIntField extends Field {  public MyIntField(String fieldName, int value, FieldType type) {     super(fieldName, type);     this.fieldsData = Integer.valueOf(value);  }  @Override  public BytesRef binaryValue() {     byte[] bs = new byte[Integer.BYTES];     NumericUtils.intToSortableBytes((Integer) this.fieldsData, bs, 0);     return new BytesRef(bs);  }}



推荐阅读
  • ***byte(字节)根据长度转成kb(千字节)和mb(兆字节)**parambytes*return*publicstaticStringbytes2kb(longbytes){ ... [详细]
  • 纠正网上的错误:自定义一个类叫java.lang.System/String的方法
    本文纠正了网上关于自定义一个类叫java.lang.System/String的错误答案,并详细解释了为什么这种方法是错误的。作者指出,虽然双亲委托机制确实可以阻止自定义的System类被加载,但通过自定义一个特殊的类加载器,可以绕过双亲委托机制,达到自定义System类的目的。作者呼吁读者对网上的内容持怀疑态度,并带着问题来阅读文章。 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
  • EPICS Archiver Appliance存储waveform记录的尝试及资源需求分析
    本文介绍了EPICS Archiver Appliance存储waveform记录的尝试过程,并分析了其所需的资源容量。通过解决错误提示和调整内存大小,成功存储了波形数据。然后,讨论了储存环逐束团信号的意义,以及通过记录多圈的束团信号进行参数分析的可能性。波形数据的存储需求巨大,每天需要近250G,一年需要90T。然而,储存环逐束团信号具有重要意义,可以揭示出每个束团的纵向振荡频率和模式。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 在Oracle11g以前版本中的的DataGuard物理备用数据库,可以以只读的方式打开数据库,但此时MediaRecovery利用日志进行数据同步的过 ... [详细]
  • 图像因存在错误而无法显示 ... [详细]
  • 本文详细介绍了使用C#实现Word模版打印的方案。包括添加COM引用、新建Word操作类、开启Word进程、加载模版文件等步骤。通过该方案可以实现C#对Word文档的打印功能。 ... [详细]
  • 本文介绍了在C#中SByte类型的GetHashCode方法,该方法用于获取当前SByte实例的HashCode。给出了该方法的语法和返回值,并提供了一个示例程序演示了该方法的使用。 ... [详细]
  • 本文介绍了关于Java异常的八大常见问题,包括异常管理的最佳做法、在try块中定义的变量不能用于catch或finally的原因以及为什么Double.parseDouble(null)和Integer.parseInt(null)会抛出不同的异常。同时指出这些问题是由于不同的开发人员开发所导致的,不值得过多思考。 ... [详细]
author-avatar
手机用户2502937541
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有