Как можно использовать Lucene.NET, чтобы помочь осуществить поиск на сайте, таком как переполнение стека? - PullRequest
62 голосов
/ 19 февраля 2010

Я задал простой вопрос о переполнении стека , но это конкретно касается того, используется ли Lucene.NET при переполнении стека.

Цель вопроса здесь скорее гипотетическая, относительно того, какие подходы можно было бы использовать, если бы они использовали Lucene.NET в качестве основы для поиска по сайту и других факторов на сайте , таких как Переполнение стека [SO].

Согласно записи в блоге Stack Overflow, озаглавленной " Проблемы полнотекстового поиска в SQL 2008 ", было сильное указание на то, что в какой-то момент рассматривался Lucene.NET, но, похоже, это не совсем так, согласно комментарию Джеффа Далгаза от 19 февраля 2010 года:

Lucene.NET не используется для стека Переполнение - мы используем SQL Server Полнотекстовая индексация. Поиск это область где мы продолжаем делать второстепенные щипки.

Итак, мой вопрос: как использовать Lucene.NET на сайте с такой же семантикой переполнения стека?

Вот некоторая предыстория и то, что я уже сделал / подумал (да, я реализовал большую часть этого, и поиск - последний аспект, который я должен завершить):

Технологии:

И, конечно, звезда шоу, Lucene.NET.

Также планируется перейти на .NET / C # 4.0 как можно скорее. Хотя я не думаю, что это изменит правила игры, это следует отметить.

Прежде чем углубляться в аспекты Lucene.NET, важно указать на его аспекты в SQL Server 2008, а также на соответствующие модели.

Модель

Эта система имеет более одного основного типа модели по сравнению с переполнением стека. Некоторые примеры этих моделей:

  • Вопросы: это вопросы, которые люди могут задавать. Люди могут отвечать на вопросы, как и в случае переполнения стека.
  • Примечания. Это односторонние проекции, поэтому в отличие от вопроса вы делаете заявление о содержании. Люди не могут оставлять ответы на это.
  • События: это данные о событии в реальном времени. Имеется информация о местоположении, дате и времени.

Важное замечание по поводу этих моделей:

  • Все они имеют свойство Name / Title (текст) и свойство Body (HTML) (форматы не имеют значения, так как контент будет анализироваться соответствующим образом для анализа).
  • Каждый экземпляр модели имеет уникальный URL на сайте

Тогда есть вещи, которые обеспечивает переполнение стека, которые IMO являются декораторами для моделей. Эти декораторы могут иметь разный кардинал, один к одному или один ко многим:

  • Голосов: набрано у пользователя
  • Ответы: Необязательно, в качестве примера, см. Выше заметки
  • Избранное: модель указана в списке избранных пользователя?
  • Комментарии: (необязательно)
  • Ассоциации тегов: теги находятся в отдельной таблице, чтобы не повторять тег для каждой модели. Существует связь между моделью и таблицей ассоциаций тегов, а затем из таблицы ассоциаций тегов в таблицу тегов.

И есть опорные бюллетени, которые сами по себе являются декораторами «один к одному» для моделей, которые привязаны к ним одинаково (обычно по типу идентификатора модели и идентификатору модели):

  • Подсчет голосов: общее количество положительных, отрицательных голосов, Интервал оценки Уилсона (это важно, он будет определять уровень доверия на основе голосов за запись, по большей части, принять нижнюю границу интервал Вильсона).

Ответы (ответы) - это модели, у которых есть большинство декораторов, которые есть у большинства моделей, у них просто нет заголовка или URL-адреса, и указывать, есть ли у модели ответ, необязательно. Если ответы разрешены, это, конечно, отношение один ко многим.

SQL Server 2008

Таблицы в основном соответствуют макету моделей, приведенных выше, с отдельными таблицами для декораторов, а также с некоторыми вспомогательными таблицами и представлениями, хранимыми процедурами и т. Д.

Следует отметить, что решение не использовать полнотекстовый поиск основано, прежде всего, на том факте, что он не нормализует оценки, такие как Lucene.NET. Я открыт для предложений о том, как использовать текстовый поиск, но мне придется выполнять поиск по нескольким типам моделей, поэтому имейте в виду, что мне нужно нормализовать оценку как-то .

Lucene.NET

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

Индексация

Вопросы / Модели

Я полагаю, что каждая модель должна иметь собственный индекс, содержащий уникальный идентификатор, чтобы быстро искать его на основе экземпляра Term этого идентификатора (проиндексированного, не проанализированного).

В этой области я рассмотрел вопрос о том, чтобы Lucene.NET анализировал каждый вопрос / модель и каждый ответ отдельно. Таким образом, если бы был один вопрос и пять ответов, вопрос и каждый из ответов были бы проиндексированы как одна единица отдельно.

