最近需要对公司的产品搜索功能做一步改动,搜索到的结果首先按照是否有库存进行排序,然后再按照销量。由于库存量也是一个整数,如果直接按照库存量进行倒序排序的话,是不符合要求的,Lucene也没有支持我们这种特殊的业务需求,但是可以通过扩展的方式进行改写。
参考文档:http://blog.csdn.net/cctcc/article/details/45672247
public class EmptyStockComparatorSource extends FieldComparatorSource {
@Override
public FieldComparator<?> newComparator(String fieldname, int numHits, int sortPos, boolean reversed)
throws IOException {
return new LongComparator(numHits, fieldname, 0L);
}
public static class LongComparator extends FieldComparator.NumericComparator<Long> {
private final long[] values;
private long bottom;
private long topValue;
/**
* Creates a new comparator based on {@link Long#compare} for {@code numHits}.
* When a document has no value for the field, {@code missingValue} is substituted.
*/
public LongComparator(int numHits, String field, Long missingValue) {
super(field, missingValue);
values = new long[numHits];
}
@Override
protected void doSetNextReader(LeafReaderContext context) throws IOException {
currentReaderValues = getNumericDocValues(context, field);
if (missingValue != null) {
docsWithField = getDocsWithValue(context, field);
// optimization to remove unneeded checks on the bit interface:
if (docsWithField instanceof Bits.MatchAllBits) {
docsWithField = null;
}
} else {
docsWithField = null;
}
}
@Override
public int compare(int slot1, int slot2) {
return Long.compare(values[slot1], values[slot2]);
}
@Override
public int compareBottom(int doc) {
// TODO: there are sneaky non-branch ways to compute
// -1/+1/0 sign
long v2 = currentReaderValues.get(doc);
// Test for v2 == 0 to save Bits.get method call for
// the common case (doc has value and value is non-zero):
if (docsWithField != null && v2 == 0 && !docsWithField.get(doc)) {
v2 = missingValue;
}
return Long.compare(bottom, v2);
}
@Override
public void copy(int slot, int doc) {
long v2 = currentReaderValues.get(doc);
// Test for v2 == 0 to save Bits.get method call for
// the common case (doc has value and value is non-zero):
if (docsWithField != null && v2 == 0 && !docsWithField.get(doc)) {
v2 = missingValue;
}
values[slot] = v2 > 0L ? 1L : 0L;
}
@Override
public void setBottom(final int bottom) {
this.bottom = values[bottom];
}
@Override
public void setTopValue(Long value) {
topValue = value;
}
@Override
public Long value(int slot) {
return Long.valueOf(values[slot]) ;
}
@Override
public int compareTop(int doc) {
long docValue = currentReaderValues.get(doc);
// Test for docValue == 0 to save Bits.get method call for
// the common case (doc has value and value is non-zero):
if (docsWithField != null && docValue == 0 && !docsWithField.get(doc)) {
docValue = missingValue;
}
return Long.compare(topValue, docValue);
}
}
}

其中LongComparator直接从lucene源码中copy出来,只需要做些许修改即可,最主要的修改就是copy(int slot, int doc)方法,在复制比较值得过程中,将所有存在库存的值都视为1,否则视为0,这样排序的结果就是我们所期待的。
我们用到的测试用例:
Directory directory1 = FSDirectory.open(Paths.get(
"/Users/xxx/develop/tools/solr-5.5.0/server/solr/product/data/index"));
DirectoryReader directoryReader1 = DirectoryReader.open(directory1);
IndexSearcher searcher1 = new IndexSearcher(directoryReader1);
Sort sort1 = new Sort(new SortField("psfixstock", new EmptyStockComparatorSource(), true),
new SortField("salesVolume", SortField.Type.INT, true));
TopFieldDocs topDocs1 = searcher1.search(new TermQuery(new Term("gender_text", "女士")), 10, sort1);
for (ScoreDoc scoreDoc : topDocs1.scoreDocs) {
int doc = scoreDoc.doc;
Document document = searcher1.doc(doc);
System.out.println(String.format("docId=%s, psfixstock=%s, salesVolumn=%s", doc, document.get("psfixstock"), document.get("salesVolume")));
}

