Bleve代码阅读(二)——Index Mapping

时间:2021-11-22 22:43:54

引言

Bleve是Golang实现的一个全文检索库,类似Lucene之于Java。在这里通过阅读其代码,来学习如何使用及定制检索功能。也是为了通过阅读代码,学习在具体环境下Golang的一些使用方式。代码的路径在github上https://github.com/blevesearch/bleve。

Index Mapping是bleve的一个功能特性,用来控制每个类型的文档,文档内的每个字段,具体应该如何被分析、索引、存储,这部分与Lucence的设计思路相同。

1 IndexMapping的结构

Bleve通过IndexMapping及各成员结构,来控制如何对每种类型的Document,Document内的NestDocument,Document内的各Field进行分析、索引和存储。它本身是一个递归的结构,在DocumentMapping这一层可以进行任意深度的嵌套。

我们先整体看一下Index Mapping及其成员的从属关系。下图是我画的结构简图,很多成员并未在图上体现出来。本节的内容就是从左到右依次介绍每个结构类型的及其成员对应的物理意义。

Bleve代码阅读(二)——Index Mapping

1.1 IndexMapping

从上一篇文章Bleve代码阅读(一)——新建索引我们知道,新建一个索引首先需要初始化一个IndexMapping结构。在示例代码中,这是通过函数bleve.NewIndexMapping()实现的,然后这个结构的指针被作为参数传递给bleve.New(index_path, index_mapping)。我们还知道,这个结构的内容被序列化后,存储在index文件里了。因此,这个结构与索引是一一对应的。

下面我们先看一下IndexMapping的定义代码,这个结构在上一篇文章也给出了。

type IndexMappingImpl struct {
TypeMapping map[string]*DocumentMapping `json:"types,omitempty"`
DefaultMapping *DocumentMapping `json:"default_mapping"`
TypeField string `json:"type_field"`
DefaultType string `json:"default_type"`
DefaultAnalyzer string `json:"default_analyzer"`
DefaultDateTimeParser string `json:"default_datetime_parser"`
DefaultField string `json:"default_field"`
StoreDynamic bool `json:"store_dynamic"`
IndexDynamic bool `json:"index_dynamic"`
DocValuesDynamic bool `json:"docvalues_dynamic,omitempty"`
CustomAnalysis *customAnalysis `json:"analysis,omitempty"`
cache *registry.Cache
}

上图最左边的矩形,就是代表了IndexMappingImpl这个结构,下面都以IndexMapping这个名称代替。TypeMapping也与图中的同名矩形对应,它是IndexMapping的一个成员。我们这里重点看一下这四个成员:

  1. TypeMapping:它是一个字符串到DocumentMapping指针的map。它的key是字符串,代表Document的类型。它的value是DocumentMapping的指针,用来定制该类型文档的索引方式。
  2. DefaultMapping:它是DocumentMapping的指针。当TypeMapping没有配置某类型文档的DocumentMapping时,则该类文档使用该默认的DocumentMapping。
  3. DefaultAnalyzer:默认的根节点Analyzer。由于每个DocumentMapping、每个DocumentField都可以配置自己的Analyzer,当处理某个Field时,优先使用最个性化的设置。找不到个性化的才使用默认的,一级一级往上找,直到这里。
  4. DefaultType:当一篇文档未提供分类时,就会使用DefaultType设置的类型名称,默认是“_default”,也可以修改。需要注意的是,一个文档时DefaultType不等于它就要使用DefaultMapping,可以在TypeMapping中为DefaultType设置个性化的索引方式。

1.2 DocumentMapping

从本文开头的图中可以看出,DocumentMapping是最多出现的一个结构,在文档、嵌入文档、字段这几个概念层次上都要用到。任何一个文本结构,都应该对应一个DocumentMapping,文本结构包括整个文档、文档内的嵌入文档、文档或嵌入文档内的字段。DocumentMapping的定义如下:

type DocumentMapping struct {
Enabled bool `json:"enabled"`
Dynamic bool `json:"dynamic"`
Properties map[string]*DocumentMapping `json:"properties,omitempty"`
Fields []*FieldMapping `json:"fields,omitempty"`
DefaultAnalyzer string `json:"default_analyzer,omitempty"` // StructTagKey overrides "json" when looking for field names in struct tags
StructTagKey string `json:"struct_tag_key,omitempty"`
}

1.2.1 文档

图中第二列最顶端的矩形,是一个对应原始文档的DocumentMapping。我们在这里重点关注以下几个成员:

  1. Enabled:如果为False,则整个文本结构不被index处理。
  2. Properties:一个map。key为文档的成员名,value是一个DocumentMapping的指针,该DocumentMapping对定义该成员的文本应该被如何处理。无论该成员是一个嵌入文档或是一个字段,都需要一个DocumentMapping与之对应。
  3. Fields:一个FieldMapping指针的切片。作为DocumentMapping的成员,如果当前DocumentMapping对应于一个文档或嵌入文档,则该字段为空。只有当前DocumentMapping对应一个字段,该成员才有可能非空。每个FieldMapping用来控制该字段应该被索引怎样分析、存储,一个字段可以有多个分析、存储方式。
  4. DefaultAnalyzer:当前文本结构的默认Analyzer。

