Как реализовать точное совпадение в фильтре сasticsearch? - PullRequest
2 голосов
/ 27 января 2020

Я работаю над запросом, основанным на полях имен в Elasticsearch 2.4. Интересующие меня поля:

  • штат
  • город
  • колония

Если я отправлю этот запрос:

    {"query": 
        {"bool" : 
            {"must" : [
                {"match" : {"state" : {"query" : "michoacán de ocampo", "type" : "boolean"} } }, 
                {"match" : {"colony" : {"query" : "zamora", "type" : "boolean"} } }, 
                {"match" : {"city" : {"query" : "zamora", "type" : "boolean"} } } 
            ], 
            "filter" : {"term" : {"state" : "michoacán"} } 
        } 
    } }

Результаты

{
    "_shards": {
        "failed": 0,
        "successful": 5,
        "total": 5
    },
    "hits": {
        "hits": [
            {
                "_id": "71807",
                "_index": "my_place",
                "_score": 8.708784,
                "_source": {
                    "@timestamp": "2019-11-13T15:34:33.373Z",
                    "@version": "1",
                    "city": "Zamora",
                    "city_id": 828,
                    "colony": "Balcones de Zamora",
                    "id": 71807,
                    "state": "Michoacán de Ocampo",
                    "state_id": 16,
                    "type": "place",
                    "zipcode": "59624",
                    "zone_id": null
                },
                "_type": "place"
            },
            {
                "_id": "71762",
                "_index": "my_place",
                "_score": 8.634264,
                "_source": {
                    "@timestamp": "2019-11-13T15:34:33.112Z",
                    "@version": "1",
                    "city": "Zamora",
                    "city_id": 828,
                    "colony": "Zamora de Hidalgo Centro",
                    "id": 71762,
                    "state": "Michoacán de Ocampo",
                    "state_id": 16,
                    "type": "place",
                    "zipcode": "59600",
                    "zone_id": null
                },
                "_type": "place"
            }
        ],
        "max_score": 8.708784,
        "total": 2
    },
    "timed_out": false,
    "took": 5
}

Которые в порядке

Но если я отправил полное имя состояния в фильтре, вот так ( обратите внимание на полное имя «Michoacán de ocampo» в фильтре )

    {"query": 
        {"bool" : 
            {"must" : [
                {"match" : {"state" : {"query" : "michoacán de ocampo", "type" : "boolean"} } }, 
                {"match" : {"colony" : {"query" : "zamora", "type" : "boolean"} } }, 
                {"match" : {"city" : {"query" : "zamora", "type" : "boolean"} } } 
            ], 
            "filter" : {"term" : {"state" : "Michoacán de Ocampo"} } 
        } 
    } }

Я получил такие результаты:

{
    "_shards": {
        "failed": 0,
        "successful": 5,
        "total": 5
    },
    "hits": {
        "hits": [],
        "max_score": null,
        "total": 0
    },
    "timed_out": false,
    "took": 6
}

Мне нужно отправить полное имя в фильтре, как я могу добиться это или перенастроить мой индекс, чтобы получить те же результаты?

Ответы [ 2 ]

3 голосов
/ 27 января 2020

Обновление : Как упомянул OP в комментарии, что он использует 2.4, я обновляю свое решение, чтобы включить решение, которое работает для него.

ES 2.4 решение

Создание индекса с необходимыми настройками и сопоставлениями

{
    "settings": {
        "analysis": {
            "analyzer": {
                "lckeyword": {
                    "filter": [
                        "lowercase"
                    ],
                    "tokenizer": "keyword"
                }
            }
        }
    },
    "mappings": {
        "so": {
            "properties": {
                "state": {
                    "type": "string"
                },
                "city": {
                    "type": "string"
                },
                "colony": {
                    "type": "string"
                },
                "state_raw": {
                    "type": "string",
                    "analyzer": "lckeyword"
                }
            }
        }
    }
}

Поисковый запрос

{
    "query": {
        "filtered": {
            "query": {
                "bool": {
                    "should": [
                        {
                            "match": {
                                "state": {
                                    "query": "michoacán de ocampo"
                                }
                            }
                        },
                        {
                            "match": {
                                "colony": {
                                    "query": "zamora"
                                }
                            }
                        },
                        {
                            "match": {
                                "city": {
                                    "query": "zamora"
                                }
                            }
                        }
                    ]
                }
            },
            "filter": {
                "term": {
                    "state_raw": "michoacán de ocampo"
                }
            }
        }
    }
}

Здесь важно отметить создание пользовательского анализатора (ключевое слово с фильтром строчных букв) , так что поле, в котором мы создаем фильтр, сохраняется как есть, но с маленькой буквой, так как именно это вы передаете в своем запросе. Теперь вышеприведенный запрос возвращает вам оба документа, this - это коллекция почтальонов, в которой есть создание индекса, создание образца документов и запрос, который возвращает оба возвращенных документа.

ES 7.X решение

Проблема в том, что вы определяете свое поле state как поле text, а затем в своем фильтре используете запрос [term][1], который не является проанализировано, как объяснено в официальном ES do c.

Возвращает документы, содержащие точный термин в указанном поле.

Hence it would try to find token `Michoacán de Ocampo` in inverted index which isn't present as state field is defined as text and generates 3 tokens `michoacán`, `de` and `ocampo` and ES works on token(search term) to token(inverted index) match. You can check these tokens with [analyze API][2] and can use [explain API][3] to see the tokens generated by ES when the query has results

Fix
---
Define `state` field as a [multi-field][4] and store it as it is(kwyword form) so that you can filter on it.

    {
        "mappings": {
            "properties": {
                "state": {
                    "type": "text",
                    "fields": {
                        "raw": {
                            "type": "keyword"
                        }
                    }
                },
                "city": {
                    "type": "text"
                },
                "colony": {
                    "type": "text"
                }
            }
        }
    }

Now below query would give you both results.

    {
        "query": {
            "bool": {
                "must": [
                    {
                        "match": {
                            "state": {
                                "query": "michoacán de ocampo"
                            }
                        }
                    },
                    {
                        "match": {
                            "colony": {
                                "query": "zamora"
                            }
                        }
                    },
                    {
                        "match": {
                            "city": {
                                "query": "zamora"
                            }
                        }
                    }
                ],
                "filter": {
                    "term": {
                        "state.raw": "Michoacán de Ocampo" -->notice .raw to search on keyword field.
                    }
                }
            }
        }
    }

РЕДАКТИРОВАТЬ: - https://www.getpostman.com/collections/f4b9ed00d50e2f4bc7f4 - это ссылка на коллекцию почтальона, если вы хотите быстро ее проверить.

1 голос
/ 27 января 2020

я предполагаю, что сопоставление вашего поля state является значением по умолчанию, т. Е. state является текстовым полем с подполем ключевого слова (см. dynamici c отображение поля ).

Если это так, то фильтр вашего первого запроса «работает», поскольку он соответствует одному из токенов, созданных анализаторами текста по умолчанию. Фактически, «Michoacán de Ocampo» обрабатывается в следующие три строчных токена: [«michoacán», «de», «ocampo»].

По той же причине второй фильтр не может совпадать, потому что вы держите фразу «Michoacán de Ocampo» в регистре. Что должно работать, это следующий запрос:

{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "state": {
              "query": "michoacán de ocampo"
            }
          }
        },
        {
          "match": {
            "colony": {
              "query": "zamora"
            }
          }
        },
        {
          "match": {
            "city": {
              "query": "zamora"
            }
          }
        }
      ],
      "filter": {
        "term": {
          "state.keyword": "Michoacán de Ocampo"
        }
      }
    }
  }
}
...