Elasticsearch教程之二:数据存储


Elasticsearch可以说是企业级搜索应用的最佳选择。相较于传统的关系型数据库,Elasticsearch更适合于存储海量数据并对其进行全文检索和数据分析(聚合)。同时ES适合进行结构化/非结构化查询。下面介绍Elasticsearch中的数据存储方式。

Elasticsearch教程
Elasticsearch教程

Elasticsearch系列教程

Elasticsearch中的几个重要概念

ES是面向文档(document oriented)的,整个文档/对象会被存储并可被搜索。正因为如此,ES支持全文检索。

ES中的索引(index)就相当于数据库中的库(database)
ES中的类型(type)就相当于数据库中的表(table)
ES中的文档(document)就相当于数据库中的记录(record)

自从ES6.x之后,一个index中只能有一个type,因此检索方式由早期的:

bash
http://localhost:9200/tutorial/helloworld/1

更改为(无需指定type):

bash
http://localhost:9200/tutorial/_doc/1

Elasticsearch中的索引

正向索引(forward index)

所谓正向索引,就是通过键查找值(key => value)的过程。比如:学生信息系统中,根据学号查找学生具体信息。或者,在一个数组中通过下标访问数组元素。

正向索引得优点就是其简单性。因此只能在一些简单场景下使用。当需要从不同数据源查询不同字段时,尤其是海量数据时,其速度是远不能满足要求的。(比如在数据库查询中的模糊匹配通常无法通过索引(B Tree),而是进行全局扫描来实现,因此其效率较低。)同时,在进行全文检索的时候,较难实现。比如:在数据库中搜索“凤凰传奇”和“传奇凤凰”时,需要通过不同的关键字查询。针对更为复杂的场景,关系型数据库查询效率很低。

倒排索引(inverted index)

倒排索引是通过字/词来查找索引的过程。它记录了出现关键字文档的ID及该关键字在文档中的位置。

倒排索引主要构成:

  • 单词字典(Term Dictionary):通过B+Trees实现,通常存储在内存中。
  • 倒排列表(Posting List)

而倒排列表中包含的信息有:

  • docid
  • TF(Term Frequency): 单词出现频率,用于计算相关性
  • position:分词后单词所在位置
  • offset:单词在文档中起始位置,用于实现标红

倒排索引的优点是其全文检索的效率远高于正向索引。它的缺点则是倒排表的建立和维护都非常复杂

Elasticsearch分词

Elasticsearch分词器介绍

所谓分词,就是将文本转换成一系列单词(terms / tokens)。ES分词主要包括三部分的工作:

  • Character Filter: 对原始文本进行处理,比如:过滤掉特殊字符,html标签等。
  • Tokenizer:对原始文本进行分词。
  • Token Filters:对分此后的关键字进行加工,例如:去除语气词等。

对ES默认分词器的测试

在Kibana中输入:

json
POST _analyze
{
  "analyzer": "standard",
  "text": "elastic search"
}

其返回值为:

json
{
  "tokens" : [
    {
      "token" : "elastic",
      "start_offset" : 0,
      "end_offset" : 7,
      "type" : "<ALPHANUM>",
      "position" : 0
    },
    {
      "token" : "search",
      "start_offset" : 8,
      "end_offset" : 14,
      "type" : "<ALPHANUM>",
      "position" : 1
    }
  ]
}

测试ES索引中某个字段上的分词器:

json
POST testindex/_analyze
{
  "field": "message",
  "text": "elastic search is great!"
}

测试标准分词器和自定义过滤器:

json
POST _analyze
{
  "tokenizer": "standard",
  "filter": ["uppercase"],
  "text": "elastic search"
}

以下会过滤掉html标签:

json
POST _analyze
{
  "tokenizer": "keyword",
  "char_filter": ["html_strip"],
  "text": "<li>Apple</li><li>Pear</li>"
}

ES自带分词器:Standard, Simple, Whitespace, Stop, Pattern, Language

Elasticsearch mapping (映射)

mapping用来定义index中的类型(type),使用的分词器等信息。类似于数据库中的schema。当没有指定时,ES会自动创建mapping。

获取索引中的mapping:

json
GET testindex/_mapping

例如:在创建索引时指定mapping:

json
PUT test_index
{
  "mappings": {
    "properties": {
      "title": {
        "type":"text"
      },
      "name": {
        "type":"keyword"
      }
    }
  }
}

需要注意:如果使用默认的mapping,字段类型是text,可以分词,但不能进行聚合操作,比如:返回facet查询。因此,通常需要自己定义mapping,也就是数据类型:

json
PUT testfacet/_mapping
{
  "properties" : {
    "score" : {
      "type" : "integer"
    },
    "name" : {
      "type" : "keyword"
    },
    "subject" : {
      "type" : "keyword"
    }
  }
}

Dynamic mapping: 如果插入的字段不在mapping中定义,那么ES会自动根据字段值来查找类型。

“dynamic”的可选值包括:

  • true: 允许自动添加新字段
  • false:不允许自动新增字段,但仍可以写入。写入后无法查询
  • strict: 添加新字段会报错

文章作者: 逻思
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 逻思 !