美文网首页
Elasticsearch

Elasticsearch

作者: shark没有辣椒 | 来源:发表于2023-08-06 15:26 被阅读0次

Elasticsearch 是一个开源的分布式搜索和分析引擎,它建立在 Apache Lucene 搜索引擎库之上,提供了强大的全文搜索、实时分析、复杂查询和大规模数据存储等功能。

基本概念

Node 与 Cluster

Elastic 本质上是一个分布式数据库,允许多台服务器协同工作,每台服务器可以运行多个 Elastic 实例,单个 Elastic 实例称为一个节点(node)。一组节点构成一个集群(cluster)。

Index

Elastic 会索引所有字段,经过处理后写入一个反向索引(Inverted Index)。查找数据的时候,直接查找该索引。在 Elasticsearch 中每个索引可以看作是一个数据库,用于存储和管理一组相关的文档数据。每个索引都有自己的设置、映射和配置。每个 Index (即数据库)的名字必须是小写。

Document

Index 里面单条的记录称为 Document,类似于数据库中的行,许多条 Document 构成了一个 Index。Document 使用 JSON 格式表示,下面是一个例子

  "name": "王丽",
  "age": 15,
  "grade": "5",
  "description":"三好学生"
}

同一个 Index 里面的 Document,不要求有相同的结构(scheme),但是最好保持相同,这样有利于提高搜索效率。

Type

在 Elasticsearch 6.x 版本之前,每个索引中可以包含多个类型,类似于数据库中的表。但从 Elasticsearch 7.0 开始,不再支持多类型,每个索引只能包含一个类型(_doc)。这一变化是为了更好地支持数据的灵活性和查询性能。
比如刚才学生的这个 Index 里面,可以按年纪分组,也可以按年级分组。但7版本后只能有_doc一种类型了。

数据操作

新建和删除 Index

新建 Index,可以直接向 Elastic 服务器发出 PUT 请求。下面的例子是新建一个名叫student的 Index。然后服务器返回一个 JSON 对象,里面的acknowledged字段表示操作成功。

$ curl -X PUT 'localhost:9200/student'
$ {"acknowledged":true,"shards_acknowledged":true,"index":"student"}

然后,我们发出 DELETE 请求,删除这个 Index。

$ curl -X DELETE 'localhost:9200/student'
$ {"acknowledged":true}

新增记录

向指定的 /Index/Type 发送 PUT 请求,就可以在 Index 里面新增一条记录。

curl -X PUT 'localhost:9200/student/_doc/1'  -H 'Content-Type: application/json' -d '
{
  "name": "王丽",
  "age": 15,
  "grade": "5",
  "description":"三好学生"
}'
$ {"_index":"student","_id":"1","_version":1,"result":"created","_shards":{"total":2,"successful":1,"failed":0},"_seq_no":0,"_primary_term":1}

最后的/1是该条记录的 Id。它不一定是数字,任意字符串(比如abc)都可以。新增记录的时候,也可以不指定 Id,但是要改成 POST 请求。
注意,如果没有先创建 Index(这个例子是student),直接执行上面的命令,Elastic 也不会报错,而是直接生成指定的 Index。

查看记录

向/Index/Type/Id发出 GET 请求,就可以查看这条记录,URL 的参数pretty=true表示以易读的格式返回。

$ curl 'localhost:9200/student/_doc/1?pretty=true'
$ {
  "_index" : "student",
  "_id" : "1",
  "_version" : 1,
  "_seq_no" : 0,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "name" : "王丽",
    "age" : 15,
    "grade" : "5",
    "description":"三好学生"
  }
}

返回的数据中,found字段表示查询成功,_source字段返回原始记录。

删除记录

$ curl -X DELETE 'localhost:9200/student/_doc/1'
$ {"_index":"student","_id":"1","_version":2,"result":"deleted","_shards":{"total":2,"successful":1,"failed":0},"_seq_no":1,"_primary_term":1}

更新记录

更新记录就是重新发送一次数据。

$ curl -X PUT 'localhost:9200/student/_doc/1'  -H 'Content-Type: application/json' -d '
{
  "name": "王丽",
  "age": 16,
  "grade": "6",
  "description":"三好学生"
}'
$ {"_index":"student","_id":"1","_version":2,"result":"updated","_shards":{"total":2,"successful":1,"failed":0},"_seq_no":3,"_primary_term":1}

可以看到,记录的 Id 没变,但是版本(version)从1变成2,操作类型(result)从created变成updated。

中文分词设置

在 Elasticsearch 中设置分词器是为了将中文文本进行适当的分词处理,以便于在全文检索中实现更准确和有意义的搜索结果。
首先,安装中文分词插件。这里使用的是 ik,也可以考虑其他插件,比如 smartc等。

./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v8.8.2/elasticsearch-analysis-ik-8.8.2.zip

在Elastic的bin目录下,执行上诉语句即可安装ik分词插件,然后重启服务即可。
然后,新建一个 Index,指定需要分词的字段。基本上,凡是需要搜索的中文字段,都要单独设置一下。

