如何使用整数 id 重新索引文档?

How to re-index documents with integer id?

我有 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 为这些文档编制索引。我创建了一个 class 代表这些 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:有点好笑。它不会重现错误。

更新 1:我可以可靠地重现该错误。关键是调用 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;}]

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

更新2:代码已上传至gist。

好的,我想我明白了。问题是您无法通过使用 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