Идея состоит в том, что показатель релевантности, который возвращает Lucene.NET, будет легче сравнивать между моделями, которые проецируют по-разному (скажем, что-то без ответов).

Например, вопрос задает тему, а затем уточняется ответ.

Для заметки, которая не имеет ответов, она решает вопрос представления предмета, а затем развивает его.

Я считаю, что это поможет сделать оценки релевантности более релевантными друг другу.

Метки

Первоначально я думал, что они должны храниться в отдельном индексе с несколькими полями, которые имеют идентификаторы документов в соответствующем модельном индексе. Или, если он слишком большой, существует индекс, содержащий только теги, и другой индекс, который поддерживает связь между индексом тегов и вопросами, к которым они применяются. Таким образом, когда вы нажимаете на тег (или используете структуру URL), легко увидеть прогрессивную манеру, которую вам нужно только «купить» в случае успеха:

  • Если тег существует
  • С какими вопросами связаны теги
  • Сами вопросы

Однако на практике выполнение запроса всех элементов на основе тегов (например, щелчка по тегу в переполнении стека) чрезвычайно просто с SQL Server 2008. На основе приведенной выше модели просто требуется запрос, такой как:

select
     m.Name, m.Body
from
    Models as m
        left outer join TagAssociations as ta on
            ta.ModelTypeId = <fixed model type id> and
            ta.ModelId = m.Id
        left outer join Tags as t on t.Id = ta.TagId
where
    t.Name = <tag>

И поскольку определенные свойства являются общими для всех моделей, достаточно просто сделать UNION между различными типами / таблицами моделей и получить согласованный набор результатов.

Это было бы аналогично TermQuery в Lucene.NET (я ссылаюсь на документацию по Java , поскольку она всеобъемлющая, а Lucene.NET должен быть строкой построчный перевод Lucene , поэтому вся документация одинакова).

Проблема, возникающая при использовании Lucene.NET, связана с порядком сортировки. Оценка релевантности для TermQuery, когда дело доходит до тегов, не имеет значения. Это либо 1, либо 0 (есть или нет).

В этот момент для упорядочения результатов вступает в игру показатель достоверности (интервал оценок Уилсона).

Эта оценка может храниться в Lucene.NET, но для сортировки результатов в этом поле она будет полагаться на значения, хранящиеся в кэше полей, чего я действительно очень хочу избежать. Для большого количества документов кэш поля может стать очень большим (счет Уилсона удваивается, и вам потребуется один дубль для каждого документа, который может быть одним большим массивом).

Учитывая, что я могу изменить инструкцию SQL на порядок, основанный на интервале оценок Уилсона, как это:

select
     m.Name, m.Body
from
    Models as m
        left outer join TagAssociations as ta on
            ta.ModelTypeId = <fixed model type id> and
            ta.ModelId = m.Id
        left outer join Tags as t on t.Id = ta.TagId
        left outer join VoteTallyStatistics as s on
            s.ModelTypeId = ta.ModelTypeId and
            s.ModelId = ta.ModelId
where
    t.Name = <tag>
order by
    --- Use Id to break ties.
    s.WilsonIntervalLowerBound desc, m.Id

Кажется, что это простой выбор для обработки части функции переполнения стека "пометить все элементы с помощью ".

Ответов

Первоначально я думал, что это отдельный отдельный индекс с ключом обратно в индекс вопросов.

Я думаю, что должна быть комбинация каждой модели и каждого ответа (если таковая имеется), чтобы оценки релевантности для разных моделей были более "равными" по сравнению друг с другом.

Это, конечно, увеличило бы индекс. Сейчас мне это немного комфортно.

Или есть способ сохранить, скажем, модели и ответы как отдельные документы в Lucene.NET, а затем взять оба и получить оценку релевантности для запроса, рассматривая оба документа как один ? Если это так, то это будет идеально .

Конечно, возникает вопрос о том, какие поля будут храниться, индексироваться, анализироваться (все операции могут быть отдельными операциями или смешивать и сопоставлять)? Сколько будет один индекс?

А как насчет использования специальных расширителей / носильщиков для орфографических ошибок (с помощью Метафона), а также синонимов (в сообществе, которое я буду обслуживать, есть своя терминология, которая имеет свой собственный сленг / терминологию для определенных вещей, которая имеет несколько представлений)

Повышение

Конечно, это связано с индексацией, но я думаю, что она заслуживает отдельного раздела.

Вы повышаете поля и / или документы? Если так, как вы их повысите? Является ли усиление постоянным для определенных полей? Или же он пересчитывается для полей, где применимо голосование / просмотр / избранные / внешние данные.

Например, в документе заголовок получает усиление над телом? Если да, то какие факторы повышения, по вашему мнению, работают хорошо? А как насчет тегов?

Мышление здесь такое же, как и в случае переполнения стека. Термины в документе имеют значение, но если документ помечен термином или в заголовке, его следует увеличить.

