将一对多数据添加到 Lucene 索引

Adding one to many data to Lucene Index

我是 Lucene 新手。我正在尝试创建记录索引。到目前为止,我只是将一对一的数据添加到我的索引中,这看起来不错。但是,在某些情况下,我需要添加一对多关系数据,但我不确定处理此问题的最佳方法是什么。我尝试添加每个单独的关系,将字段合并为 CSV 值,多次添加字段,但似乎没有任何效果。这是我在索引数据时的代码:

Private Shared Sub _addToLuceneIndex(ByVal sampleData As LuceneSearchData, ByVal writer As IndexWriter)
    Dim searchQuery = New TermQuery(New Term("Id", sampleData.Id.ToString()))
    writer.DeleteDocuments(searchQuery)
    Dim doc = New Document()

    doc.Add(New Field("Id", sampleData.Id.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED))
    doc.Add(New Field("Name", sampleData.Name, Field.Store.YES, Field.Index.ANALYZED))
    doc.Add(New Field("Description", sampleData.Description, Field.Store.YES, Field.Index.ANALYZED))

    For Each item As Integer In sampleData.HomeStates 
        doc.Add(New Field("Home_State", item, Field.Store.YES, Field.Index.ANALYZED))
    Next
   'i have also tried
'doc.Add(New Field("HomeStates ", String.Join(",", sampleData.HomeStates ), Field.Store.YES, Field.Index.ANALYZED))

    writer.AddDocument(doc)
End Sub

虽然上面的代码似乎为一对一数据建立了索引,但它并没有为 HomeStates 整数列表建立索引。我是否必须为整数列表中的每个项目添加相同的文档?如果是这样,您如何最好地管理它?我需要包括几个 "one-to-many" 关系。我可以看到这很快变得笨拙。或者,有更好的方法吗?

编辑 我更新为将一个添加到可能值作为这样的字段:

 doc.Add(New Field("Geo_Locations", String.Join(" ", sampleData.Geo_Location), Field.Store.YES, Field.Index.ANALYZED))

这是我搜索该字段的方式:

Private Shared Function _search(ByVal searchQuery As String, ByVal Optional searchField As String = "") As IEnumerable(Of LuceneSearchData)
    If String.IsNullOrEmpty(searchQuery.Replace("*", "").Replace("?", "")) Then Return New List(Of LuceneSearchData)()

    Using searcher = New IndexSearcher(_directory, False)
        Dim hits_limit = 1000
        Dim analyzer = New StandardAnalyzer(Version.LUCENE_30)

        If Not String.IsNullOrEmpty(searchField) Then
            Dim parser = New QueryParser(Version.LUCENE_30, searchField, analyzer)
            Dim query = parseQuery(searchQuery, parser)
            Dim hits = searcher.Search(query, hits_limit).ScoreDocs
            Dim results = _mapLuceneToDataList(hits, searcher)
            analyzer.Close()
            searcher.Dispose()
            Return results
        End If
    End Using
End Function

Private Shared Function _mapLuceneToDataList(ByVal hits As IEnumerable(Of ScoreDoc), ByVal searcher As IndexSearcher) As IEnumerable(Of LuceneSearchData)
    Dim listOfResults As List(Of LuceneSearchData)

    Try
        listOfResults = hits.[Select](Function(hit) _mapLuceneDocumentToData(searcher.Doc(hit.Doc))).ToList()            
    Catch ex As Exception
        Return Nothing
    End Try
    Return listOfResults
End Function

Private Shared Function _mapLuceneDocumentToData(ByVal doc As Document) As LuceneSearchData

    Return New LuceneSearchData With {
        .Id = Convert.ToInt32(doc.[Get]("Id")),
        .Mechanism_Name = doc.[Get]("Name"),
        .Mechanism_Purpose = doc.[Get]("Description"),            
        .Geo_Location = doc.[Get]("Home_State")
    }
End Function

然后我调用搜索bu:

   LuceneData = LuceneSearch.Search("5451", "HomeStates")

为每个项目创建一个新文档将导致在搜索其他字段时出现重复。

您应该根据需要设计文档和字段。

如果您不需要搜索这些字段,请随意存储它们。

如果您需要搜索这些多值字段,请创建一个可以搜索每个项目的字段。 对于整数列表,您可以将它们与空格合并。 对于字符串列表,您应该在索引和搜索时用一些自定义字符替换空格以避免匹配子字符串(例如 "stack overflow" => stack_overflow)

一对多使用术语

如您所知,可以使用包括空格在内的各种分隔符连接整数,结果如下:

"234 12342 345 5476456 234"

StandardAnalyzer 会将上述字符串标记为单独的标记,就像任何其他字符串一样。所以你可以搜索它们,它会给你预期的结果。

一对多使用短语

如果您需要对短语进行分词,例如:

"Control Support Engineer|Technical Support Engineer|Maintenance Technician"

分解为单个短语,而不是单词(术语),然后您需要继承一些 类。一个继承自 CharTokenizer 的分词器和一个继承自 Analyzer.

的分析器

分词器

public sealed class PipeTokenizer: CharTokenizer
{
    public PipeTokenizer(LuceneVersion matchVersion, TextReader input) : 
        base(matchVersion, input)
    {
    }

    public PipeTokenizer(LuceneVersion matchVersion, AttributeFactory factory, TextReader input) : 
        base(matchVersion, factory, input)
    {
    }

    protected override bool IsTokenChar(int c)
    {
        return !((char)c).Equals('|'); //<-- the only line that matters
    }
}

到目前为止还很简单。

分析器

public class PipeAnalyzer: Analyzer
{
    protected override TokenStreamComponents CreateComponents(string fieldName, TextReader reader)
    {
        var tokenizer = new PipeTokenizer(LuceneVersion.LUCENE_48, reader);
        var lowerCaseFilter = new LowerCaseFilter(LuceneVersion.LUCENE_48, tokenizer); 
        return new TokenStreamComponents(tokenizer, lowerCaseFilter);
    }
}

这是基于 KeywordAnalyzer 所做的,但有所不同。 KeywordAnalyzer 非常简单。它采用整个字符串或短语,并按原样对其进行索引。没有进一步分解成更小的代币。上面的分析器使用 PipeTokenizer 通过 | 分隔符分解短语,并转换为小写。

使用以下任何短语查询将 return 文档:

"control support engineer"
"technical support engineer"
"maintenance technician"