В настоящее время я работаю над внедрением нечеткого поиска терминологического веб-сервиса, и я ищу предложения о том, как улучшить текущую реализацию. Слишком много кода, чтобы поделиться, но я думаю, что объяснения могут быть достаточными для подсказки вдумчивых предложений. Я понимаю, что это много, но я буду благодарен за любую помощь.Советы по улучшению текущей реализации нечеткого поиска
Во-первых, терминология - это в основном просто несколько имен (или терминов). Для каждого слова мы разбиваем его на токены по пробелу, а затем повторяем каждый символ, чтобы добавить его в trie. На терминальном узле (например, когда символ y в клубнике достигнут), мы сохраняем в списке индекс в список основных терминов. Таким образом, терминальный узел может иметь несколько индексов (поскольку терминальный узел для клубники будет соответствовать «клубнике» и «аллергии на клубнику»).
Что касается фактического поиска, поисковый запрос также разбивается на токены по пространству. Алгоритм поиска запускается для каждого токена. Первым символом маркера поиска должно быть совпадение (поэтому травер никогда не будет соответствовать клубнике). После этого мы проходим через детей каждого последующего узла. Если есть дочерний элемент с символом, который соответствует, мы продолжаем поиск со следующим символом маркера поиска. Если дочерний элемент не соответствует указанному символу, мы смотрим на детей, используя текущий символ маркера поиска (чтобы не продвигать его). Это нечеткая часть, поэтому «stwb» будет соответствовать «клубнике».
Когда мы дойдем до конца токена поиска, мы проведем поиск по остальной структуре trie на этом узле, чтобы получить все потенциальные совпадения (так как индексы к списку основных терминов относятся только к терминальным узлам). Мы называем это сверткой. Мы сохраняем индексы, устанавливая их значение на BitSet. Затем мы просто и BitSets получаем результаты каждого результата токена поиска. Затем мы берем, скажем, первые 1000 или 5000 индексов из битовых наборов и находим фактические условия, которым они соответствуют. Мы используем Levenshtein для оценки каждого термина, а затем сортируем по результату, чтобы получить наши окончательные результаты.
Это работает довольно хорошо и довольно быстро. В дереве содержится более 390 тыс. Узлов и более 1,1 млн. Имен реальных терминов. Однако есть проблемы с этим, поскольку он стоит.
Например, поиск «car cat» вернет катетеризацию, когда мы этого не хотим (так как поисковый запрос состоит из двух слов, результат должен быть не менее двух). Это было бы достаточно легко проверить, но он не позаботится о такой ситуации, как процедура катетеризации, поскольку это два слова. В идеальном случае мы хотели бы, чтобы он соответствовал чему-то вроде сердечной катетеризации.
Основываясь на необходимости исправить это, мы придумали некоторые изменения. Во-первых, мы проходим через три в смешанном поиске глубины/ширины. По сути, мы начинаем глубже, пока символ совпадает. Те дочерние узлы, которые не совпадают, добавляются в очередь приоритетов. Очередь приоритетов упорядочивается по расстоянию редактирования, которое можно вычислить при поиске trie (поскольку, если есть совпадение символов, расстояние остается неизменным, а если нет, оно увеличивается на 1). Делая это, мы получаем расстояние редактирования для каждого слова. Мы больше не используем BitSet. Вместо этого это карта индекса для объекта Terminfo. Этот объект хранит индекс фразы запроса, а также термин фраза и оценка. Поэтому, если поиск - «автомобиль-кот», а термин «процедура катетеризации», индексы фразы будут равны 1, как и индексы фразы запроса. Для «сердечной катетеризации» индексы фразы будут равны 1,2, как и индексы фразы запроса. Как вы можете видеть, после этого очень просто посмотреть на подсчет индексов фразы и индексов фразы запросов, и если они не равны, по крайней мере, количеству слов в поисковом слове, они могут быть отброшены.
После этого мы добавляем отредактированные расстояния слов, удаляем слова из термина, соответствующие термину фраза фразы, и подсчитываем оставшиеся буквы, чтобы получить истинное расстояние редактирования.Например, если вы соответствовали термину «аллергия на клубнику», и ваш поисковый запрос был «соломой», у вас было бы 7 баллов из клубники, тогда вы использовали бы термин фраза «индекс», чтобы отбросить клубнику от этого термина и просто посчитать «аллергия на» (минус пробелы), чтобы получить оценку 16.
Это дает нам точные результаты, которые мы ожидаем. Однако это слишком медленно. Где раньше мы могли получить 25-40 мс при поиске по одному слову, теперь это может быть ровно полсекунды. Это в значительной степени от таких вещей, как создание экземпляров объектов TermInfo, использование операций .add(), операций()() и факт, что мы должны вернуть большое количество совпадений. Мы можем ограничить каждый поиск только возвращением 1000 матчей, но нет никакой гарантии, что первые 1000 результатов для «автомобиля» будут соответствовать любому из первых 1000 матчей для «кошки» (помните, что их насчитывается более 1,1 миллиона).
Даже для одного слова запроса, такого как кошка, нам по-прежнему нужно большое количество совпадений. Это связано с тем, что если мы ищем «кот», поиск будет соответствовать автомобилю и сворачивать все терминальные узлы под ним (что будет много). Однако, если мы ограничили количество результатов, это сделало бы слишком тяжелым акцент на словах, которые начинаются с запроса, а не расстояния редактирования. Таким образом, слова, подобные катетеризации, скорее всего будут включены, чем что-то вроде пальто.
Итак, в принципе, есть ли какие-либо мысли о том, как мы могли бы справиться с проблемами, которые исправлялась во второй реализации, но без какой-либо скорости замедления, которую она вводила? Я могу включить некоторый выбранный код, если он может сделать все более ясным, но я не хочу размещать гигантскую стену кода.
Следует отметить, что фактические термины (для которых индексы конечных узлов используются для получения) содержат больше, чем просто имя, но мы хотим только искать по имени. – AHungerArtist
Эта проблема может быть переназначена временным рядом; то ваша проблема будет заключаться в том, как найти соответствующие временные ряды. Я использую FTSE algo для сопоставления временных рядов с http://www.eecs.umich.edu/db/files/sigmod07timeseries.pdf – Adrian