【问题标题】:How to re-index documents with integer id?如何重新索引具有整数 id 的文档?
【发布时间】:2021-07-30 09:27:44
【问题描述】:

我有代表数据库行的 JSON 文档。

{"id":3121,"name":"Nikon AF-S DX Nikkor 35 mm", "brand": "Nikon", "price": 456.32}

{"id":3122,"name":"Canon EF-S 55-250 mm", "brand": "Canon", "price": 500.98}

我正在尝试使用 Lucene.NET 索引这些文档。我创建了一个代表这些 JSON 条目的类。

[<CLIMutable>]
type Lens =
  { Id: int; Name: string; Brand: string; Price: Float}

我可以打开 JSON 条目将其转换为 Lens 对象,然后创建一个 Lucene 文档。

  let getDocument (inputDocument:Lens) =
    let id = StoredField("id", inputDocument.Id)
    let name  = TextField("name", inputDocument.Name, Field.Store.YES)
    let brand  = StoredField("brand", inputDocument.Brand)
    let price  = StoredField("price", inputDocument.Price)
    let doc = Document()
    doc.Add(id)
    doc.Add(name)
    doc.Add(brand)
    doc.Add(price)
    // return
    doc

这行得通,我可以将文档添加到索引中并进行搜索。当我想更新索引中的文档而不是添加它时,问题就开始了。

let upsertDocument (writer:IndexWriter) (doc:Document) =
      try
        let id = doc.GetField("id").GetStringValue()
        let term = Term("id", id)
        writer.UpdateDocument(term, doc)
        writer.Flush(triggerMerge = false, applyAllDeletes = false)
        Ok "Ok"
      with ex ->
        logger
        <| sprintf "Exception : %s" ex.Message
        logger
        <| sprintf "Exception : %A" ex.StackTrace
        Error ex.Message

文档说我必须使用 id 字段创建一个 Term 并使用 .GetStringValue() 作为该术语并调用 .UpdateDocument(term, doc)。但是,这不起作用,每次我调用 upsertDocument 时都会添加一个新文档。

int32 数据库 ID 的最佳字段类型是什么?如何在索引更新操作中使用它来覆盖先前的条目?

单个文件中的完整工作流程:

核心是一个要点:

https://gist.github.com/l1x/91c36b867acc70e8486a6bce7899332a

Update0:有点搞笑。它不会重现错误。

Update1:​​我可以可靠地重现该错误。关键是调用 IndexWriter.commit()。通过检查文档可以看到重复。

搜索 Nikon 或 Canon 会导致很多文档。这些都具有相同的 id。

> searcherz.Search(query, 20).ScoreDocs;;
val it : ScoreDoc [] =
  [|doc=5 score=1.1118877 shardIndex=-1 {Doc = 5;
                                         Score = 1.111887693f;
                                         ShardIndex = -1;};
    doc=6 score=1.1118877 shardIndex=-1 {Doc = 6;
                                         Score = 1.111887693f;
                                         ShardIndex = -1;};
    doc=7 score=1.1118877 shardIndex=-1 {Doc = 7;
                                         Score = 1.111887693f;
                                         ShardIndex = -1;};
    doc=8 score=1.1118877 shardIndex=-1 {Doc = 8;
                                         Score = 1.111887693f;
                                         ShardIndex = -1;}|]

复制:

> let hits = searcherz.Search(query, 20).ScoreDocs;;
val hits : ScoreDoc [] =
  [|doc=5 score=1.1118877 shardIndex=-1; doc=6 score=1.1118877 shardIndex=-1;
    doc=7 score=1.1118877 shardIndex=-1; doc=8 score=1.1118877 shardIndex=-1|]

> hits |> Seq.map (fun hit -> searcherz.Doc(hit.Doc)) |> Seq.map Seq.head;;
val it : seq<IIndexableField> =
  seq
    [stored<id:3122> {Boost = 1.0f;
                      FieldType = stored;
                      IndexableFieldType = stored;
                      Name = "id";
                      NumericType = INT32;};
     stored<id:3122> {Boost = 1.0f;
                      FieldType = stored;
                      IndexableFieldType = stored;
                      Name = "id";
                      NumericType = INT32;};
     stored<id:3122> {Boost = 1.0f;
                      FieldType = stored;
                      IndexableFieldType = stored;
                      Name = "id";
                      NumericType = INT32;};
     stored<id:3122> {Boost = 1.0f;
                      FieldType = stored;
                      IndexableFieldType = stored;
                      Name = "id";
                      NumericType = INT32;}]

我不确定这是错误还是功能。

Update2:代码已上传到 gist。

【问题讨论】:

  • 我对你的最后一句话感到困惑。您粘贴的完整代码是否按预期工作?如果是这样,那似乎可以解决您的问题,对吗?
  • 更新:我想我已经复制了你所看到的。我对 Lucene 没有任何经验,但我会看看我能弄清楚什么。
  • 真的很奇怪。我粘贴的代码正是我们在生产中所拥有的。生产代码产生重复。我粘贴的代码没有产生重复。我需要进一步调查。
  • 好吧,我对 IndexWriter 的理解可能完全不对了。我不确定如何拥有一个只将数据写入磁盘的写入器和一个从磁盘读取的读取器。即使在使用带有术语的 UpdateDocument 时,作者也会确定重复数据。如果我使用写入器创建磁盘内容,则读取器看不到磁盘内容。
  • 谢谢@brianberns。我对 Lucene 没有太多经验。我想我应该试着弄清楚什么是一致性模型以及应该如何使用 IndexWriter。

标签: c# f# lucene lucene.net


【解决方案1】:

好的,我想我想通了。问题是您无法通过使用GetStringValue 将整数ID 转换为字符串(例如"3122")来创建有效的Term。相反,您必须从 ID 的原始字节(例如 [60 8 0 0 18 31])创建术语,如下所示:

open Lucene.Net.Util

let id = doc.GetField("id").GetInt32Value().Value
let bytes = BytesRef(NumericUtils.BUF_SIZE_INT32)
NumericUtils.Int32ToPrefixCodedBytes(id, 0, bytes)
let term = Term("id", bytes)

进行此更改后,我不再在索引中看到重复的文档。请参阅this SO question 了解更多信息。

【讨论】:

    猜你喜欢
    • 2011-05-27
    • 1970-01-01
    • 2014-11-07
    • 2022-10-16
    • 1970-01-01
    • 2015-09-26
    • 2015-09-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多