# Пересечение по тэгам

**URL:** https://discuss.elastic.co/t/topic/239008
**Category:** Вопросы на русском языке
**Created:** [June 28, 2020, 12:26pm UTC](https://discuss.elastic.co/t/topic/239008 "2020-06-28T12:26:05Z")
**Posts on this page:** 10
**Page:** 1

<div class="post-metadata">

### Author: ![Nasty](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/nasty/32/55343_2.png) [@Nasty](https://discuss.elastic.co/u/Nasty)
#### Post date: [June 28, 2020, 12:26pm UTC](https://discuss.elastic.co/t/topic/239008/1 "2020-06-28T12:26:05Z")

</div>

Здравствуйте!  
Дано: в каждой аккаунт-карточке которую я храню в ЭС, есть поле "тэги", в котором лежит набор ключевых слов для каждого аккаунта. Это string.  
Мне нужно пробежаться по всей бд и найти процент совпадения этого массива с каждым аккаунтом (то есть сколько слов совпало - и процент от общего количества в массиве). Можно неточный поиск (например, до 5 первых символов). (Пишу на java, но видела что-то похожее в php - similar...).  
Есть ли похожий анализатор в Elasticsearch?

---

<div class="post-metadata">

### Author: ![Igor\_Motov](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/igor_motov/32/45193_2.png) [@Igor\_Motov](https://discuss.elastic.co/u/Igor_Motov)
#### Post date: [June 28, 2020, 12:49pm UTC](https://discuss.elastic.co/t/topic/239008/2 "2020-06-28T12:49:34Z")

</div>

А можно пример с данными, запросом и желаемым рузультатом?

---

<div class="post-metadata">

### Author: ![Nasty](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/nasty/32/55343_2.png) [@Nasty](https://discuss.elastic.co/u/Nasty)
#### Post date: [June 29, 2020, 3:51pm UTC](https://discuss.elastic.co/t/topic/239008/3 "2020-06-29T15:51:59Z")

</div>

Вот базовая часть мэппинга, искать пересечения нужно будет по полям features, tags (пока для теста делаю двумя отдельными запросами, так как конечный результат будет еще и по локации фильтровать, работаю впервые с Эластик, боюсь запутаться).  
Суть в том, чтобы поставить фильтр на количество совпадений по тэгам. Напр., получать какой-то score, сколько совпало, а уже в скрипте вычислять процент от длины стринга. В идеале - делать расчет процента совпадения прямо во время фильтра, конечно.

Чтобы выдача была больше (юзеры будут писать что попало в любом случае), думаю указать fuzzyness 1-2. Насколько поняла из туториала, мой stringForSearch будет сплиттован по пробелам - и каждое слово будет проверяться отдельно. Но соблюдается ли порядок слов в этом случае? (т.е. если тэги лежат в базе в другом порядке, найдутся ли совпадения? или ищет согласно порядку в исходном стринге?)  
Извините, если вопросы довольно поверхностные. Это мой первый опыт работы с этой базой.

По поводу процентной планки для фильтра - нашла вариант c параметром ".minimumShouldMatch("...%"), если генерировать Query через java class. Но я пишу вручную для аннотации @Query и репозитория.

```auto
    {
        "posts": {
            "mappings": {
                "properties": {
                    "_class": {
                        "type": "text",
                        "fields": {
                            "keyword": {
                                "type": "keyword",
                                "ignore_above": 256
                            }
                        }
                    },
                    "features": {
                        "type": "text",
                        "fields": {
                            "keyword": {
                                "type": "keyword",
                                "ignore_above": 256
                            }
                        }
                    },
                    "email": {
                        "type": "text",
                        "fields": {
                            "keyword": {
                                "type": "keyword",
                                "ignore_above": 256
                            }
                        }
                    },
                    "flag": {
                        "type": "text",
                        "fields": {
                            "keyword": {
                                "type": "keyword",
                                "ignore_above": 256
                            }
                        }
                    },
                    "id": {
                        "type": "text",
                        "fields": {
                            "keyword": {
                                "type": "keyword",
                                "ignore_above": 256
                            }
                        }
                    },
                    "location": {
                        "type": "geo_point"
                    },
                    "tags": {
                        "type": "text",
                        "fields": {
                            "keyword": {
                                "type": "keyword",
                                "ignore_above": 256
                            }
                        }
                    },
                    "type": {
                        "type": "text",
                        "fields": {
                            "keyword": {
                                "type": "keyword",
                                "ignore_above": 256
                            }
                        }
                    }
                }
            }
        }
    }

```

---

<div class="post-metadata">

### Author: ![Igor\_Motov](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/igor_motov/32/45193_2.png) [@Igor\_Motov](https://discuss.elastic.co/u/Igor_Motov)
#### Post date: [June 29, 2020, 3:56pm UTC](https://discuss.elastic.co/t/topic/239008/4 "2020-06-29T15:56:17Z")

</div>

Что значит "искать пересечения"? Вы не могли бы показать на примере?

---

<div class="post-metadata">

### Author: ![Nasty](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/nasty/32/55343_2.png) [@Nasty](https://discuss.elastic.co/u/Nasty)
#### Post date: [June 30, 2020, 5:49am UTC](https://discuss.elastic.co/t/topic/239008/5 "2020-06-30T05:49:08Z")

</div>

Постараюсь объяснить:

1. Получаю из es пост по id, беру из него строку=массив тэгов (слова разделены пробелами). Судя по документации, можно пользоваться string без трансформации в массив.

String arrayToSearch = customRepo.findById("id").getTags()...;

(Напр., "хороший вкусный свежий необычный дизайн")

1. В запросе (@query) мне нужно передать эту строку для поиска совпадений.

2. Es должен перебирать в индексе каждый пост, брать из него tags (такого же формата) и сравнивать с arrayToSearch (содержится ли каждое слово по отдельности из arrayToSearch в Поле tags у перебираемого поста). Программно я бы сделала, как вариант, через set.retainAll() и дальше бы считала процент. Но тогда теряется вообще смысл в использовании движка Es.

3. Моя цель - получить процент содержания arrayToSearch в tags каждого поста. Например, arrayToSearch.length=10, 3 слова совпали (НЕ по порядку, а по отдельности) с tags. Остальные 7 не совпали. Значит, я должна получить 30%. Под этим я подразумеваю пересечение массивов/строк.

4. В плане фильтрации мне достаточно неточного поиска (например, "добряк" должен показывать совпадение с "добрый", "доброта", "добротный"). Если верно понимаю, для этого мне нужно установить fuzzy.

Что можете сказать? Есть ли похожие анализаторы? В каком направлении стоит копать?

Спасибо!

---

<div class="post-metadata">

### Author: ![Igor\_Motov](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/igor_motov/32/45193_2.png) [@Igor\_Motov](https://discuss.elastic.co/u/Igor_Motov)
#### Post date: [June 30, 2020, 12:31pm UTC](https://discuss.elastic.co/t/topic/239008/6 "2020-06-30T12:31:27Z")

</div>

Вообще, этим занимается запрос [more\_like\_this](https://www.elastic.co/guide/en/elasticsearch/reference/7.8/query-dsl-mlt-query.html). Однако, это запрос рассчитывает не процент совпадений а их релевантность, то есть он предпочитает совпадение по редким тэгам совпадениям по часто встречающимся, что, как-правило, улучшает качество результатов.

Если MLT для вас не работает, и вам действительно надо процент совпадений, то есть много других способов. Можно, например, просто поместить все слова в отдельные запросы [match](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html), завернуть их в [constant\_score](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-constant-score-query.html), и поместить в элементы [should](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-bool-query.html#query-dsl-bool-query), тогда score всего запроса будет равняться количеству совпавших слов. А можно все поместить в один match и контролировать расчет score с помощью [скриптов](https://www.elastic.co/guide/en/elasticsearch/reference/current/index-modules-similarity.html#scripted_similarity). Но если вы только начинаете работать с elasticsearch, я бы порекомендовал туда пока не соваться.

В плане неточного поиска, нужно настроить анализатор. Лучше всего добавить фильтр [hunspell](https://www.elastic.co/guide/en/elasticsearch/reference/7.8/analysis-hunspell-tokenfilter.html), или, если важна скорость больше чем точность, добавить фильтр [stemmer](https://www.elastic.co/guide/en/elasticsearch/reference/7.8/analysis-stemmer-tokenfilter.html).

---

<div class="post-metadata">

### Author: ![Nasty](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/nasty/32/55343_2.png) [@Nasty](https://discuss.elastic.co/u/Nasty)
#### Post date: [June 30, 2020, 7:21pm UTC](https://discuss.elastic.co/t/topic/239008/7 "2020-06-30T19:21:24Z")

</div>

Спасибо! Не сразу увидела Ваш ответ, и пока ждала - немного поменяла тактику: ищу поиск всех постов по проценту совпадения строки. Вот такой простенький вариант сработал (на случай, если кому-то тоже пригодится для подобной задачи):

```auto
  "query": {
        "query_string": {
            "fields": [
                "title"
            ],
            "query": "this that thus",
            "minimum_should_match": 2
        }
    }

```

Ваши идеи тоже изучу и попробую, спасибо большое!  
Еще в процессе возник вопрос о применении нескольких фильтров с тенденцией на **сужение списка** для поиска. То есть можно ли в параметре matchAll, например, задать фильтрацию **по градации:**

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

---

<div class="post-metadata">

### Author: ![Igor\_Motov](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/igor_motov/32/45193_2.png) [@Igor\_Motov](https://discuss.elastic.co/u/Igor_Motov)
#### Post date: [June 30, 2020, 8:35pm UTC](https://discuss.elastic.co/t/topic/239008/8 "2020-06-30T20:35:57Z")

</div>

Лучше заменить query\_string на match, потому что query\_string пытается распарсить вашу строку как запрос и могут возникать всякие странные проблемы. MatchAll ничего не фильтрует и параметров (кроме буста) не имеет, фильтрацию лучше задавать через запрос bool. Туда же можно добавить фильтрацию по дистанции через [geo\_distance](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-geo-distance-query.html).

Язык запросов Elasticsearch и подход к поиску весьма уникален. Поэтому я бы порекомендовал почитать книжку или взять курсы. Можно, конечно, пробиться путем проб и ошибок, но я думаю, с книжкой или курсами будет проще.

---

<div class="post-metadata">

### Author: ![Nasty](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/nasty/32/55343_2.png) [@Nasty](https://discuss.elastic.co/u/Nasty)
#### Post date: [July 1, 2020, 3:13am UTC](https://discuss.elastic.co/t/topic/239008/9 "2020-07-01T03:13:24Z")

</div>

Спасибо вам большое!

---

<div class="post-metadata">

### Author: ![system](https://us1.discourse-cdn.com/elastic/original/3X/1/a/1ac57faf039f6b580b3f104ef42a2a89e41014de.png) [@system](https://discuss.elastic.co/u/system)
#### Post date: [July 29, 2020, 3:13am UTC](https://discuss.elastic.co/t/topic/239008/10 "2020-07-29T03:13:30Z")

</div>

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.
