1

我使用 match_phrase 查询进行搜索全文匹配。

但它并没有像我想象的那样工作。

询问:

POST /_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match_phrase": {
            "browsing_url": "/critical-illness"
          }
        }
      ],
      "minimum_should_match": 1
    }
  }
}

结果:

"hits" : [
      {
        "_source" : {
          "browsing_url" : "https://www.google.com/url?q=https://industrytoday.co.uk/market-research-industry-today/global-critical-illness-commercial-insurance-market-to-witness-a-pronounce-growth-during-2020-2025&usg=afqjcneelu0qvjfusnfjjte1wx0gorqv5q"
        }
      },
      {
        "_source" : {
          "browsing_url" : "https://www.google.com/search?q=critical+illness"
        }
      },
      {
        "_source" : {
          "browsing_url" : "https://www.google.com/search?q=critical+illness&tbm=nws"
        }
      },
      {
        "_source" : {
          "browsing_url" : "https://www.google.com/search?q=do+i+have+a+critical+illness+-insurance%3f"
        }
      },
      {
        "_source" : {
          "browsing_url" : "https://www.google.com/search?q=do+i+have+a+critical+illness%3f"
        }
      }
    ]

期待:

To only get results where the given string is an exact sub-string in the field. For example:

https://www.example.com/critical-illness OR
https://www.example.com/critical-illness-insurance

映射:

"browsing_url": {
  "type": "text",
  "norms": false,
  "fields": {
    "keyword": {
      "type": "keyword",
      "ignore_above": 256
    }
  }
}

结果不是我所期望的。我希望得到的结果与作为存储文本的子字符串的搜索/critical-illness完全一样。

4

1 回答 1

0

您看到意外结果的原因您的搜索查询和字段本身都在通过分析器运行。分析器会将文本分解为可以搜索的单个术语列表。这是使用_analyze端点的示例:

GET _analyze
{
  "analyzer": "standard",
  "text": "example.com/critical-illness"
}

{
  "tokens" : [
    {
      "token" : "example.com",
      "start_offset" : 0,
      "end_offset" : 11,
      "type" : "<ALPHANUM>",
      "position" : 0
    },
    {
      "token" : "critical",
      "start_offset" : 12,
      "end_offset" : 20,
      "type" : "<ALPHANUM>",
      "position" : 1
    },
    {
      "token" : "illness",
      "start_offset" : 21,
      "end_offset" : 28,
      "type" : "<ALPHANUM>",
      "position" : 2
    }
  ]
}

因此,虽然您的文档的真实值为example.com/critical-illness,但在幕后 Elasticsearch 只会使用此标记列表进行匹配。您的搜索查询也是如此,因为您使用的是match_phrase,它对传入的短语进行标记。最终结果是 Elasticsearch 尝试将标记列表["critical", "illness"]与您的文档标记列表进行匹配。

大多数时候,标准分析器在删除不必要的标记方面做得很好,但是在您的情况下,您关心字符,/因为您想匹配它们。解决此问题的一种方法是使用不同的分析器,例如反向路径层次分析器。以下是如何配置此分析器并将其用于您的browsing_url领域的示例:

PUT /browse_history
{
  "settings": {
    "analysis": {
      "analyzer": {
        "url_analyzer": {
          "tokenizer": "url_tokenizer"
        }
      },
      "tokenizer": {
        "url_tokenizer": {
          "type": "path_hierarchy",
          "delimiter": "/",
          "reverse": true
        }
      }
    }
  }, 
  "mappings": {
    "properties": {
      "browsing_url": {
        "type": "text",
        "norms": false,
        "analyzer": "url_analyzer",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 256
          }
        }
      }
    }
  }
}

现在,如果您分析一个 URL,您现在会看到 URL 路径保持完整:

GET browse_history/_analyze
{
  "analyzer": "url_analyzer",
  "text": "example.com/critical-illness?src=blah"
}

{
  "tokens" : [
    {
      "token" : "example.com/critical-illness?src=blah",
      "start_offset" : 0,
      "end_offset" : 37,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "critical-illness?src=blah",
      "start_offset" : 12,
      "end_offset" : 37,
      "type" : "word",
      "position" : 0
    }
  ]
}

这使您match_phrase_prefix可以查找所有具有包含critical-illness路径的 URL 的文档:

POST /browse_history/_search
{
  "query": {
    "match_phrase_prefix": {
      "browsing_url": "critical-illness"
    }
  }
}

{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : 1.7896894,
    "hits" : [
      {
        "_index" : "browse_history",
        "_type" : "_doc",
        "_id" : "3",
        "_score" : 1.7896894,
        "_source" : {
          "browsing_url" : "https://www.example.com/critical-illness"
        }
      }
    ]
  }
}

编辑:

修订之前的先前答案是使用关键字字段和 a regexp,但是这是一个非常昂贵的查询。

POST /browse_history/_search
{
  "query": {
    "regexp": {
      "browsing_url.keyword": ".*/critical-illness"
    }
  }
}
于 2020-06-19T03:58:02.767 回答