Как использовать Lucene.NET для реализации поиска на сайте, таком как Stack Overflow?

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

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

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

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

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

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

Технологии:

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

Целью также является переход на .NET/С# 4.0 как можно скорее. Следует отметить, что, хотя я не думаю, что это игровой чейнджер.

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

Модели

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

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

Важно отметить эти модели:

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

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

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

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

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

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

SQL Server 2008

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

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

Lucene.NET

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

Индексация

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

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

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

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

В качестве примера задается вопрос, а затем ответ подробно обсуждается.

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

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

Метки

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

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

Однако на практике выполнение запроса всех элементов на основе тегов (например, щелчка по тегу в Stack Overflow) очень просто с 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 для заказа на основе интервала оценки Wilson следующим образом:

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

Похоже, что это простой способ использовать эту функцию для обработки элемента Qaru "получить все элементы, помеченные тегом <tag> ".

Ответы

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

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

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

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

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

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

подталкивания

Это связано, конечно, с индексированием, но я считаю, что это заслуживает его собственного раздела.

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

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

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

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

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

И затем с помощью boost, но не на основе исходного значения. Я считаю, что у меня есть тот, который покрыт интервалом времени Уилсона.

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

Поиск тегов

Первоначально я думал, что при запросе на тег (путем отдельного нажатия на один или с использованием структуры 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 (вместе с идентификаторами элементов, которые нужно выбрать), после чего я выполнил вычисление с оценкой достоверности и затем вернул данные правильно.

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

Ответ 1

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

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

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

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

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

Ответ 2

Индекс lucene будет иметь следующие поля:

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

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

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

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

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

Хотя взаимосвязь вопросов не очень распространена, наличие базового веса, похожего на pagerank, может вызвать интересные результаты.

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

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

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

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

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

Ответ 3

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

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

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

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

Ответ 4

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

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

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

Ответ 5

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

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

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