Contents

使用Elasticsearch快速介绍全文搜索

1. 概述

全文搜索查询并对文档执行语言搜索。它包括单个或多个单词或短语,并返回匹配搜索条件的文档。

ElasticSearch 是一个基于Apache Lucene 的搜索引擎,Apache Lucene是一个免费的开源信息检索软件库。它提供了一个分布式全文搜索引擎,带有 HTTP Web 界面和无模式 JSON 文档。

本文检查 ElasticSearch REST API 并演示仅使用 HTTP 请求的基本操作。

2. 设置

要在您的机器上安装 ElasticSearch,请参阅官方设置指南

RESTfull API 在端口 9200 上运行。让我们使用以下 curl 命令测试它是否正常运行:

curl -XGET 'http://localhost:9200/'

如果您观察到以下响应,则说明实例正在正常运行:

{
  "name": "NaIlQWU",
  "cluster_name": "elasticsearch",
  "cluster_uuid": "enkBkWqqQrS0vp_NXmjQMQ",
  "version": {
    "number": "5.1.2",
    "build_hash": "c8c4c16",
    "build_date": "2017-01-11T20:18:39.146Z",
    "build_snapshot": false,
    "lucene_version": "6.3.0"
  },
  "tagline": "You Know, for Search"
}

3. 索引文件

ElasticSearch 是面向文档的。它存储和索引文档。索引创建或更新文档。建立索引后,您可以搜索、排序和过滤完整的文档,而不是行列数据。这是一种完全不同的数据思考方式,也是 ElasticSearch 可以执行复杂全文搜索的原因之一。

文档表示为 JSON 对象。大多数编程语言都支持 JSON 序列化,并已成为 NoSQL 运动使用的标准格式。它简单、简洁、易于阅读。

我们将使用以下随机条目来执行全文搜索:

{
  "title": "He went",
  "random_text": "He went such dare good fact. The small own seven saved man age."
}
{
  "title": "He oppose",
  "random_text": 
    "He oppose at thrown desire of no. \
      Announcing impression unaffected day his are unreserved indulgence."
}
{
  "title": "Repulsive questions",
  "random_text": "Repulsive questions contented him few extensive supported."
}
{
  "title": "Old education",
  "random_text": "Old education him departure any arranging one prevailed."
}

在我们可以索引一个文档之前,我们需要决定在哪里存储它。可以有多个索引,而这些索引又包含多种类型。这些类型包含多个文档,每个文档都有多个字段。

我们将使用以下方案存储我们的文档:

  • text:索引名称。
  • article:类型名称。
  • id:此特定示例文本条目的 ID。

要添加文档,我们将运行以下命令:

curl -XPUT 'localhost:9200/text/article/1?pretty'
  -H 'Content-Type: application/json' -d '
{
  "title": "He went",
  "random_text": 
    "He went such dare good fact. The small own seven saved man age."
}'

这里我们使用id=1,我们可以使用相同的命令和递增的 id 添加其他条目。

4. 检索文件

添加完所有文档后,我们可以使用以下命令检查集群中有多少文档:

curl -XGET 'http://localhost:9200/_count?pretty' -d '
{
  "query": {
    "match_all": {}
  }
}'

此外,我们可以通过以下命令使用其 id 获取文档:

curl -XGET 'localhost:9200/text/article/1?pretty'

我们应该从弹性搜索中得到以下答案:

{
  "_index": "text",
  "_type": "article",
  "_id": "1",
  "_version": 1,
  "found": true,
  "_source": {
    "title": "He went",
    "random_text": 
      "He went such dare good fact. The small own seven saved man age."
  }
}

正如我们所看到的,这个答案与使用 id 1 添加的条目相对应。

5. 查询文件

好的,让我们使用以下命令执行全文搜索:

curl -XGET 'localhost:9200/text/article/_search?pretty' 
  -H 'Content-Type: application/json' -d '
{
  "query": {
    "match": {
      "random_text": "him departure"
    }
  }
}'

我们得到以下结果:

{
  "took": 32,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 2,
    "max_score": 1.4513469,
    "hits": [
      {
        "_index": "text",
        "_type": "article",
        "_id": "4",
        "_score": 1.4513469,
        "_source": {
          "title": "Old education",
          "random_text": "Old education him departure any arranging one prevailed."
        }
      },
      {
        "_index": "text",
        "_type": "article",
        "_id": "3",
        "_score": 0.28582606,
        "_source": {
          "title": "Repulsive questions",
          "random_text": "Repulsive questions contented him few extensive supported."
        }
      }
    ]
  }
}

正如我们所见,我们正在寻找*“他离开”,我们得到两个不同分数的结果。第一个结果很明显,因为文本内部有执行的搜索,我们可以看到我们有1.4513469*的分数。

检索到第二个结果是因为目标文档包含单词“him”。

默认情况下,ElasticSearch 根据相关性分数对匹配结果进行排序,即每个文档与查询的匹配程度。请注意,第二个结果的得分相对于第一个命中较小,表明相关性较低。

6. 模糊搜索

模糊匹配将两个“模糊”相似的词视为同一个词。首先,我们需要定义模糊的含义。

Elasticsearch 支持使用 fuzziness 参数指定的最大编辑距离为 2。fuzziness 参数可以设置为 AUTO,这会导致以下最大编辑距离:

  • 0表示一个或两个字符的字符串
  • 1表示三个、四个或五个字符的字符串
  • 2表示超过五个字符的字符串

您可能会发现编辑距离为2会返回看似不相关的结果。

您可能会获得更好的结果和更好的性能,最大模糊度为 1。距离是指 Levenshtein 距离,它是用于测量两个序列之间差异的字符串度量。非正式地,两个单词之间的Levenshtein 距离 是单字符编辑的最小数量。

好的,让我们进行模糊搜索:

curl -XGET 'localhost:9200/text/article/_search?pretty' -H 'Content-Type: application/json' -d' 
{ 
  "query": 
  { 
    "match": 
    { 
      "random_text": 
      {
        "query": "him departure",
        "fuzziness": "2"
      }
    } 
  } 
}'

结果如下:

{
  "took": 88,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 4,
    "max_score": 1.5834423,
    "hits": [
      {
        "_index": "text",
        "_type": "article",
        "_id": "4",
        "_score": 1.4513469,
        "_source": {
          "title": "Old education",
          "random_text": "Old education him departure any arranging one prevailed."
        }
      },
      {
        "_index": "text",
        "_type": "article",
        "_id": "2",
        "_score": 0.41093433,
        "_source": {
          "title": "He oppose",
          "random_text":
            "He oppose at thrown desire of no. 
              \ Announcing impression unaffected day his are unreserved indulgence."
        }
      },
      {
        "_index": "text",
        "_type": "article",
        "_id": "3",
        "_score": 0.2876821,
        "_source": {
          "title": "Repulsive questions",
          "random_text": "Repulsive questions contented him few extensive supported."
        }
      },
      {
        "_index": "text",
        "_type": "article",
        "_id": "1",
        "_score": 0.0,
        "_source": {
          "title": "He went",
          "random_text": "He went such dare good fact. The small own seven saved man age."
        }
      }
    ]
  }
}'

正如我们所看到的,模糊性给了我们更多的结果。

我们需要谨慎使用模糊性,因为它往往会检索看起来不相关的结果。