1.2.2 嵌入文档

图中第三列下面的矩形,是一个对应嵌入文档的DocumentMapping。它所有成员的意义与对应文档的DocumentMapping相同。

1.2.3 字段

图中第三列顶端的矩形,是一个对应字段的DocumentMapping。它的成员的意义与对应文档的DocumentMapping略有不同,体现在以下两方面:

  1. Properties:当它是一个对应字段的DocumentMapping的成员时,为空。
  2. Fields:当它是一个对应字段的DocumentMapping的成员时,才可以不为空。它是一个FieldMapping指针的切片,定义了该字段应该被怎样处理,可以有多个处理方式。

1.3 FieldMapping

FieldMapping描述了一个具体的字段如何被放入索引。它一定包含于一个对应字段DocumentMapping,而不能直接被对应文档的DocumentMapping包含。FieldMapping的定义如下:

type FieldMapping struct {
Name string `json:"name,omitempty"`
Type string `json:"type,omitempty"` // Analyzer specifies the name of the analyzer to use for this field. If
// Analyzer is empty, traverse the DocumentMapping tree toward the root and
// pick the first non-empty DefaultAnalyzer found. If there is none, use
// the IndexMapping.DefaultAnalyzer.
Analyzer string `json:"analyzer,omitempty"` // Store indicates whether to store field values in the index. Stored
// values can be retrieved from search results using SearchRequest.Fields.
Store bool `json:"store,omitempty"`
Index bool `json:"index,omitempty"` // IncludeTermVectors, if true, makes terms occurrences to be recorded for
// this field. It includes the term position within the terms sequence and
// the term offsets in the source document field. Term vectors are required
// to perform phrase queries or terms highlighting in source documents.
IncludeTermVectors bool `json:"include_term_vectors,omitempty"`
IncludeInAll bool `json:"include_in_all,omitempty"`
DateFormat string `json:"date_format,omitempty"` // DocValues, if true makes the index uninverting possible for this field
// It is useful for faceting and sorting queries.
DocValues bool `json:"docvalues,omitempty"`
}

FieldMapping不会再包含任何上述结构了,它是整个IndexMapping的叶节点。它的成员大多是bool型,其他string类型的变量也大多是描述作用。

  1. Type:描述该字段是作为那种类型,文本、数字、布尔、日期、地理坐标等。
  2. Store:该字段是否被存入索引。
  3. Index:该字段是否被索引。
  4. IncludeTermVectors:该字段的occ是否被保存,用于标红、短语query。
  5. IncludeInAll:该字段是否包含在all字段里,默认所有字段都在all字段,除非明确指定不包含。
  6. Analyzer:该字段使用哪种Analyzer。

2 实践落地

上一篇文章中建立新索引的代码,只包含了2个函数调用,代码重复如下。

indexMapping := bleve.NewIndexMapping()
index, err := bleve.New("example.bleve", mapping)

为了指定各字段的索引方式,我们需要在初始化indexMapping后对其进行一些编辑修改。

blogMapping := bleve.NewDocumentMapping()
indexMapping.AddDocumentMapping("blog", blogMapping)

上面的代码,在indexMapping新增加了一个文档类型blog,并为其建立了一个DocumentMapping。此时的DocumentMapping是空的,是默认方式。

nameFieldMapping := bleve.NewTextFieldMapping()
nameFieldMapping.Analyzer = "en"
blogMapping.AddFieldMappingsAt("name", nameFieldMapping)

上面的代码为blog类型的文本的name字段,添加了一个FieldMapping,并将其Analyzer指定为“en”。

注意:在1.2.1节中,我们提到了当DocumentMapping对应于文档或嵌入文档时,它不能包含FieldMapping。这里调用方法blogMapping.AddFieldMappingsAt("name", nameFieldMapping),相当于为blogMapping在Properties成员上添加了一个key为"name"的DocumentMapping,再将nameFieldMapping这个FieldMapping添加到这个中间DocumentMapping的Fields。

author := bleve.NewDocumentMapping()
authorNameFieldMapping := bleve.NewTextFieldMapping()
authorNameFieldMapping.Store = false
author.AddFieldMappingsAt("name", authorFieldNameMapping)
authorEmailFieldMapping := bleve.NewTextFieldMapping()
authorEmailFieldMapping.IncludeInAll = false
author.AddFieldMappingsAt("email", authorEmailFieldMapping)
blog.AddSubDocumentMapping("author", author)

上面的代码演示了如何在blog类型的DocumentMapping上添加一个嵌入文档的DocumentMapping。该嵌入文档在文档的key为author,包含name、email两个成员。并且,name只索引不存储,email不包含在_all字段。

