【问题标题】:Search by exact match in all fields in Elasticsearch在 Elasticsearch 中的所有字段中按完全匹配搜索
【发布时间】:2019-07-06 13:00:26
【问题描述】:

假设我有 3 个文档,每个文档只包含一个字段(但假设还有更多,我们需要搜索所有字段)。

  1. 字段值为“第一秒”
  2. 字段值为“第二优先”
  3. 字段值为“first second third”

这是一个可用于创建这 3 个文档的脚本:

# drop the index completely, use with care!
curl -iX DELETE 'http://localhost:9200/test'

curl -H 'content-type: application/json' -iX PUT 'http://localhost:9200/test/_doc/one' -d '{"name":"first second"}'
curl -H 'content-type: application/json' -iX PUT 'http://localhost:9200/test/_doc/two' -d '{"name":"second first"}'
curl -H 'content-type: application/json' -iX PUT 'http://localhost:9200/test/_doc/three' -d '{"name":"first second third"}'

我需要找到唯一一个在其中一个字段中恰好包含“第一秒”文本的文档(文档 1)。

这是我尝试过的。

A.普通搜索:

curl -H 'Content-Type: application/json' -iX POST 'http://localhost:9200/test/_search' -d '{
  "query": {
    "query_string": {
      "query": "first second"
    }
  }
}'

返回所有 3 个文档

B.引用

curl -H 'Content-Type: application/json' -iX POST 'http://localhost:9200/test/_search' -d '{
  "query": {
    "query_string": {
      "query": "\"first second\""
    }
  }
}'

给出 2 个文档:1 和 3,因为两者都包含“第一秒”。

这里https://stackoverflow.com/a/28024714/7637120 他们建议在索引时使用“关键字”分析器来分析字段,但我想避免对映射进行任何自定义。

是否有可能避免它们并且仍然只找到文档 1?

【问题讨论】:

    标签: elasticsearch exact-match


    【解决方案1】:

    在 Elasticsearch 7.1.0 中,即使不创建特殊映射,您似乎也可以使用 keyword 分析器。至少我没有,以下查询可以满足我的需要:

    curl -H 'Content-Type: application/json' -iX POST 'http://localhost:9200/test/_search' -d '{
      "query": {
        "query_string": {
          "query": "first second",
          "analyzer": "keyword"
        }
      }
    }'
    

    【讨论】:

      【解决方案2】:

      是的,您可以通过将 name 映射类型声明为 keyword 来做到这一点。解决问题的关键很简单——声明name 映射type:keyword 然后就可以了

      为了演示,我做了这些

      1) created mapping with `keyword` for `name` field`
      2) indexed the three documents
      3) searched with a `match` query
      

      映射

      PUT so_test16
      {
        "mappings": {
          "_doc":{
            "properties":{
              "name": {
                "type": "keyword"
      
              }
            }
          }
        }
      }
      

      为文档编制索引

      POST /so_test16/_doc
      {
          "id": 1,
          "name": "first second"
      }
      POST /so_test16/_doc
      {
          "id": 2,
          "name": "second first"
      }
      
      POST /so_test16/_doc
      {
          "id": 3,
          "name": "first second third"
      }
      

      查询

      GET /so_test16/_search
      {
        "query": {
          "match": {"name": "first second"}
        }
      }
      

      和结果

      {
        "took" : 1,
        "timed_out" : false,
        "_shards" : {
          "total" : 5,
          "successful" : 5,
          "skipped" : 0,
          "failed" : 0
        },
        "hits" : {
          "total" : 1,
          "max_score" : 0.2876821,
          "hits" : [
            {
              "_index" : "so_test16",
              "_type" : "_doc",
              "_id" : "m1KXx2sB4TH56W1hdTF9",
              "_score" : 0.2876821,
              "_source" : {
                "id" : 1,
                "name" : "first second"
              }
            }
          ]
        }
      }
      

      添加第二个解决方案 (如果name 不是keyword 类型而是text 类型。这里唯一需要为name 字段添加fielddata:true

      映射

      PUT so_test18
      {
      
          "mappings" : {
            "_doc" : {
              "properties" : {
                "id" : {
                  "type" : "long"
                },
                "name" : {
                  "type" : "text",
                  "fielddata": true
                }
              }
            }
      
        }
      }
      

      和搜索查询

      GET /so_test18/_search
      {
        "query": {
          "bool": {
            "must": [
              {"match_phrase": {"name": "first second"}}
            ],
            "filter": {
      
              "script": {
                "script": {
                  "lang": "painless",
                  "source": "doc['name'].values.length == 2"
                }
              }
      
            }
          }
      
        }
      }
      

      和回应

      {
        "took" : 3,
        "timed_out" : false,
        "_shards" : {
          "total" : 5,
          "successful" : 5,
          "skipped" : 0,
          "failed" : 0
        },
        "hits" : {
          "total" : 1,
          "max_score" : 0.3971361,
          "hits" : [
            {
              "_index" : "so_test18",
              "_type" : "_doc",
              "_id" : "o1JryGsB4TH56W1hhzGT",
              "_score" : 0.3971361,
              "_source" : {
                "id" : 1,
                "name" : "first second"
              }
            }
          ]
        }
      }
      

      【讨论】:

      • 谢谢@JBone。但是,如果我不控制映射,并且我不知道文档中将存在的确切字段怎么办?在这种情况下,有没有办法使用“完全匹配”进行搜索?
      • 似乎在不知道mapping的情况下我猜这会很困难,但是如果您想尝试我回答中的第二种解决方案,请尝试。这里的东西是在mappings for name 字段中,我需要添加fielddata=true 才能使脚本工作。谁知道你的mappings 可能已经考虑过fielddata=true,所以你可以尝试运行第二个解决方案。同时向我们展示mappings by your_url/index/_mappings
      • 实际上,我有兴趣找到一种无需任何映射操作即可进行此类搜索的方法。我还没有任何已知的映射,这就像一个研发任务,所以还没有什么可显示的,唉。
      • 太棒了。在新版本的 Elastic(7.2 版)中,他们添加了match_bool_prefix ,这可能会解决您的问题(通过在查询级别而不是映射级别使用分析器)我们可以尝试一下。我的机器上有 6.7 版本,所以我需要获得这个新的 Elasticsearch。但是请仔细阅读此链接以了解我在说什么。 elastic.co/guide/en/elasticsearch/reference/current/…
      • 谢谢,我去看看
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-08-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-10
      • 1970-01-01
      相关资源
      最近更新 更多