elasticsearch match_phrase запрос для точного поиска подстроки - PullRequest
1 голос
/ 19 июня 2020

Я использовал запрос 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
    }
  }
}

Результаты не такие, как я ожидал. Я ожидал получить результаты точно такие же, как при поиске / критическая-болезнь в качестве подстроки сохраненного текста.

1 Ответ

0 голосов
/ 19 июня 2020

Причина, по которой вы видите неожиданные результаты, состоит в том, что и ваш поисковый запрос, и само поле обрабатываются анализатором . Анализаторы разбивают текст на список отдельных терминов, по которым можно выполнять поиск. Вот пример использования конечной точки _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, чтобы найти все документы с URL-адресами, которые содержат critical-illness путь:

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"
        }
      }
    ]
  }
}

EDIT:

Предыдущий ответ перед пересмотром заключался в использовании поля ключевого слова и regexp, однако это довольно дорогостоящий запрос.

POST /browse_history/_search
{
  "query": {
    "regexp": {
      "browsing_url.keyword": ".*/critical-illness"
    }
  }
}
...