Bleve代码阅读(二)——Index Mapping的更多相关文章

  1. Bleve代码阅读(一)——新建索引

    引言 Bleve是Golang实现的一个全文检索库,类似Lucene之于Java.在这里通过阅读其代码,来学习如何使用及定制检索功能.也是为了通过阅读代码,学习在具体环境下Golang的一些使用方式. ...

  2. Linux协议栈代码阅读笔记(二)网络接口的配置

    Linux协议栈代码阅读笔记(二)网络接口的配置 (基于linux-2.6.11) (一)用户态通过C库函数ioctl进行网络接口的配置 例如,知名的ifconfig程序,就是通过C库函数sys_io ...

  3. [置顶] Linux协议栈代码阅读笔记(二)网络接口的配置

    Linux协议栈代码阅读笔记(二)网络接口的配置 (基于linux-2.6.11) (一)用户态通过C库函数ioctl进行网络接口的配置 例如,知名的ifconfig程序,就是通过C库函数sys_io ...

  4. Linux内核启动代码分析二之开发板相关驱动程序加载分析

    Linux内核启动代码分析二之开发板相关驱动程序加载分析 1 从linux开始启动的函数start_kernel开始分析,该函数位于linux-2.6.22/init/main.c  start_ke ...

  5. 代码阅读分析工具Understand 2.0试用

    Understand 2.0是一款源代码阅读分析软件,功能强大.试用过一段时间后,感觉相当不错,确实可以大大提高代码阅读效率.由于Understand功能十分强大,本文不可能详尽地介绍它的所有功能,所 ...

  6. [置顶] Linux协议栈代码阅读笔记(一)

    Linux协议栈代码阅读笔记(一) (基于linux-2.6.21.7) (一)用户态通过诸如下面的C库函数访问协议栈服务 int socket(int domain, int type, int p ...

  7. Android4.0图库Gallery2代码分析(二) 数据管理和数据加载

    Android4.0图库Gallery2代码分析(二) 数据管理和数据加载 2012-09-07 11:19 8152人阅读 评论(12) 收藏 举报 代码分析android相册优化工作 Androi ...

  8. Python - 关于代码阅读的一些建议

    初始能力 让阅读思路保持清晰连贯,主力关注在流程架构和逻辑实现上,不被语法.技巧和业务流程等频繁地阻碍和打断. 建议基本满足以下条件,再开始进行代码阅读: 具备一定的语言基础:熟悉基础语法,常用的函数 ...

  9. Tools - 一些代码阅读的方法

    1 初始能力 让阅读思路清晰连贯,保持在程序的流程架构和逻辑实现上,不被语法.编程技巧和业务流程等频繁地阻碍和打断. 语言基础:熟悉基础语法,常用的函数.库.编程技巧等: 了解设计模式.构建工具.代码 ...

随机推荐

  1. (转)linux grep 正则表达式

    转自:http://www.cnblogs.com/xiaouisme/archive/2012/11/09/2762543.html -------------------------------- ...

  2. c++的类与对象

    对象:此对象,非彼对象,:-D,跟妹子无关(不过貌似也可以,,),闲言少叙,书归正传 我们可以把我们见到的一切事物都称为对象.它可以有形,可以无形,可以简单,可以复杂,但是对某一种具体的对象,比如公司 ...

  3. StopWatch的使用

    //StopWatch在System.Diagnostics命名控件,要使用它就要先引用这个命名空间. //其使用方法如下: //var stopWatch = new StopWatch(); // ...

  4. D3D游戏降帧的动态创建D3D设备以及ShellCode HOOK玩法

    欢迎转载,转载请注明出处:http://blog.csdn.net/gnorth/article/details/9327971 说白了,也就是HOOK掉Present,这种代码,其实百度上某些地方有 ...

  5. Junit 入门使用教程

    1.Junit 是什么? JUnit是一个Java语言的单元测试框架.它由Kent Beck和Erich Gamma建立,逐渐成为源于Kent Beck的sUnit的xUnit家族中最为成功的一个JU ...

  6. 0_Simple__simpleAtomicIntrinsics + 0_Simple__simpleAtomicIntrinsics_nvrtc

    原子操作.并且在静态代码和运行时编译两种条件下使用. ▶ 源代码:静态使用 #ifndef _SIMPLEATOMICS_KERNEL_H_ #define _SIMPLEATOMICS_KERNEL ...

  7. for in 在数组和json中

    数组中也有for--in,相较于对象中的用法是有区别的:数组中 var arr = ['曹操','曹植','曹丕'] for(i in arr){ console.log(i) //0 1 2 con ...

  8. Es6 Symbol.iterator

    Symbol.iterator 为每一个对象定义了默认的迭代器.该迭代器可以被 for...of 循环结构使用. --描述 当需要迭代一个对象的时候(比如在 for...of 循环的开始时),它的 @ ...

  9. 性能之ab简单使用

    ab是apache自带的性能测试工具,他所有关注的请求返回的状态码(2XX),不关心后续处理过程,所以测试时间很小,严重依赖CPU颗粒数 一.进入ab存放的目录执行./ab.其中/ab [option ...

  10. MySQL--自增列学习

    ##=====================================================================================## 在数据库表设计中会纠 ...