最近做lucence的应用,趁着一个节点的间歇,总结了下lucence中有关自定义搜索结果排序的相关代码,一来和大家共同探讨,二来也便于备忘。
众所周知,lucence默认的结果是根据Score从高到低,当Score相等时,则会根据建立索引时创建的docID由小到大排序。通过自定义搜索结果的排序,则可以实现完全按照真实业务的需要,自定义结果的排序。
下面以一个查询餐馆距离的例子配合代码进行讲解(该例很多地方都有,但是我参考的时候发现很多地方提供的例子都是不能直接运行的)。并提供可以直接运行的例子代码如下:
DistanceComparatorSource.java
package com.xxx.demo;
import java.io.IOException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.FieldCache;
import org.apache.lucene.search.FieldComparator;
import org.apache.lucene.search.FieldComparatorSource;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.FieldCache.IntParser;
public class DistanceComparatorSource extends FieldComparatorSource{
private int x;
private int y;
public DistanceComparatorSource(int x,int y){
this.x = x;
this.y = y;
}
@Override
public FieldComparator newComparator(String fieldname,int numHits,
int sortPos,boolean reversed) throws IOException{
return new DistanceScoreDocLookupComparator(fieldname,numHits);
}
private class DistanceScoreDocLookupComparator extends FieldComparator{
private int[] xDoc,yDoc;
private float[] values;
private float bottom;
String fieldName;
public DistanceScoreDocLookupComparator(String fieldName,int numHits){
values = new float[numHits];
this.fieldName = fieldName;
}
private class DistanceXIntParser implements IntParser{
@Override
public int parseInt(String string){
return Integer.parseInt(string.split(",")[0]);
}
}
private class DistanceYIntParser implements IntParser{
@Override
public int parseInt(String string){
return Integer.parseInt(string.split(",")[1]);
}
}
@Override
public int compare(int slot1,int slot2){
if(values[slot1]<values[slot2]) return -1;
if(values[slot1]>values[slot2]) return 1;
return 0;
}
@Override
public int compareBottom(int doc) throws IOException{
float docDistance = getDistance(doc);
if(bottom<docDistance) return -1;
if(bottom>docDistance) return 1;
return 0;
}
@Override
public void copy(int slot,int doc) throws IOException{
values[slot] = getDistance(doc);
}
@Override
public void setBottom(int slot){
bottom = values[slot];
}
@Override
public void setNextReader(IndexReader reader,int docBase)
throws IOException{
xDoc = FieldCache.DEFAULT.getInts(reader,this.fieldName,new DistanceXIntParser());
yDoc = FieldCache.DEFAULT.getInts(reader,this.fieldName,new DistanceYIntParser());
}
@Override
public Float value(int slot){
return new Float(values[slot]);
}
private float getDistance(int doc){
int deltax = xDoc[doc] - x;
int deltay = yDoc[doc] - y;
return (float)Math.sqrt(deltax*deltax + deltay*deltay);
}
public int sortType(){
return SortField.CUSTOM;
}
}
public String toString(){
return "Distance from ("+x+","+y+")";
}
}
DistanceSortingTest.java
package com.xxx.demo;
import java.io.IOException;
import junit.framework.TestCase;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.FieldDoc;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.TopFieldDocs;
import org.apache.lucene.store.RAMDirectory;
import org.apache.lucene.util.Version;
public class DistanceSortingTest extends TestCase{
private RAMDirectory directory;
private IndexSearcher searcher ;
private Query query;
protected void setUp() throws Exception{
directory = new RAMDirectory();
IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_33,new StandardAnalyzer(Version.LUCENE_33));
config.setOpenMode(OpenMode.CREATE);
IndexWriter writer = new IndexWriter(directory,config);
addPoint(writer,"El Charro","restaurant restaurant restaurant",1,2);//5
addPoint(writer,"Cafe Poca Cosa","restaurant",5,9);//25+81=106
addPoint(writer,"Los Betos","restaurant",9,6);//81+36=117
addPoint(writer,"Nico's Taco Shop","restaurant restaurant",3,8);//9+64=73
writer.close();
searcher = new IndexSearcher(directory);
QueryParser parser = new QueryParser(Version.LUCENE_33, "type", new StandardAnalyzer(Version.LUCENE_33));
query = parser.parse("type:restaurant");
}
private void addPoint(IndexWriter writer,String name,String type,int x,int y)
throws CorruptIndexException, IOException{
Document doc = new Document();
doc.add(new Field("name",name,Field.Store.YES,Field.Index.ANALYZED));
doc.add(new Field("type",type,Field.Store.YES,Field.Index.ANALYZED));
doc.add(new Field("location",x+","+y,Field.Store.YES,Field.Index.NOT_ANALYZED));
writer.addDocument(doc);
}
public void testNormRestaurant() throws IOException{
TopDocs hits = searcher.search(query,10);
System.out.println("--------testNormRestaurant---------- ");
for(ScoreDoc doc : hits.scoreDocs){
System.out.println("docId:"+doc.doc+"score:"+doc.score+", name:"+searcher.doc(doc.doc).get("name"));
}
assertEquals("first","Cafe Poca Cosa",searcher.doc(hits.scoreDocs[0].doc).get("name"));
assertEquals("second","Los Betos",searcher.doc(hits.scoreDocs[1].doc).get("name"));
assertEquals("third","Nico's Taco Shop",searcher.doc(hits.scoreDocs[2].doc).get("name"));
assertEquals("forth","El Charro",searcher.doc(hits.scoreDocs[3].doc).get("name"));
}
public void testNearestRestaurantToHome() throws IOException{
Sort sort = new Sort(new SortField("location",new DistanceComparatorSource(0,0)));
TopDocs hits = searcher.search(query,null,10,sort);
System.out.println("--------testNearestRestaurantToHome---------- ");
for(ScoreDoc doc : hits.scoreDocs){
System.out.println("docId:"+doc.doc+"name:"+searcher.doc(doc.doc).get("name"));
}
assertEquals("cloest","El Charro",searcher.doc(hits.scoreDocs[0].doc).get("name"));
assertEquals("second","Nico's Taco Shop",searcher.doc(hits.scoreDocs[1].doc).get("name"));
assertEquals("third","Cafe Poca Cosa",searcher.doc(hits.scoreDocs[2].doc).get("name"));
assertEquals("furthest","Los Betos",searcher.doc(hits.scoreDocs[3].doc).get("name"));
}
public void testNearestRestaurantToWork() throws IOException{
Sort sort = new Sort(new SortField("location",new DistanceComparatorSource(10,10)));
TopFieldDocs docs = searcher.search(query,null,3,sort);
assertEquals(4,docs.totalHits);
assertEquals(3,docs.scoreDocs.length);
FieldDoc fieldDoc = (FieldDoc)docs.scoreDocs[0];
assertEquals("(10,10) -> (9,6) = sqrt(17)",new Float(Math.sqrt(17)),fieldDoc.fields[0]);
Document document = searcher.doc(fieldDoc.doc);
assertEquals("Los Betos", document.get("name"));
}
}
分享到:
相关推荐
7. **Filter**和**Collector**:用于进一步优化搜索结果,例如根据时间、地理位置等条件过滤,或自定义结果排序。 ### Lucene搜索技术Demo 在“LuceneIndexDemo”中,我们通常会经历以下步骤: 1. **初始化索引**...
深入学习AJAX Lucene,你可以探索更多高级特性,如模糊搜索、短语搜索、过滤器、分页以及自定义排序算法。同时,理解JavaScript和Lucene的相关概念,如DOM操作、Promise、ES6语法、分析器和查询解析器,都将有助于你...
《lucene+nutch开发自己的搜索引擎一书源代码》是一份专为初学者设计的资源,旨在教授如何利用Apache Lucene和Nutch构建自定义搜索引擎。Lucene是Java编写的一个高性能全文检索库,而Nutch则是一个开源的网络爬虫...
5. **高级特性**:介绍如命中高亮、排序、 faceted search(分面搜索)、实时索引、分布式搜索等进阶主题,这些特性使得Lucene能应对更复杂的搜索需求。 6. **性能优化**:讨论如何提升搜索性能,包括索引优化、...
Lucene的核心功能包括索引创建、搜索以及结果排序。其索引机制允许快速地对大量文本数据进行搜索,同时支持多种查询语法,提供丰富的搜索功能。通过使用倒排索引,Lucene可以高效地处理大规模文档的检索需求。 二、...
如果需要自定义排序规则,可以实现Comparator接口,对结果进行二次排序。 分页查询是大型数据集检索中常见的需求。通过设置TopDocs的skipTo()方法和setMaxDocs()方法,我们可以轻松实现分页。同时,Lucene还提供了...
4. **结果处理** - 处理返回的搜索结果,如排序、过滤和展示。 在Java中,Lucene提供了`Analyzer`类用于分词,`IndexWriter`类用于写入索引,`Directory`类用于存储索引,`QueryParser`类用于解析查询字符串,以及`...
Lucene的灵活性允许开发者对其进行深入定制,如简化查询分析器、实现删除功能、自定义排序逻辑以及扩展应用接口。这些高级特性让Lucene能适应更广泛的需求场景。 **学习Lucene的收获** 学习Lucene不仅可以掌握全文...
它提供了一套完整的文本搜索功能,包括索引创建、文档存储、查询解析、排序等。在大数据时代,Lucene因其高效、灵活的特性而被广泛应用于企业级搜索解决方案中。 #### 二、Lucene 4.3.0的安装 1. **下载**:首先从...
- 排序和评分:理解如何根据相关性对结果进行排序,以及如何自定义评分函数。 - 前向索引与倒排索引:了解这两种索引结构及其在搜索中的作用。 - 查询优化:如何通过缓存、近似搜索等手段提高查询性能。 通过这个...
8. **代码友好**:Luck 作为一款开源工具,其源代码可供学习,开发者可以通过查看代码了解 Lucene 内部工作原理,甚至可以根据需要自定义功能。 在使用 Luck-6.5.0 压缩包时,你需要解压文件得到 "luke-6.5.0" ...
它提供了丰富的功能,包括文档索引、搜索、排序等。在Lucene中,为了提高搜索效率和精度,我们需要对输入的文本进行分词处理。而`TokenFilter`是Lucene中用于分词过滤的一个关键组件,它允许我们在分词过程中对原始...
Lucene的核心功能包括文本分析、索引创建、查询解析以及搜索结果的排序。在本文中,我们将深入探讨Lucene 3.0版本的原理与代码分析。 ### 1. 文本分析 Lucene的文本分析过程是将输入的文本转换为可搜索的索引项。...
4. **排序与评分**:学习如何根据文档的相关性(由`ScoreDoc`表示)进行排序,以及自定义排序策略。 5. **过滤与高亮**:使用`Filter`来限制搜索结果,以及`Highlighter`来突出显示匹配的搜索词。 6. **多字段搜索...
Lucene包含了从文本分析到结果排序的一系列功能,使得开发者无需从头开始构建复杂的搜索逻辑。 首先,要构建日志搜索系统,我们需要对日志数据进行预处理。这包括读取日志文件、解析日志格式(如JSON、CSV或自定义...