Shashikant Kore предлагает такую ​​структуру документа:

  • Название
  • Вопрос
  • Принятый ответ (или ответ с высоким рейтингом, если нет принятого ответа)
  • Все ответы объединены

И затем с использованием повышения, но не на основе необработанного значения голоса. Я полагаю, что это покрыто интервалом счета Уилсона.

Вопрос в том, должно ли повышение применяться ко всему документу? Я склоняюсь к «нет», потому что это будет означать, что мне придется переиндексировать документ каждый раз, когда пользователь голосует по модели.

Поиск предметов с меткой

Первоначально я думал, что при запросе тега (путем особого нажатия на него или использования структуры URL для поиска содержимого с тегами) это простой TermQuery для индекса тега для тега, а затем в индексе ассоциаций (если необходимо ) затем вернемся к вопросам, Lucene.NET справится с этим очень быстро.

Однако, учитывая приведенные выше замечания относительно того, как легко это сделать в SQL Server, я выбрал этот маршрут, когда речь идет о поиске теговых элементов.

Общий поиск

Итак, сейчас самый важный вопрос заключается в том, что при выполнении общего поиска по фразе или термину по контенту, что и как вы интегрируете в другую информацию (например, голоса), чтобы определить результаты в правильном порядке? Например, при выполнении этого поиска в ASP.NET MVC в переполнении стека эти цифры подсчитываются для пяти лучших результатов (при использовании вкладки релевантности):

    q votes answers accepted answer votes asp.net highlights mvc highlights
    ------- ------- --------------------- ------------------ --------------
         21      26                    51                  2              2
         58      23                    70                  2              5
         29      24                    40                  3              4
         37      15                    25                  1              2
         59      23                    47                  2              2

Обратите внимание, что основные моменты присутствуют только в заголовке и аннотации на странице результатов и являются лишь второстепенными показателями относительно того, какова истинная частота термина в документе, заголовке, теге, ответе (однако они применяются, что является еще одним хорошим вопрос).

Как все это объединено?

На данный момент я знаю, что Lucene.NET вернет нормализованную оценку релевантности, а данные голосования дадут мне интервал оценки Уилсона, который я могу использовать для определения степени достоверности.

Как мне смотреть на объединение этих двух баллов, чтобы указать порядок сортировки набора результатов, основанный на релевантности и достоверности?

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

Мои первоначальные мысли таковы: если показатель релевантности составляет от 0 до 1, а показатель достоверности - от 0 до 1, тогда я мог бы сделать что-то вроде этого:

1 / ((e ^ cs) * (e ^ rs))

Таким образом, можно получить нормализованное значение, которое приближается к 0, тем более релевантным и уверенным является результат, и его можно отсортировать по этому.

Основная проблема заключается в том, что если повышение выполняется для тега и / или поля заголовка, тогда показатель релевантности выходит за границы от 0 до 1 (верхний предел становится неограниченным, и я не знаю, как справиться с этим).

Кроме того, я считаю, что мне придется скорректировать показатель доверия, чтобы учесть подсчеты голосов, которые являются абсолютно отрицательными. Поскольку результаты голосования, которые являются абсолютно отрицательными, дают результат в интервале оценок Уилсона с нижней границей 0, то показатель с -500 голосами имеет тот же показатель достоверности, что и показатель с -1 голосом или 0 голосами.

К счастью, верхняя граница уменьшается с 1 до 0 по мере увеличения числа отрицательных голосов. Я могу изменить показатель достоверности на диапазон от -1 до 1, например:

confidence score = votetally < 0 ? 
    -(1 - wilson score interval upper bound) :
    wilson score interval lower bound

Проблема с этим заключается в том, что включение 0 в уравнение оценивает все элементы с нулевым голосом ниже, чем с отрицательным подсчетом голосов.

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

confidence score = 0.5 + 
    (votetally < 0 ? 
        -(1 - wilson score interval upper bound) :
        wilson score interval lower bound) / 2

Другие мои вопросы касаются того, как на самом деле выполнять вычисления с учетом Lucene.NET и SQL Server. Я не решаюсь поставить показатель достоверности в индекс Lucene, потому что он требует использования кеша полей, что может оказать огромное влияние на потребление памяти (как упоминалось ранее).

У меня была идея получить оценку релевантности из Lucene.NET, а затем использовать табличный параметр для потоковой передачи оценки в SQL Server (вместе с идентификаторами элементов для выбора), в этот момент я выполнил бы расчет с доверительной оценкой, а затем вернул данные правильно.

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

Ответы [ 5 ]

10 голосов
/ 25 февраля 2010

Ответы, которые вы ищете, на самом деле не могут быть найдены, используя только lucene.Вам нужны алгоритмы ранжирования и группировки, чтобы фильтровать и понимать данные и их связь.Lucene может помочь вам получить нормализованные данные, но после этого вам нужен правильный алгоритм.

