Как отфильтровать совпадающие слова из предложений и сохранить их уникальность? - PullRequest
0 голосов
/ 22 октября 2019

Я начал работать над старым проектом и обновил ElasticSearch с версии 2.4 до 6.8 и не могу понять, как заставить работать автозаполнение. Я пытаюсь найти отдельные слова в заголовках сообщений, которые соответствуют или начинаются с напечатанной фразы, но все, чего я мог добиться, - это получить полные заголовки, содержащие мои нужные термины.

Я прочитал несколько примерови учебники, которые использовали разные фильтры, такие как edge_ngram и match_phrase_prefix, но безрезультатно. Даже заказал книгу (Advanced Elasticsearch 7.0), но не смог найти там решения. Нужно ли постобработать результаты с помощью чего-то вроде агрегации или чего-то подобного?

Вот как настраиваются мои сообщения:

$mappingProperties = [
    'title' => [
        'type'      => 'text',
        'analyzer'  => 'autocomplete_analyzer',
        'fielddata' => true,
    ],
];

$indexSettings = [
    'analysis' => [
        'filter' => [
            'autocomplete_filter' => [
                'type'     => 'edge_ngram',
                'min_gram' => 3,
                'max_gram' => 20,
            ],
        ],
        'analyzer' => [
            'autocomplete_analyzer' => [
                'tokenizer' => 'autocomplete_tokenizer',
                'filter' => [
                    'lowercase',
                    'autocomplete_filter',
                ],
            ],
        ],
        'tokenizer' => [
            'autocomplete_tokenizer' => [
                'type' => 'edge_ngram',
                'min_gram'    => 2,
                'max_gram'    => 20,
                'token_chars' => ['letter'],
            ],
        ],
    ],
];

И вот так выглядит мой поисковый запрос:

$params = [
    'body' => [
        'query' => [
            'match' => [
                'status' => 1,
            ],
            'match' => [
                'title' => $term,
            ],
        ],
        'aggs' => [
            'autocomplete' => [
                'terms' => [
                    'field'   => 'title',
                    'order'   => ['_count' => 'desc'],
                    'include' => $term . '.*',
                ],
            ],
        ],
    ],
];

Когда я запускаю поиск частичного термина 'Zahnar' (немецкий здесь), я получаю следующие результаты:

[0] => [
    [key] => zahnarz
    [doc_count] => 82
]

[1] => [
    [key] => zahnarzt
    [doc_count] => 82
]

[2] => [
    [key] => zahnarztp
    [doc_count] => 82
]

[3] => [
    [key] => zahnarztpr
    [doc_count] => 29
]

[4] => [
    [key] => zahnarztpra
    [doc_count] => 27
]

[5] => [
    [key] => zahnarztprax
    [doc_count] => 27
]

[6] => [
    [key] => zahnarztpraxi
    [doc_count] => 27
]

[7] => [
    [key] => zahnarztpraxis
    [doc_count] => 15
]

[8] => [
    [key] => zahnarztpraxe
    [doc_count] => 15
]

[9] => [
    [key] => zahnarztpraxen
    [doc_count] => 12
]

Кажется, что это все возможные токенизированныечасти слова были проанализированы во время индексации. Я хотел бы только полные слова, то есть: результаты 1, 7 и 9.

1 Ответ

0 голосов
/ 22 октября 2019

Вам необходимо создать отображение, как показано ниже

PUT index38
{
  "mappings": {
    "properties": {
      "title": {
        "type": "text",     ----> for searching
        "analyzer": "autocomplete_analyzer",
        "fields": {
          "keyword": {
            "type": "keyword"   --- > for terms aggregation
          }
        }
      }
    }
  },
  "settings": {
    "analysis": {
      "filter": {
        "autocomplete_filter": {
          "type": "edge_ngram",
          "min_gram": 3,
          "max_gram": 20
        }
      },
      "analyzer": {
        "autocomplete_analyzer": {
          "tokenizer": "autocomplete_tokenizer",
          "filter": [
            "lowercase",
            "autocomplete_filter"
          ]
        }
      },
      "tokenizer": {
        "autocomplete_tokenizer": {
          "type": "edge_ngram",
          "min_gram": 2,
          "max_gram": 20,
          "token_chars": [
            "letter"
          ]
        }
      }
    }
  }
}

