在Lucene Index中添加一对多数据

时间:2019-05-02 01:09:49

标签: asp.net vb.net lucene lucene.net

我是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整数列表。我是否必须为整数列表中的每个项目添加相同的文档?如果是这样,您如何最好地管理它?我需要包括几个“一对多”关系。我可以看到它很快变得笨拙。还是有更好的方法?

编辑 我更新了将可能值添加为这样的字段:

 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")

2 个答案:

答案 0 :(得分:0)

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

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

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

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

答案 1 :(得分:0)

使用术语一对多

您已经发现,可以使用包括空格在内的各种分隔符来连接整数,结果如下:

"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通过|分隔符将短语分解,然后转换为小写。

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

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