Я бы порекомендовал вам проверить одну или все из следующих книг, они помогут вам с математикой и укажут вам правильное направление:

Алгоритмы интеллектуальной сети

Коллективный разум в действии

Программирование Коллективного разума

4 голосов
/ 02 марта 2010

У индекса lucene будут следующие поля:

  • Заголовок
  • Вопрос
  • Принятый ответ (или высоко оцененный ответ, если нет принятого ответа)
  • Все ответы объединены

Все эти поля анализируются.Нормализация длины отключена, чтобы получить лучший контроль над оценкой.

Вышеупомянутый порядок полей также отражает их важность в порядке убывания.Это означает, что совпадение запроса в заголовке важнее, чем в принятом ответе, а все остальное остается тем же.

Число голосов для ответа на вопрос, и верхний ответ может быть получен путем усиления этих полей.Но необработанный подсчет голосов не может быть использован в качестве повышающих значений, поскольку он может значительно искажать результаты.(Вопрос с 4-мя ответами получит вдвое больше, чем вопрос с 2-мя ответами.) Эти значения должны быть резко ослаблены, прежде чем их можно будет использовать в качестве коэффициента повышения.Использование чего-то естественного логарифм (для upvotes> 3) выглядит хорошо.

Заголовок может быть увеличен на значение немного выше, чем у вопроса.

Хотя взаимосвязь вопросов не очень распространена, имея базовый вес вопроса, подобный PageRank, можно получить интересные результаты.

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

Типичный поисковый запрос будет выполняться по всем четырем полям.

+(title:query question:query accepted_answer:query all_combined:query)

Это широкий набросок, требующий значительной настройки, чтобы прийти к правильномуповысить значения и правильные веса для запросов, если требуется.Экспериментирование покажет правильные веса для двух измерений качества - актуальности и важности.Вы можете усложнить ситуацию, введя параметр недавности в качестве параметра aranking.Идея заключается в том, что если проблема возникает в конкретной версии продукта и исправляется в более поздних версиях, новые вопросы могут быть более полезными для пользователя.

Можно добавить некоторые интересные поводы для поиска.Некоторая форма базового поиска по синонимам может быть полезна, если найдено только «несколько» подходящих результатов.Например, «уменьшение размера кучи Java» аналогично «уменьшению размера кучи Java».Но тогда это также будет означать, что «уменьшение карты» начнет совпадать с «уменьшением карты».(Проверка правописания очевидна, но, полагаю, программисты правильно написали бы свои запросы.)

2 голосов
/ 02 марта 2010

Я думаю, что Lucene не подходит для этой работы.Вам нужно что-то действительно быстрое с высокой доступностью ... как SQL Но вы хотите открытый код?

Я бы посоветовал вам использовать Sphinx - http://www.sphinxsearch.com/ Это намного лучше, и я говорю с опытом, яиспользовал их обоих.

Сфинкс потрясающий.Действительно есть.

2 голосов
/ 23 февраля 2010

Определение релевантности всегда сложно. Вы должны выяснить, чего вы пытаетесь достичь. Ваш поиск пытается найти точное совпадение с проблемой, с которой кто-то может столкнуться, или пытается предоставить список последних элементов по теме?

После того, как вы выяснили, что вы хотите вернуть, вы можете посмотреть на относительный эффект каждой индексируемой функции. Это приведет к грубому поиску. Оттуда вы настраиваете на основе обратной связи с пользователем (я предлагаю использовать неявную обратную связь вместо явной, в противном случае вы будете раздражать пользователя).

Что касается индексации, вы должны попытаться поместить данные так, чтобы каждый элемент имел всю информацию, необходимую для его ранжирования. Это означает, что вам нужно собрать данные из нескольких мест для их создания. Некоторые системы индексирования имеют возможность добавлять значения к существующим элементам, что облегчает добавление баллов к вопросам при поступлении последующих ответов. Простота просто заставит вас перестраивать вопрос так часто.

2 голосов
/ 21 февраля 2010

Вы, вероятно, больше думали на эту тему, чем большинство людей, которые попытаются ответить вам (одна из причин, почему это был день, и я ваш первый ответ, я бы вообразил). Я просто попытаюсь ответить на ваши последние три вопроса, потому что там очень много вопросов, на которые у меня нет времени, и я думаю, что эти три наиболее интересны (физические вопросы реализации, вероятно, в конечном итоге «выбрать что-то, а затем настроить его, как вы узнаете больше»).

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

ответов Я бы согласился с вашим подходом при условии некоторого тестирования; помните, что это должен быть итеративный процесс, основанный на обратной связи с пользователем (поэтому вам нужно будет собрать метрики о том, вернули ли поиски успешные результаты для поисковика)

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

...