В агрегации приходит несколько токенов из-за установки fielddata как true

Из документов

поле текста анализируется до индексации, так что значение, как Нью-Йорк, можно найти с помощью поиска новых или для йорка. А условия агрегации на этом поле будет возвращать новое ведро и йорк ведро, когда вы, вероятно, хотите, один ковш под названием Нью-Йорк. Вместо этого у вас должно быть текстовое поле для полнотекстового поиска и неанализированное поле ключевого слова с включенными значениями doc_values ​​для агрегатов

Необходимо создать подполе с именем ключевого слова с типом keyword ,который хранит весь текст в виде одного токена.

Edit1: Mapping

GET index38/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": {
              "query": "Zahnar",
              "analyzer": "autocomplete_analyzer"
            }
          }
        }
      ]
    }
  },
  "aggs": {
    "autocomplete": {
      "terms": {
        "field": "title.keyword",
        "include": "zahnar.*", 
        "order": {
          "_count": "desc"
        }
      }
    }
  }
}

Данные:

 [
      {
        "_index" : "index38",
        "_type" : "_doc",
        "_id" : "ilUK_G0Bl2iO22h0waYm",
        "_score" : 1.0,
        "_source" : {
          "title" : "Zahnarzt"
        }
      },
      {
        "_index" : "index38",
        "_type" : "_doc",
        "_id" : "i1UK_G0Bl2iO22h0xabf",
        "_score" : 1.0,
        "_source" : {
          "title" : "zahnarztpraxis"
        }
      },
      {
        "_index" : "index38",
        "_type" : "_doc",
        "_id" : "jFUK_G0Bl2iO22h0zabu",
        "_score" : 1.0,
        "_source" : {
          "title" : "zahnarztpraxen"
        }
      },
      {
        "_index" : "index38",
        "_type" : "_doc",
        "_id" : "jVUK_G0Bl2iO22h00qYI",
        "_score" : 1.0,
        "_source" : {
          "title" : "Zahnarzt abc"
        }
      }
    ]

Запрос:

GET index38/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": {
              "query": "Zahnar",
              "analyzer": "autocomplete_analyzer"
            }
          }
        }
      ]
    }
  },
  "aggs": {
    "autocomplete": {
      "terms": {
        "field": "title.keyword",
        "include": "zahnar.*", 
        "order": {
          "_count": "desc"
        }
      }
    }
  }
}

Ответ:

[
      {
        "_index" : "index38",
        "_type" : "_doc",
        "_id" : "i1UK_G0Bl2iO22h0xabf",
        "_score" : 0.90610546,
        "_source" : {
          "title" : "zahnarztpraxis"
        }
      },
      {
        "_index" : "index38",
        "_type" : "_doc",
        "_id" : "jFUK_G0Bl2iO22h0zabu",
        "_score" : 0.90610546,
        "_source" : {
          "title" : "zahnarztpraxen"
        }
      },
      {
        "_index" : "index38",
        "_type" : "_doc",
        "_id" : "ilUK_G0Bl2iO22h0waYm",
        "_score" : 0.8928052,
        "_source" : {
          "title" : "Zahnarzt"
        }
      },
      {
        "_index" : "index38",
        "_type" : "_doc",
        "_id" : "jVUK_G0Bl2iO22h00qYI",
        "_score" : 0.8913534,
        "_source" : {
          "title" : "Zahnarzt abc"
        }
      }
    ]
  },
  "aggregations" : {
    "autocomplete" : {
      "doc_count_error_upper_bound" : 0,
      "sum_other_doc_count" : 0,
      "buckets" : [
        {
          "key" : "zahnarzt",
          "doc_count" : 2
        },
        {
          "key" : "zahnarztpraxen",
          "doc_count" : 1
        },
        {
          "key" : "zahnarztpraxis",
          "doc_count" : 1
        }
      ]
    }
  }

Есть и другой вариант, если вы не используете autocomplete_analyzer

GET index38/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match_phrase_prefix": {
            "title": {
              "query": "zahn"
            }
          }
        }
      ]
    }
  },
 "highlight" : {
        "fields" : {
            "title" : {}
        }
    }
}

Вам нужно будет получить токены из выделенных полей на стороне клиента. и матч фраза будет соответствовать тексту в любом месте и не подходят для автозаполнения типа функциональных

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...