Здравствуйте!
Дано: в каждой аккаунт-карточке которую я храню в ЭС, есть поле "тэги", в котором лежит набор ключевых слов для каждого аккаунта. Это string.
Мне нужно пробежаться по всей бд и найти процент совпадения этого массива с каждым аккаунтом (то есть сколько слов совпало - и процент от общего количества в массиве). Можно неточный поиск (например, до 5 первых символов). (Пишу на java, но видела что-то похожее в php - similar...).
Есть ли похожий анализатор в Elasticsearch?
А можно пример с данными, запросом и желаемым рузультатом?
Вот базовая часть мэппинга, искать пересечения нужно будет по полям features, tags (пока для теста делаю двумя отдельными запросами, так как конечный результат будет еще и по локации фильтровать, работаю впервые с Эластик, боюсь запутаться).
Суть в том, чтобы поставить фильтр на количество совпадений по тэгам. Напр., получать какой-то score, сколько совпало, а уже в скрипте вычислять процент от длины стринга. В идеале - делать расчет процента совпадения прямо во время фильтра, конечно.
Чтобы выдача была больше (юзеры будут писать что попало в любом случае), думаю указать fuzzyness 1-2. Насколько поняла из туториала, мой stringForSearch будет сплиттован по пробелам - и каждое слово будет проверяться отдельно. Но соблюдается ли порядок слов в этом случае? (т.е. если тэги лежат в базе в другом порядке, найдутся ли совпадения? или ищет согласно порядку в исходном стринге?)
Извините, если вопросы довольно поверхностные. Это мой первый опыт работы с этой базой.
По поводу процентной планки для фильтра - нашла вариант c параметром ".minimumShouldMatch("...%"), если генерировать Query через java class. Но я пишу вручную для аннотации @Query и репозитория.
{
"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
}
}
}
}
}
}
}
Что значит "искать пересечения"? Вы не могли бы показать на примере?
Постараюсь объяснить:
- Получаю из es пост по id, беру из него строку=массив тэгов (слова разделены пробелами). Судя по документации, можно пользоваться string без трансформации в массив.
String arrayToSearch = customRepo.findById("id").getTags()...;
(Напр., "хороший вкусный свежий необычный дизайн")
-
В запросе (@query) мне нужно передать эту строку для поиска совпадений.
-
Es должен перебирать в индексе каждый пост, брать из него tags (такого же формата) и сравнивать с arrayToSearch (содержится ли каждое слово по отдельности из arrayToSearch в Поле tags у перебираемого поста). Программно я бы сделала, как вариант, через set.retainAll() и дальше бы считала процент. Но тогда теряется вообще смысл в использовании движка Es.
-
Моя цель - получить процент содержания arrayToSearch в tags каждого поста. Например, arrayToSearch.length=10, 3 слова совпали (НЕ по порядку, а по отдельности) с tags. Остальные 7 не совпали. Значит, я должна получить 30%. Под этим я подразумеваю пересечение массивов/строк.
-
В плане фильтрации мне достаточно неточного поиска (например, "добряк" должен показывать совпадение с "добрый", "доброта", "добротный"). Если верно понимаю, для этого мне нужно установить fuzzy.
Что можете сказать? Есть ли похожие анализаторы? В каком направлении стоит копать?
Спасибо!
Вообще, этим занимается запрос more_like_this. Однако, это запрос рассчитывает не процент совпадений а их релевантность, то есть он предпочитает совпадение по редким тэгам совпадениям по часто встречающимся, что, как-правило, улучшает качество результатов.
Если MLT для вас не работает, и вам действительно надо процент совпадений, то есть много других способов. Можно, например, просто поместить все слова в отдельные запросы match, завернуть их в constant_score, и поместить в элементы should, тогда score всего запроса будет равняться количеству совпавших слов. А можно все поместить в один match и контролировать расчет score с помощью скриптов. Но если вы только начинаете работать с elasticsearch, я бы порекомендовал туда пока не соваться.
В плане неточного поиска, нужно настроить анализатор. Лучше всего добавить фильтр hunspell, или, если важна скорость больше чем точность, добавить фильтр stemmer.
Спасибо! Не сразу увидела Ваш ответ, и пока ждала - немного поменяла тактику: ищу поиск всех постов по проценту совпадения строки. Вот такой простенький вариант сработал (на случай, если кому-то тоже пригодится для подобной задачи):
"query": {
"query_string": {
"fields": [
"title"
],
"query": "this that thus",
"minimum_should_match": 2
}
}
Ваши идеи тоже изучу и попробую, спасибо большое!
Еще в процессе возник вопрос о применении нескольких фильтров с тенденцией на сужение списка для поиска. То есть можно ли в параметре matchAll, например, задать фильтрацию по градации:
- сначала выбирает только те, что подходят по гендеру (например),
- из них по локации в заданной дистанции,
- из них по совпадению тегов (то, что выше написала: по количеству совпавших слов из заданной строки).
То есть цель - чтобы каждый фильтр не прогонять, конечно, через весь миллион постов.
Вижу пока в документации только варианты с несколькими фильтрами через запятую - это работает на поэтапное сужение?
Спасибо!
Лучше заменить query_string на match, потому что query_string пытается распарсить вашу строку как запрос и могут возникать всякие странные проблемы. MatchAll ничего не фильтрует и параметров (кроме буста) не имеет, фильтрацию лучше задавать через запрос bool. Туда же можно добавить фильтрацию по дистанции через geo_distance.
Язык запросов Elasticsearch и подход к поиску весьма уникален. Поэтому я бы порекомендовал почитать книжку или взять курсы. Можно, конечно, пробиться путем проб и ошибок, но я думаю, с книжкой или курсами будет проще.
Спасибо вам большое!
This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.