在排序时,需要将其加入至Sort对象中,但执行的时候出现错误,显示docvalues的类型不正确:
Exception in thread "main" java.lang.IllegalStateException: unexpected docvalues type NONE for field 'psfixstock' (expected=NUMERIC). Use UninvertingReader or index with docvalues.
at org.apache.lucene.index.DocValues.checkField(DocValues.java:208)
at org.apache.lucene.index.DocValues.getNumeric(DocValues.java:227)
at org.apache.lucene.search.FieldComparator$NumericComparator.getNumericDocValues(FieldComparator.java:167)
at com.zp.solr.handler.component.EmptyStockComparatorSource$LongComparator.doSetNextReader(EmptyStockComparatorSource.java:36)
at org.apache.lucene.search.SimpleFieldComparator.getLeafComparator(SimpleFieldComparator.java:36)
at org.apache.lucene.search.FieldValueHitQueue.getComparators(FieldValueHitQueue.java:183)
at org.apache.lucene.search.TopFieldCollector$SimpleFieldCollector.getLeafCollector(TopFieldCollector.java:164)
at org.apache.lucene.search.IndexSearcher.search(IndexSearcher.java:812)
at org.apache.lucene.search.IndexSearcher.search(IndexSearcher.java:535)
at org.apache.lucene.search.IndexSearcher.search(IndexSearcher.java:744)
at org.apache.lucene.search.IndexSearcher.searchAfter(IndexSearcher.java:729)
at org.apache.lucene.search.IndexSearcher.searchAfter(IndexSearcher.java:671)
at org.apache.lucene.search.IndexSearcher.search(IndexSearcher.java:577)
at org.apache.lucene.search.IndexSearcher.search(IndexSearcher.java:627)
at com.zp.solr.handler.component.EmptyStockSortingTest.main(EmptyStockSortingTest.java:57)

经过一番查找,找到原因,参考文档:http://qindongliang.iteye.com/blog/2297280,我们搜索所使用到的字段没有设置对应的docType。如果在solr中,需要进行手动排序的字段,设置docValues=“true”,并进行重新索引(使用full-import方式):
<field name="psfixstock" type="tint" indexed="true" stored="true" multiValued="false" docValues="true" />

必须要重新建立索引才可以正常运行。注意,此时Solr与Elastic Search采取的方案有所不同,Solr默认docValues=false,而ES则相反,使用Doc索引方式会对性能产生一定的影响,要谨慎使用。
对于lucene中,需要将添加document中增加数字类型Field:NumericDocValuesField,否则出现上面的错误,
document.add(new NumericDocValuesField("stock", stock));

最终的排序结果已经按照我们的需要进行了:
docId=2629, psfixstock=98391, salesVolumn=4685
docId=305, psfixstock=991, salesVolumn=14
docId=16762, psfixstock=3, salesVolumn=12
docId=22350, psfixstock=993, salesVolumn=10
docId=29021, psfixstock=11076, salesVolumn=10
docId=3635, psfixstock=61, salesVolumn=6
docId=4111, psfixstock=1104, salesVolumn=5
docId=10608, psfixstock=4395, salesVolumn=5
docId=4874, psfixstock=4975, salesVolumn=4
docId=4911, psfixstock=6, salesVolumn=4
docId=15071, psfixstock=998, salesVolumn=4
docId=4837, psfixstock=9, salesVolumn=3
docId=4860, psfixstock=1002, salesVolumn=3
docId=3749, psfixstock=2240, salesVolumn=2
docId=4109, psfixstock=1493, salesVolumn=2
docId=15068, psfixstock=1000, salesVolumn=2
docId=25901, psfixstock=11110, salesVolumn=2
docId=3688, psfixstock=21, salesVolumn=1
docId=4912, psfixstock=17, salesVolumn=1
docId=5035, psfixstock=2, salesVolumn=1
docId=11835, psfixstock=8, salesVolumn=1
docId=12044, psfixstock=1, salesVolumn=1
docId=13508, psfixstock=2, salesVolumn=1
docId=20019, psfixstock=1, salesVolumn=1
docId=20884, psfixstock=100000, salesVolumn=1
docId=22620, psfixstock=1, salesVolumn=1
docId=24128, psfixstock=1, salesVolumn=1
docId=0, psfixstock=2, salesVolumn=0
docId=9, psfixstock=1, salesVolumn=0
docId=11, psfixstock=4, salesVolumn=0
docId=15, psfixstock=3, salesVolumn=0
docId=20, psfixstock=4, salesVolumn=0
docId=23, psfixstock=2, salesVolumn=0
docId=24, psfixstock=5, salesVolumn=0
docId=25, psfixstock=7, salesVolumn=0
docId=35, psfixstock=2, salesVolumn=0
docId=53, psfixstock=2, salesVolumn=0

