Хранение реляционных данных в индексе Lucene.NET - PullRequest
6 голосов
/ 19 ноября 2009

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

На высоком уровне данные, которые я пытаюсь найти, сгруппированы, каждый элемент принадлежит от 1 до 3 групп. Затем мне нужно будет выполнить поиск всех элементов, которые входят в комбинацию групп (например, каждый элемент принадлежит как группе А, так и группе В).

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

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

Кто-нибудь знает, как лучше всего справиться с подобным поиском в Lucene.NET? (Просто чтобы уточнить, прежде чем кто-то скажет, что я использую не тот инструмент, это лишь часть более широкого набора фильтров, который включает полнотекстовый поиск. услышать, что правильно)

1 Ответ

5 голосов
/ 19 ноября 2009

У меня была доля проблем с хранением реляционных данных в Lucene, но ту, которая у вас есть, должно быть легко исправить.

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

Пожалуйста, проверьте следующий маленький кусочек кода:

internal class Program {
    private static void Main(string[] args) {
        var directory = new RAMDirectory();
        var writer = new IndexWriter(directory, new StandardAnalyzer());
        AddDocument(writer, "group", "stuff", Field.Index.UN_TOKENIZED);
        AddDocument(writer, "group", "other stuff", Field.Index.UN_TOKENIZED);
        writer.Close(true);

        var searcher = new IndexSearcher(directory);
        Hits hits = searcher.Search(new TermQuery(new Term("group", "stuff")));

        for (int i = 0; i < hits.Length(); i++) {
            Console.WriteLine(hits.Doc(i).GetField("group").StringValue());
        }
    }

    private static void AddDocument(IndexWriter writer, string name, string value, Field.Index index) {
        var document = new Document();
        document.Add(new Field(name, value, Field.Store.YES, index));
        writer.AddDocument(document);
    }
}

Образец добавляет в индекс два документа, которые не маркированы, выполняет поиск материала и получает один удар. Если вы изменили код, добавив их токены, у вас будет два попадания, как вы видите сейчас.

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

Еще один пример, иллюстрирующий поведение:

    private static void Main(string[] args) {
        var directory = new RAMDirectory();
        var writer = new IndexWriter(directory, new StandardAnalyzer());

        var documentA = new Document();
        documentA.Add(new Field("name", "A", Field.Store.YES, Field.Index.UN_TOKENIZED));
        documentA.Add(new Field("group", "stuff", Field.Store.YES, Field.Index.UN_TOKENIZED));
        documentA.Add(new Field("group", "other stuff", Field.Store.YES, Field.Index.UN_TOKENIZED));
        writer.AddDocument(documentA);
        var documentB = new Document();
        documentB.Add(new Field("name", "B", Field.Store.YES, Field.Index.UN_TOKENIZED));
        documentB.Add(new Field("group", "stuff", Field.Store.YES, Field.Index.UN_TOKENIZED));
        writer.AddDocument(documentB);
        var documentC = new Document();
        documentC.Add(new Field("name", "C", Field.Store.YES, Field.Index.UN_TOKENIZED));
        documentC.Add(new Field("group", "other stuff", Field.Store.YES, Field.Index.UN_TOKENIZED));
        writer.AddDocument(documentC);

        writer.Close(true);

        var query1 = new TermQuery(new Term("group", "stuff"));
        SearchAndDisplay("First sample", directory, query1);

        var query2 = new TermQuery(new Term("group", "other stuff"));
        SearchAndDisplay("Second sample", directory, query2);

        var query3 = new BooleanQuery();
        query3.Add(new TermQuery(new Term("group", "stuff")), BooleanClause.Occur.MUST);
        query3.Add(new TermQuery(new Term("group", "other stuff")), BooleanClause.Occur.MUST);
        SearchAndDisplay("Third sample", directory, query3);
    }

    private static void SearchAndDisplay(string title, Directory directory, Query query3) {
        var searcher = new IndexSearcher(directory);
        Hits hits = searcher.Search(query3);
        Console.WriteLine(title);
        for (int i = 0; i < hits.Length(); i++) {
            Console.WriteLine(hits.Doc(i).GetField("name").StringValue());
        }
    }
...