我正在寻找一种方法来自定义我的Lucene.Net结果,我放置null
- 底部的值(文档上不存在字段),无论排序的方向(升序或降序)
以下数据总结了情况和想要的结果:
data in index wanted sort result
data desc asc
---- ---- ----
100 400 100
400 300 300
null 100 400
300 null null
我的情况是,我有一些产品并非所有产品都有价格。当升序排序时,我首先想要最便宜的产品,而不是没有价格的产品(这是预期的默认行为)。没有价格的产品仍然应该在结果中,但最后,因为这些在价格排序时最不相关。
我已经看了很多谷歌,我还没有真正找到你如何在 Lucene.Net 3.0.3 中实现自定义排序的任何答案。
我发现的最好的例子是this answer,这似乎指向了我正在寻找的方向。但答案是陈旧的,它所引用的ScoreDocComparator
似乎是原始来源中的deprecated,因此也是Lucene的当前版本3.0.3 。净。
原始项目将FieldComparator称为替换,但这似乎比ScoreDocComparator
要复杂得多(许多方法需要实现/覆盖,许多方法可以从继承中获益)重复的实现),我怀疑这是正确的道路吗?
理想情况下,我想为int / long字段实现一些通用的东西,它可以像在SortField对象中一样使用fieldname,因为我希望将来有更多的字段可以使这种自定义排序行为受益。
我认为实现是在Sort
/ SortField
类的使用周围完成的,所以我的结束使用代码可能是这样的:
var sort = new Sort(new MyNullLastSortField("Price", SortField.INT, reverse));
但也许这也是错误的方式? SortField
有一个构造函数,它以FieldComparator
作为参数,但我似乎无法理解它是如何构造和实现的,以及来自索引的实际数据值流入的位置和进行。
非常感谢任何帮助我指向正确方向的帮助(最好带有示例代码)。
我的故障转移解决方案(不是首选)将向索引添加两个字段,仅用于进行排序,在插入时手动处理空值并在降序情况下将它们设置为-1在升序的情况下到9999999。然后通常使用具有价格和方向的特定字段名称的字段进行排序。
答案 0 :(得分:1)
好奇心得到了我的好处。这是一个解决方案(有警告)
完整来源位于https://github.com/AndyPook/SO_CustomSort-40744865
添加可为空的int的扩展方法。 NumericField使用编码存储值,我不想进入,所以我只使用了一个标记值。
public static class NumericFieldExtensions
{
public static NumericField SetIntValue(this NumericField f, int? value)
{
if (value.HasValue)
f.SetIntValue(value.Value);
else
f.SetIntValue(int.MinValue);
return f;
}
}
"了解"定点。它只是lucene IntComparator
的副本sealed
,因此要复制。寻找int.MinValue
以查看差异。
public class NullableIntComparator : FieldComparator
{
private int[] values;
private int[] currentReaderValues;
private string field;
private IntParser parser;
private int bottom; // Value of bottom of queue
private bool reversed;
public NullableIntComparator(int numHits, string field, Parser parser, bool reversed)
{
values = new int[numHits];
this.field = field;
this.parser = (IntParser)parser;
this.reversed = reversed;
}
public override int Compare(int slot1, int slot2)
{
// TODO: there are sneaky non-branch ways to compute
// -1/+1/0 sign
// Cannot return values[slot1] - values[slot2] because that
// may overflow
int v1 = values[slot1];
int v2 = values[slot2];
if (v1 == int.MinValue)
return reversed ? -1 : 1;
if (v2 == int.MinValue)
return reversed ? 1 : -1;
if (v1 > v2)
{
return 1;
}
else if (v1 < v2)
{
return -1;
}
else
{
return 0;
}
}
public override int CompareBottom(int doc)
{
if (bottom == int.MinValue)
return reversed ? -1 : 1;
// TODO: there are sneaky non-branch ways to compute
// -1/+1/0 sign
// Cannot return bottom - values[slot2] because that
// may overflow
int v2 = currentReaderValues[doc];
if (v2 == int.MinValue)
return reversed ? 1 : -1;
if (bottom > v2)
{
return 1;
}
else if (bottom < v2)
{
return -1;
}
else
{
return 0;
}
}
public override void Copy(int slot, int doc)
{
values[slot] = currentReaderValues[doc];
}
public override void SetNextReader(IndexReader reader, int docBase)
{
currentReaderValues = FieldCache_Fields.DEFAULT.GetInts(reader, field, parser);
}
public override void SetBottom(int bottom)
{
this.bottom = values[bottom];
}
public override IComparable this[int slot] => values[slot];
}
最后一个FieldComparatorSource
来定义自定义排序
public class NullableIntFieldCompatitorSource : FieldComparatorSource
{
public override FieldComparator NewComparator(string fieldname, int numHits, int sortPos, bool reversed)
{
return new NullableIntComparator(numHits, fieldname, FieldCache_Fields.NUMERIC_UTILS_INT_PARSER, reversed);
}
}
一些测试。了解Sort
是如何创建插件的。{/ p>
private class DataDoc
{
public int ID { get; set; }
public int? Data { get; set; }
}
private IEnumerable<DataDoc> Search(Sort sort)
{
var result = searcher.Search(new MatchAllDocsQuery(), null, 99, sort);
foreach (var topdoc in result.ScoreDocs)
{
var doc = searcher.Doc(topdoc.Doc);
int id = int.Parse(doc.GetFieldable("id").StringValue);
int data = int.Parse(doc.GetFieldable("data").StringValue);
yield return new DataDoc
{
ID = id,
Data = data == int.MinValue ? (int?)null : data
};
}
}
[Fact]
public void SortAscending()
{
var sort = new Sort(new SortField("data", new NullableIntFieldCompatitorSource()));
var result = Search(sort).ToList();
Assert.Equal(4, result.Count);
Assert.Equal(new int?[] { 100, 300, 400, null }, result.Select(x => x.Data));
}
[Fact]
public void SortDecending()
{
var sort = new Sort(new SortField("data", new NullableIntFieldCompatitorSource(),true));
var result = Search(sort).ToList();
Assert.Equal(4, result.Count);
Assert.Equal(new int?[] { 400, 300, 100, null }, result.Select(x => x.Data));
}
请注意
NullableIntFieldCompatitorSource
更复杂,以便为您的字段名称返回正确的比较符。NumericField
并找出如何编码null
。但这意味着要进入其他几个班级