Lucene根据字段进行自定义搜索扩展相关推荐

  1. lucene使用3.0.3_使用Apache Lucene 4.3轻松进行搜索

    lucene使用3.0.3 Lucene是用Java编写的全文搜索引擎,可以为任何应用程序提供强大的搜索功能. Lucene的核心是基于文件的全文本索引. Lucene提供API创建该索引,然后向该索 ...

  2. 使用Apache Lucene 4.3轻松进行搜索

    Lucene是用Java编写的全文搜索引擎,可以为任何应用程序提供强大的搜索功能. Lucene的核心是基于文件的全文本索引. Lucene提供API创建该索引,然后向该索引添加和删除内容. 此外,它 ...

  3. Visual Studio和VS Code自定义文件扩展名映射

    Visual Studio和VS Code自定义文件扩展名映射 自定义文件扩展名映射可以让Visual Studio和VS Code将一种扩展名识别为另一种扩展名,以便增加对原扩展名文件的语法高亮和智 ...

  4. ABAP:ALV中自定义搜索帮助

    如果希望ALV中某字段具有搜索帮助,第一种办法当然是对表中某字段的引用,设置ref_table.ref_field,将自动触发该字段所带的搜索帮助. 可不可以直接设置Searh Help呢?应该不可以 ...

  5. Lucene –快速添加索引和搜索功能

    什么是Lucene? Apache LuceneTM是完全用Java编写的高性能,功能齐全的文本搜索引擎库. 它是一项适用于几乎所有需要全文搜索的应用程序的技术,尤其是跨平台的应用程序. Lucene ...

  6. java搜索引擎创建索引_搜索引擎系列 ---lucene简介 创建索引和搜索初步

    一.什么是Lucene? Lucene最初是由Doug Cutting开发的,2000年3月,发布第一个版本,是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎 :Lucene得名于Doug妻子 ...

  7. odoo10参考系列--ORM API 三(字段、继承与扩展、域和更新到新API)

    字段 基础字段 class odoo.fields.Field(string=<object object>, **kwargs) 字段描述符包含字段定义,并管理记录中相应字段的访问和分配 ...

  8. ABAP ALV中自定义搜索帮助

    一.干货: 在fieldcat-ref_table 和 fieldcat-ref_field 填入参考表和参考字段,搜索帮助就自动出来了. 想要选择屏幕添加自定义搜索帮助-转到这里 ALV自定义搜索帮 ...

  9. Salesforce自定义搜索结果布局

    自定义搜索结果布局: 当您的用户搜索信息时,他们希望搜索结果显示对他们最重要的信息.通过搜索布局,您可以选择用户在他们的搜索结果和查找对话框中看到哪些标准和自定义对象及字段.在 Lightning E ...

最新文章

  1. php 自定义表格并统计,PHP 使用Echarts生成数据统计报表的实现
  2. 基于STM32分析栈、堆、全局区、常量区、代码区、RAM、ROM
  3. Django后台admin的使用
  4. 1040 有几个PAT (25分)——18行代码AC
  5. LeetCode 1362. 最接近的因数
  6. 移动项目工作笔记0002---使用Weex开发移动端应用
  7. 深度学习的研究方向: 你会为AI转型么?
  8. Nginx 日志配置实践
  9. [EMNLP18]针对自顶向下和中序移进归约成分句法分析的Dynamic Oracles
  10. linux debian安装ssh,Ubuntu Linux上安装SSH和vsFTPd的方法
  11. MySQL 高阶语句之函数存储
  12. 计算机的标准输入法,维语输入法电脑版
  13. 全方位剖析“清华同方”,脉络千里!!
  14. 张宇高数 第二讲 一元函数微分学(思维导图)
  15. 台式计算机刚做完系统就蓝屏,做完系统蓝屏怎么办|刚做完系统重启电脑蓝屏解决方法...
  16. Character Controller角色控制器组件
  17. Mac 和 iPad 产品线要合并?苹果高管回应了
  18. 【JS教程】移动端 Touch(触摸)事件
  19. 那些年,我们用过的服务器软件
  20. 【微信小程序】微信开发文档摘要

热门文章

  1. 脉搏波相关概念及内容-记录整理
  2. 在线微量水总结与应用
  3. linux通过ssh实现反向连接
  4. JsonObject存入顺序和输出顺序不一样问题-豆果
  5. 第十三章 Java 文件与流
  6. 全自动过滤器:全自动管道过滤器工作原理
  7. mser python车牌识别_基于MSER与SVM算法的车牌定位识别方法
  8. 手机配件实体店好做不_震惊!手机实体店,你不得不防的套路!
  9. 有关python的比赛_用 Python 写一个 Kaggle 比赛排行榜的爬虫
  10. 疫情导致招聘平台Xing的母公司New Work SE裁员