curl -X PUT 'localhost:9200/student' -H 'Content-Type: application/json' -d '
{
  "mappings": {
    "properties": {
      "name": {
        "type": "text",
        "analyzer": "ik_max_word",
        "search_analyzer": "ik_max_word"
      },
      "description": {
        "type": "text",
        "analyzer": "ik_max_word",
        "search_analyzer": "ik_max_word"
      }
    }
  }
}'

Elastic 的分词器称为 analyze。我们对每个字段指定分词器。比如上诉语句中,analyzer是字段指定的分词器,search_analyzer是搜索词的分词器,ik_max_word分词器是插件ik提供的,可以对文本进行最大数量的分词。
此外,ik还支持同义词过滤器,用于处理同义词。下述命令是先创建一个名为 ik 的自定义分析器,并定义其参数;然后创建一个名为 my_synonym 的同义词过滤器,用于处理同义词。

$ curl -X PUT "http://localhost:9200/_analysis/ik" -H "Content-Type: application/json" -d '{
  "tokenizer": {
    "ik_max_word"
  },
  "filter": [
    "my_synonym"
  ]
}'
$ curl -X PUT "http://localhost:9200/_analysis/filter/my_synonym" -H "Content-Type: application/json" -d '{
  "type": "synonym",
  "synonyms": [
    "搜索=>查找",
    "快乐=>幸福"
  ]
}'

数据查询

返回所有记录

使用 GET 方法,直接请求/Index/_search,就会返回所有记录。

$ curl 'localhost:9200/student/_search'
$ {"took":34,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":2,"relation":"eq"},"max_score":1.0,"hits":[
{"_index":"student","_id":"1","_score":1.0,"_source":{
  "name": "王丽",
  "age": 16,
  "grade": "6",
  "description":"三好学生"
}},
{"_index":"student","_id":"2","_score":1.0,"_source":
{
  "name": "张伟",
  "age": 14,
  "grade": "5",
  "description":"运动健将"
}}]}}

上面代码中,返回结果的 took字段表示该操作的耗时(单位为毫秒),timed_out字段表示是否超时,hits字段表示命中的记录,里面子字段的含义如下:

  • total:返回记录数,本例是2条。
  • max_score:最高的匹配程度,本例是1.0。
  • hits:返回的记录组成的数组。

返回的记录中,每条记录都有一个_score字段,表示匹配的程序,默认是按照这个字段降序排列。

全文搜索

Elastic 的查询非常特别,使用自己的查询语法,要求 GET 请求带有数据体。

$ curl 'localhost:9200/student/_search' -H "Content-Type: application/json"  -d '
{
  "query" : { "match" : { "description" : "健将" }}
}'
$ {"took":2,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":1,"relation":"eq"},"max_score":1.3862942,"hits":[{"_index":"student","_id":"2","_score":1.3862942,"_source":
{
  "name": "张伟",
  "age": 14,
  "grade": "5",
  "description":"运动健将"
}}]}}

上面代码使用Match 查询,指定的匹配条件是description字段里面包含"健将"这个词。

Elastic 默认一次返回10条结果,可以通过size字段改变这个设置,还可以通过from字段,指定位移,通过sort进行排序(默认按照_score进行默认排序)。

curl 'localhost:9200/student/_search' -H "Content-Type: application/json"  -d '
{
  "query" : { "match" : { "description" : "健将 三好" }},
  "from": 0,
  "size": 2,
  "sort": [
      { "age": "asc" }
  ]
}'

逻辑运算

如果有多个搜索关键字, Elastic 认为它们是or关系,比如下面的健将or三好。

$ curl 'localhost:9200/student/_search' -H "Content-Type: application/json"  -d '
{
  "query" : { "match" : { "description" : "健将 三好" }}
}'
$ {"took":3,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":2,"relation":"eq"},"max_score":1.3862942,"hits":[
{"_index":"student","_id":"1","_score":1.3862942,"_source":
{
  "name": "王丽",
  "age": 16,
  "grade": "6",
  "description":"三好学生"
}},{"_index":"student","_id":"2","_score":1.3862942,"_source":
{
  "name": "张伟",
  "age": 14,
  "grade": "5",
  "description":"运动健将"
}}]}}

如果要执行多个关键词的and搜索,必须使用布尔查询。

$ curl 'localhost:9200/student/_search' -H "Content-Type: application/json" -d '
{
  "query": {
    "bool": {
      "must": [
        { "match": { "description": "三好" } },
        { "match": { "description": "学生" } }
      ]
    }
  }
}'
$ {"took":5,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":1,"relation":"eq"},"max_score":2.7725885,"hits":[
{"_index":"student","_id":"1","_score":2.7725885,"_source":
{
  "name": "王丽",
  "age": 16,
  "grade": "6",
  "description":"三好学生"
}}]}}

布尔查询中,经常会使用到should、must、must_not 等关键字组合不同的查询条件,以便实现更精确的搜索。

  • must:用于指定必须匹配的查询条件,这些条件必须全部满足才会返回文档。
