`

Elasticsearch 倒排索引 + 分词

阅读更多
 
 
es使用称为倒排索引的结构达到快速全文搜索的目的。
 
一个倒排索引包含一系列不同的单词,这些单词出现在任何一个文档,
对于每个单词,对应着所有它出现的文档。
 
比如说,我们有2个文档,每个文档有一个conteng字段。
内容如下:
“ The quick brown fox jumped over the lazy dog”
“ Quick brown foxes leap over lazy dogs in summer”
 
为了创建倒排索引,
我们首先对每个字段进行分词,我们称之为terms或者tokens,创建了一些列有序列表,
然后列举了每个单词所出现的文档,结果如下:
 
Term      Doc_1  Doc_2
-------------------------
Quick   |       |  X
The     |   X   |
brown   |   X   |  X
dog     |   X   |
dogs    |       |  X
fox     |   X   |
foxes   |       |  X
in      |       |  X
jumped  |   X   |
lazy    |   X   |  X
leap    |       |  X
over    |   X   |  X
quick   |   X   |
summer  |       |  X
the     |   X   |
------------------------
现在,如果我们想搜索"quick brown",我们只需要找到每个单词出现的文档。
 
Term      Doc_1  Doc_2
-------------------------
brown   |   X   |  X
quick   |   X   |
------------------------
Total   |   2   |  1
 
两个文档都匹配,但是第一个文档有更高的匹配度,
如果我们采用一个简单的相似算法,我们可以说,第一个文档比第2个文档有更高的匹配度。
也更相关。
 
 
但是,仍然有一些问题。
 
"Quick""quick"看起来是不同的单词,但是用户通常认为是一样的。
 
"fox""foxes"更相似,还有"dog""dogs",具有共同的词根。
 
"jumped""leap",尽管不具备相同的词根,意思上是一样的。它们是同义词。
 
对于以上的索引,一个搜索"+Quick + fox"不可能匹配任何文档。
 
单词"Quick" 和单词 "fox"必须在同一个文档里以满足查询要求,
但是第一个文档包括"quick fox" 而第2个文档包含 "Quick foxes".
 
我们的用户有理由希望两个文档都匹配,我们可以做的更好。
 
如果我们把单词归一化到标准格式,我们就可以达到上面的目标。
这种情况下,虽然单词不是完全一致,但是也足够相似保证相关性。比如:
 
 
"Quick" 可以小写为 "quick".
"foxes" 可以提取词根成为 "fox".
类似的 "dogs" 可以成为 "dog".
"jumped" "leap" 是同义词,可以索引为一个单词 "jump".
 
那么,现在的索引就是:
 
Term      Doc_1  Doc_2
-------------------------
brown   |   X   |  X
dog     |   X   |  X
fox     |   X   |  X
in      |       |  X
jump    |   X   |  X
lazy    |   X   |  X
over    |   X   |  X
quick   |   X   |  X
summer  |       |  X
the     |   X   |  X
------------------------
 
我们的搜索 "+Quick +fox" 仍然失败,因为我们不再有Quick在索引里,
尽管如此,如果我们采用同样的归一化规则,
我们可以用在查询字符串上,它就变为 "+quick +fox",
这样就可以匹配到文档。
 
这一点非常重要,你只能找到那些在你的索引里出现的单词,所以,索引过的文本和查询字符串都需要遵循
同一种归一化规则。
 
分词和归一化叫做分析http://my.oschina.net/qiangzigege/blog/265360
 
 
 
 
 
 
什么叫分析过程?
1)将文本块分词,以倒排索引的方式
2)归一化到标准形式来提高可搜索性。
 
这个工作的执行者叫做分析器,一个分析器包含以下3个功能:
1)字母过滤
首先,字符串依次通过任何一个字符过滤器,
过滤器的工作是清洗字符串,也就是说先清洗再分词。
一个字符过滤器可以去除HTML字符,也可以转换"&"变为"and".
2)分词
下一步,字符串被分词为很多个单词,一个简单的分词器也许依靠空格或者标点符号来分词。
3)单词过滤
最后,每个单词传给单词过滤器,它们可以将单词小写或者删除单词比如a,and,the,etc.
或者增加单词比如jump和leap.
 
es提供了很多字符串过滤器,分词器和单词过滤器。
这些单元可以组合起来使用,后面再说。
 
内置的分析器
尽管如此,es也包含了很多内置的分词器,你可以直接使用。
 
我们列举了最重要的一些,演示区别。
所用的文本基于:
"Set the shape to semi-transparent by calling set_trans(5)"
 
标准分析器
标准分析器是es默认的分析器,如果文本可能是各种语言,总的来说选择标准分析器是没错的。
它把文本分成单词(由Unicode决定),移除大部分标点符号,
最终,将所有单词小写化,这样,结果如下:
 
 
set, the, shape, to, semi, transparent, by, calling, set_trans, 5
PS:先清洗,再分词,再归一化。
 
简单analyzer
这个分析器将文本分词的规则是每个字符是不是一个letter,
然后将所有单词小写化,结果如下:
 
set, the, shape, to, semi, transparent, by, calling, set, trans
 
空格analyzer
依靠空格来分词,不将单词小写化,
结果如下:
Set, the, shape, to, semi-transparent, by, calling, set_trans(5)
 
语言分析器
特定语言分析器是可用的,可以处理特殊字符。
比如,英语分析器附带很多英语过滤单词集合,这些集合包括没啥意义的单词。
那分析器就会去掉这些单词,可以对英语单词进行提取词干,
这是因为英语分析器知道英语的语法规则。
 
结果如下:
set, shape, semi, transpar, call, set_tran, 5
注意:"transparent", "calling", and "set_trans" 已经归一化到词根形式。
 
什么时候用分析器
当我们索引一个文档,它的整个文档被分析成单词,单词用来创建倒排索引。
尽管如下,当我们搜索全文字段,我们也需要将查询字符串进行同样的分析过程。
实际上也就是说,你之前如何对全文做索引的,这个时候也要对查询字符串做同样的归一化处理。这样才可以
保证可以查出来数据。
 
后面说全文搜索,
当你查询一个全文字段,查询将对查询字符串应用同样的分析器来产生一系列分词后的单词。
 
当你查询一个具体的字段,查询就不会对查询字符串进行分析,仅仅搜索具体的值。
 
现在你就理解了之前的:
date字段包含了一个具体的值:一个单词 "2014-09-15".
_all字段是一个全文字段,所以分析器已经把日期分成3个单词: "2014", "09" and "15".
当我们在_all字段里搜索2014,有12个结果,因为都包含2014这个单词。
 
GET /_search?q=2014              # 12 results
 
当我们在_all字段里搜索2014-09-15, 首先会把查询字符串分词为"2014", "09" and "15".
仍然包含了12个tweets,因为都包含2014.
GET /_search?q=2014-09-15        # 12 results !
 
当我们查询date字段,值为2014-09-15, 搜索会寻找具体的日期,结果只有1个:
GET /_search?q=date:2014-09-15   # 1  result
 
当我们查询date字段,值为2014,就找不到文档。
GET /_search?q=date:2014         # 0  results !
 
 
测试分析器
如果你是一个新手,有时会很难理解分词的具体原理和存储索引的过程。
(不看源码你永远不可能知道,just read the fuc*ing source code please!!!)
为了更好的理解怎样运行的,你可以使用分析API来看文本如何分析的。
在查询字符串参数里指定你想用哪个分析器,body里指定分析的文本。
 
GET /_analyze?analyzer=standard
Text to analyze
 
结果如下:
{
   "tokens": [
      {
         "token":        "text",
         "start_offset": 0,
         "end_offset":   4,
         "type":         "<ALPHANUM>",
         "position":     1
      },
      {
         "token":        "to",
         "start_offset": 5,
         "end_offset":   7,
         "type":         "<ALPHANUM>",
         "position":     2
      },
      {
         "token":        "analyze",
         "start_offset": 8,
         "end_offset":   15,
         "type":         "<ALPHANUM>",
         "position":     3
      }
   ]
}
 
这些单词就是真实的被存储在索引里的单词。
position表明单词出现的顺序,
start_offset 和 end_offset 表明 字符在原始文本里的位置。
 
分析API对于理解es的索引很有用。
 
指定分析器
当es发现有需要索引的String字段,自动认为是全文字符串字段,用标准分析器来分析。
 
有的时候你可能不想要这个分析器,纳尼?
也许你想采用一个不同的分析器,因为你觉得它更合适。
也许你还希望一个string字段就是一个字段,不需要认为是全文字段,比如说字符串类型的userid和内部身份。
http://my.oschina.net/qiangzigege/blog/265553
 
 
 
 
 
索引里的每个文档有一个type,
每一个type有它自己的映射模式,一个映射指定了type里的字段,每个字段的数据类型,并且字段如何被es处理。
一个映射也被用来配置元数据。
 
我们会后续详细讨论映射。
 
核心简单字段类型
 
es支持下列简单的字段类型:
 
String: string
 
Whole number: byte, short, integer, long
 
Floating point: float, double
 
Boolean: boolean
 
Date: date
 
当你索引一个文档,而这个文档包含了一个新的字段,es会动态映射此字段,规则如下:
 
JSON type:                Field type:
 
Boolean: true or false    "boolean"
 
Whole number: 123         "long"
 
Floating point: 123.45    "double"
 
String, valid date: "2014-09-15"  "date"
 
String: "foo bar"          "string"
 
这也就意味着,如果你索引 “123”,就映射成字符串,而不是long.
尽管如此,如果这个字段已经映射成long,es将尝试按此转换,失败则抛出异常。
 
查看映射
使用/_mapping来查看已有的映射关系。
比如查看index:gb  type:tweet的映射
GET /gb/_mapping/tweet
结果:
{
   "gb": {
      "mappings": {
         "tweet": {
            "properties": {
               "date": {
                  "type": "date",
                  "format": "dateOptionalTime"
               },
               "name": {
                  "type": "string"
               },
               "tweet": {
                  "type": "string"
               },
               "user_id": {
                  "type": "long"
               }
            }
         }
      }
   }
}
 
定制字段的映射
 
字段最重要的属性就是type,
 
{
    "number_of_clicks":
    {
        "type": "integer"
    }
}
string类型的字段,默认,认为是全文搜索,
也就是说,值会传递给分析器,搜索之前也会将查询字符串进行全文分词再搜索。
 
字符串字段最重要的2个映射关系是index和analyser.
 
index
index属性决定字符串如何被索引,有3个值:
1 analyzed
先分词,再索引,全文搜索
 
2 not_analyzed
索引这个字段,这样可以被搜索,但是直接索引,不分词。
 
3 no
不要索引这个字段,这个字段也不会被搜出来。
 
string字段默认是analyzed.
 
下面阐述了不分词的做法
{
    "tag": {
        "type":     "string",
        "index":    "not_analyzed"
    }
}
其它简单的字段类型-long, double, date etc ,
也可以接收index参数,不过只可以取值no ,not_analyzed.
 
analyzer
对于需要分词的string字段,analyzer属性决定使用哪个分词器,默认,使用standard分词器。
但是你可以设置为一个内置的分词器,比如whitespace,simple,english.
 
{
    "tweet": {
        "type":     "string",
        "analyzer": "english"
    }
}
 
更新映射
你可以在创建索引时指定映射,另外,你可以使用/_mapping来修改映射或者增加映射。
 
如果一个字段已经在映射里存在,这也许意味着这个字段的数据已经被索引了,如果你想这个字段的映射,
已经索引过的数据就会出错。
 
我们可以通过增加一个新的字段来修改映射,但是我们不能修改已经存在的字段从分词到不分词。
 
 
删除gb索引
DELETE /gb
 
然后创建一个索引,指定tweet字段使用english分词器。
 
PUT /gb
{
  "mappings": {
    "tweet" : {
      "properties" : {
        "tweet" : {
          "type" :    "string",
          "analyzer": "english"
        },
        "date" : {
          "type" :   "date"
        },
        "name" : {
          "type" :   "string"
        },
        "user_id" : {
          "type" :   "long"
        }
      }
    }
  }
}
这就创建了一个带映射关系的索引。
 
下面修改映射,增加一个新的不分词的字段tag到tweet映射中。
 
PUT /gb/_mapping/tweet
{
  "properties" : {
    "tag" : {
      "type" :    "string",
      "index":    "not_analyzed"
    }
  }
}
 
测试映射
可以使用分词API来测试映射,
 
GET /gb/_analyze?field=tweet
Black-cats
 
GET /gb/_analyze?field=tag
Black-cats
前者2个分词,后者一个分词。
http://my.oschina.net/qiangzigege/blog/269856
 
 
 
 
 
除了简单的数据类型,JSON还有null值,数组和对象,es都支持。
 
多值字段:
有可能我们想我们的标签字段包含好几个标签,可以用数组:
{ "tag": [ "search", "nosql" ]}
这没有什么特别的,任何字段可以包含0,1或者多个值,这跟一个全文字段产生多个分词道理是一样的。
 
这意味着,一个数组的所有值必须是同样的数据类型,你不能混杂两种数据类型,
如果你通过索引一个数组而创建了一个新的字段,es将使用第一个值的数据类型来决定整个字段的数据类型。
 
数组的元素没有顺序,你不能说第一个元素和最后一个元素,就是一个集合。
 
空字段
数组,可以为空,事实上,Lucene是没有办法存储空值的,所以,一个没有值的字段被认为是一个空的字段。
 
以下四种字段可以认为是空的,不会被索引:
"empty_string":             "",
"null_value":               null,
"empty_array":              [],
"array_with_null_value":    [ null ]
 
多层对象
最后一个JSON数据类型是对象object,比如哈希,字典和数组。
 
数据对象嵌套是常见的,比如:
 
{
    "tweet":            "Elasticsearch is very flexible",
    "user": {
        "id":           "@johnsmith",
        "gender":       "male",
        "age":          26,
        "name": {
            "full":     "John Smith",
            "first":    "John",
            "last":     "Smith"
        }
    }
}
 
内部对象的映射
es会探测到新的对象字段并且映射为object类型,
映射如下:
{
  "gb": {
    "tweet": {
      "properties": {
        "tweet":            { "type": "string" },
        "user": {
          "type":             "object",
          "properties": {
            "id":           { "type": "string" },
            "gender":       { "type": "string" },
            "age":          { "type": "long"   },
            "name":   {
              "type":         "object",
              "properties": {
                "full":     { "type": "string" },
                "first":    { "type": "string" },
                "last":     { "type": "string" }
              }
            }
          }
        }
      }
    }
  }
}
 
内部对象如何被索引?
Lucene不知道内部对象,一个Lucene文档包含一个平级的k/v结构。
为了让es索引内部对象,我们的文档会转化如下:
{
    "tweet":            [elasticsearch, flexible, very],
    "user.id":          [@johnsmith],
    "user.gender":      [male],
    "user.age":         [26],
    "user.name.full":   [john, smith],
    "user.name.first":  [john],
    "user.name.last":   [smith]
}
Lucene仅仅索引简单的值,不是复杂的结构。
 
内部对象数组
 
最后,思考下,一个内部对象数组如何被索引,
比如说如下:
 
{
    "followers": [
        { "age": 35, "name": "Mary White"},
        { "age": 26, "name": "Alex Jones"},
        { "age": 19, "name": "Lisa Smith"}
    ]
}
这个文档将被转化如下的结构
 
{
    "followers.age":    [19, 26, 35],
    "followers.name":   [alex, jones, lisa, smith, mary, white]
}
那么{age: 35} 和 {name: Mary White} 已经丢失了
Is there a follower who is 26 years old?
这个可以回答
 
Is there a follower who is 26 years old and who is called Alex Jones?
这个问题就无法回答。
后续再讨论这个问题。http://my.oschina.net/qiangzigege/blog/270948

 

分享到:
评论

相关推荐

    文本全文搜索引擎 利用倒排索引实现

    倒排索引是实现这种搜索引擎的关键技术,它极大地优化了文本匹配和搜索过程。在这个主题中,我们将深入探讨倒排索引的概念、工作原理以及在Python中的实现。 **倒排索引概念** 倒排索引(Inverted Index)是一种...

    ElasticSearch.md倒排索引

    - 与正向索引比较:正向索引是通过扫描每一条数据(文档)来确定是否符合条件,而倒排索引是通过内容分词后查找符合条件的数据(文档) - MySQL与ElasticSearch的对比 - 表——Index(索引) - 数据(行)——...

    倒排索引1

    倒排索引是一种高效的数据结构,常用于全文搜索引擎如Elasticsearch中,目的是为了快速定位到包含特定关键词的文档。在传统的SQL数据库如MySQL中,进行模糊匹配(如`LIKE '%keyword%'`)时,效率较低,因为需要扫描...

    elasticsearch-6.4.3-全套(ela+源码+ik分词器+kibana)-linux.rar

    源码分析可以帮助我们了解Lucene(Elasticsearch的核心库)如何实现倒排索引,以及Elasticsearch如何进行分布式处理和数据存储。 接下来,`elasticsearch-analysis-ik-6.4.3.zip`是IK分词器的插件。IK分词器是...

    Elasticsearch之倒排索引

    倒排索引是全文搜索引擎如Elasticsearch实现高效搜索的关键数据结构。相较于传统的关系型数据库中的B+树索引,倒排索引在处理大规模、非结构化的文本数据时,展现出更高的性能优势。这是因为倒排索引能够快速定位到...

    elasticsearch-所需资料elasticsearch+elasticsearch-head+kibana+IK+jdk.zip

    【IK】: IK(Inverted Index,中文全称:倒排索引)是Elasticsearch的一个热门中文分词插件,为Elasticsearch提供了强大的中文分词功能。它支持多种分词模式,如精确模式、全模式、模糊模式等,适用于不同的应用场景...

    Elasticsearch 开发手册

    * 倒排索引:Elasticsearch 为什么快,核心设计理念就是采用了倒排索引机制。倒排索引的方式是,根据 content 分词后创建索引,索引后的存储方式为:index→docId→长→1,2→沙→1,2→汽→1,2→车→1,2→研→1→究→...

    十分钟学会使用 Elasticsearch 优雅搭建自己的搜索系统.pdf

    MySQL默认使用innodb引擎,底层采用b+树的方式来实现,而Es底层使用倒排索引的方式实现,使用倒排索引支持各种维度的分词,可以掌控不同粒度的搜索需求。(MYSQL8版本也支持了全文检索,使用倒排索引实现,有兴趣...

    ES分词工具插件

    分词器是这个过程的关键,它将文本分解成单独的词语,然后创建倒排索引。 2. **IK分词器:** IK是为Elasticsearch设计的中文分词插件,具有强大的分词能力,支持自定义词典,以及根据上下文动态调整分词结果的功能...

    Elasticsearch分词.pptx

    Elasticsearch(简称ES)作为一个强大的全文搜索引擎,其核心在于倒排索引,而倒排索引的构建依赖于分词。分词,简单来说,就是将一段完整的文本分割成一个个单独的词汇单元,这个过程对于理解和检索文本信息至关...

    资源前后端分离式分布式微服务架构项目课程发布ElasticSearch讲义+源码+视频

    - **理解倒排索引结构**:掌握ElasticSearch中的数据存储方式及如何通过倒排索引来提高搜索效率。 - **理解ElasticSearch的RESTful应用方法**:学会如何通过HTTP请求来操作ElasticSearch中的数据。 #### 三、...

    Elasticsearch核心知识篇+高手进阶篇

    2. **倒排索引**:Lucene的核心是倒排索引,它允许快速地进行全文搜索。Elasticsearch在后台使用倒排索引来处理搜索请求,极大地提高了查询速度。 3. **RESTful API**:Elasticsearch采用HTTP和JSON,提供了一套...

    ElasticSearch之文本搜索

    本文主要探讨Elasticsearch在文本搜索方面的应用,包括倒排索引的建立过程、文本搜索的执行步骤以及分析器在其中的作用。 1. **文本搜索简介** Elasticsearch的文本搜索能力基于倒排索引机制。倒排索引是一种允许...

    ElasticSearch面试题 30道,面试通关秘籍

    倒排索引是ElasticSearch的核心概念之一。面试官想了解候选人对基础概念的认知。 倒排索引是通过分词策略,形成了词和文章的映射关系表,这种词典+映射表即为倒排索引。 倒排索引可以实现O(1)时间复杂度的效率检索...

    elasticsearch

    **全文检索**:Elasticsearch 的全文检索能力是基于倒排索引技术。倒排索引将每个文档的关键词及其位置信息存储起来,当用户输入查询时,系统通过倒排索引快速找到包含这些关键词的文档,极大地提高了搜索效率。 **...

    ElasticSearch面试题 30道

    倒排索引是ElasticSearch中的一种重要概念,它通过分词策略,形成了词和文章的映射关系表,这种词典+映射表即为倒排索引。有了倒排索引,就能实现O(1)时间复杂度的效率检索文章了,极大的提高了检索效率。 问题3...

    es:7.17.3-ik.tar.gz

    3. 全文搜索:通过倒排索引技术,Elasticsearch可以实现快速的全文检索,同时支持模糊匹配和短语查询。 4. 数据分析:除了搜索,Elasticsearch还具备数据分析能力,如聚合统计,适用于日志分析、监控等场景。 二、...

    elasticsearch-analysis-ik-8.2.0.zip

    1. **数据索引**:对海量中文文本进行分词,建立高效的倒排索引,提高搜索速度。 2. **查询分析**:在用户输入查询语句时,IK 分词器能智能分析并生成相应的分词结果,提高查询精度。 3. **大数据分析**:在大数据...

    elasticsearch索引介绍.pptx

    **倒排索引**是ES实现快速搜索的关键机制。在索引过程中,每个文档的关键词都会被转换为倒排形式,即关键词指向包含它的文档列表。这种结构使得查找包含特定关键词的文档变得高效。 **自定义分析器**允许我们根据...

    Elasticsearch介绍.pdf

    与传统的数据库搜索不同,Elasticsearch 不依赖于 SQL 的 LIKE 操作来查找匹配的数据,而是利用倒排索引机制,即使面对海量数据也能实现高效的搜索。 Elasticsearch 的工作原理主要包括以下几个步骤: 1. **数据...

Global site tag (gtag.js) - Google Analytics