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

问题描述

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

UPDATE si
SET si.iCurrentStock = si.iCurrentStock + 
(
    SELECT
        SUM(
                          (CASE 
                              WHEN  ttd2.vEffectType = 'Add' THEN ttd2.iLeafQuantity
                              WHEN ttd2.vEffectType = 'Subtract' THEN ttd2.iLeafQuantity * -1 
                              ELSE 0 
                           END)
        )
    FROM tblTransactionDetail ttd2
    WHERE ttd2.iTransactionId = @p_TransactionId
    AND ISNULL(ttd2.bIsstockUpdated,0) = 0
),ttd.bIsstockUpdated = 1
FROM dbo.tblStoreItem si
INNER JOIN dbo.tblTransactionDetail ttd ON si.iItemId = ttd.iItemId
WHERE si.iStoreId = @v_StoreId
  AND ttd.iTransactionId = @p_TransactionId
  AND ISNULL(ttd.bIsstockUpdated,0) = 0;

我正在尝试使用 Lucene.NET 为这些文档编制索引。我创建了一个表示这些 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}

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

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

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

  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

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

int32 数据库 id 的最佳字段类型是什么,我如何在索引更新操作中使用它来覆盖上一个条目?

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

Core 更适合作为要点:

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

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

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

搜索 Nikon 或 Canon 会产生许多文档。这些都具有相同的 ID。

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

重复:

> 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;}|]

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

Update2:代码上传到 gist。

解决方法

好的,我想我明白了。问题是您无法通过使用 Term 将整数 ID 转换为字符串(例如 GetStringValue)来创建有效的 "3122"。相反,您必须从 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,bytes)
let term = Term("id",bytes)

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