$ curl 'localhost:9200/student/_search' -H "Content-Type: application/json" -d '
{
  "query": {
    "bool": {
      "must": [
        { "term": { "description": "三好学生" } },
        { "range": { "age": { "gte": 10 } } }
      ]
    }
  }
}'
$ {"took":274,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":1,"relation":"eq"},"max_score":1.6099696,"hits":[
{"_index":"student","_id":"1","_score":1.6099696,"_source":
{
  "name": "王丽",
  "age": 15,
  "grade": "5",
  "description":"三好学生"
}}]}}
  • should:用于指定应该匹配的查询条件,这些条件会增加文档的匹配分数,但不影响必须匹配条件的结果。
$ curl 'localhost:9200/student/_search' -H "Content-Type: application/json" -d '
{
  "query": {
    "bool": {
      "should": [
        { "term": { "description": "三好学生" } },
        { "term": { "description": "运动健将" } }
      ]
    }
  }
}'
$ {"took":3,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":2,"relation":"eq"},"max_score":0.8025915,"hits":[
{"_index":"student","_id":"2","_score":0.8025915,"_source":
{
  "name": "张伟",
  "age": 14,
  "grade": "5",
  "description":"运动健将"
}},{"_index":"student","_id":"1","_score":0.60996956,"_source":
{
  "name": "王丽",
  "age": 15,
  "grade": "5",
  "description":"三好学生"
}}]}}
  • must_not:用于指定不匹配的查询条件,文档必须不满足这些条件才会返回。
$ curl 'localhost:9200/student/_search' -H "Content-Type: application/json" -d '
{
  "query": {
    "bool": {
      "must_not": [
        { "term": { "name": "张伟" } }
      ]
    }
  }
}'
$ {"took":3,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":1,"relation":"eq"},"max_score":0.0,"hits":[
{"_index":"student","_id":"1","_score":0.0,"_source":
{
  "name": "王丽",
  "age": 15,
  "grade": "5",
  "description":"三好学生"
}}]}}
  • filter:filter 与 must 类似,但是 filter 中的查询条件不会影响搜索的相关性分数,只会用于过滤文档。

其他常见的查询类型

  • Term Query: 用于精确匹配某个字段的值。
{
  "term": {
    "field_name": "value"
  }
}
  • Range Query: 用于范围查询,匹配指定范围内的值。
{
  "range": {
    "age": {
      "gte": 18,
      "lte": 30
    }
  }
}
  • Prefix Query: 用于前缀匹配。
{
  "prefix": {
    "field_name": "prefix_value"
  }
}
  • Wildcard Query: 用于通配符匹配。
{
  "wildcard": {
    "field_name": "wildcard*"
  }
}
  • Fuzzy Query: 用于模糊匹配。
{
  "fuzzy": {
    "field_name": "value"
  }
}
  • Match Phrase Query: 用于短语匹配。
{
  "match_phrase": {
    "field_name": "search phrase"
  }
}
  • Match Phrase Prefix Query: 用于前缀短语匹配。
{
  "match_phrase_prefix": {
    "field_name": "search prefix"
  }
}
  • Multi-Match Query: 用于在多个字段中匹配相同的值。
{
  "multi_match": {
    "query": "search query",
    "fields": ["field1", "field2"]
  }
}
  • Exists Query: 用于检查字段是否存在。
{
  "exists": {
    "field": "field_name"
  }
}

反向索引

反向索引是 Elasticsearch 中用于加速文本搜索的核心数据结构之一。它是一种将文档中的词汇信息与文档的位置信息建立关联的索引方式,可以快速地定位包含特定词汇的文档。具体来说,反向索引的结构如下:

  • 词汇表: Elasticsearch 维护一个词汇表,其中包含了所有文档中出现过的词汇。

  • 倒排列表: 对于每个词汇,Elasticsearch 维护一个倒排列表,其中记录了包含该词汇的文档的信息。倒排列表存储了文档的 ID、位置信息等,以及其他统计信息,如词频、文档频率等。

通过反向索引,Elasticsearch 可以在进行全文搜索时非常快速地定位包含关键词汇的文档,从而加速搜索操作。当用户发起一个查询时,Elasticsearch会在词汇表中查找关键词,然后从对应的倒排列表中获取相关文档信息,最终生成搜索结果。

总结

本文我们简单介绍了Elasticsearch的简单操作及反向索引,但在实际应用中, 面对不同业务场景需要我们灵活设计索引和查询策略。例如刚才的学生索引,在复杂业务情况下可以考虑按照学生的年龄、性别、描述等维度创建不同的索引,每个索引都包含一组与该维度相关的学生文档(或者只包含学生id再另外查询也是常见的方案)。


引用:https://www.ruanyifeng.com/blog/2017/08/elasticsearch.html

相关文章

网友评论

      本文标题:Elasticsearch

      本文链接:https://www.haomeiwen.com/subject/lokppdtx.html