在移动开发中越来越多的App都有周边搜索,有找附近的人的,附近的酒店,附近的餐馆的。。越来越多的人和企业都使用位置感知的搜索服务。创建位置感知搜索服务通常属于昂贵的专用解决方案的一部分,并且一般由地理空间专家完成。。本文实现了搜索服务器solr的地理感知这批数据。
构建一个常见的业务场景:
搜索周边5KM以内的宾馆,且按照距离排序。
利用Solr来实现空间搜索,我总结了3种方式。
本人使用为版本为solr4.10。采用solr自带的 空间查询类型,配置schema.xml
/**
* 空间搜索
*/
if(!isEmpty(map, "longitude")&&!isEmpty(map, "latitude")){
//--由于 于空格形式,所以必须设置到queryBean中,让pt 为主查询,让之后所有空间搜索字段 为过滤查询
//注:pt 不影响查询
String job_coordinate=map.get("longitude").toString().trim()+" "+map.get("latitude").toString().trim();
queryBean.setParam("pt", job_coordinate);//当前经纬度
//默认为100公里以内
if(isEmpty(map, "distance")){
map.put("distance", "100");
}
query+=" {!geofilt score=distance sfield=job_coordinate d="+map.get("distance")+"} ";//加入 过滤器
}
相关测试代码:
public static void queryTest(String solrServer)throws Exception {
HttpSolrServer server = IndexerUtil.getHttpSolrServer(solrServer);
SolrQuery params=new SolrQuery();
params.setParam("q", "*:*");
// params.put("fq", "{!geofilt}");//距离过滤函数
params.setParam("pt", "116.425397 40.037791");//当前经纬度
// params.put("sfield", "job_coordinate");//经纬度的字段
// params.put("d", "100");//就近2公里的所有酒店
// params.setParam("fq", "{!geofilt score=distance sfield=job_coordinate d=5}");
// params.setParam("sort", "score desc");//根据距离排序
params.setParam("start", "0");//记录开始位置
params.setParam("rows", "10");//查询的行数
String query=" {!geofilt score=distance sfield=job_coordinate d=5 } ";
params.addFilterQuery(query);
QueryResponse resp = server.query(params);
SolrDocumentList docs = resp.getResults();
for(int i=0;i
排序规则:
1) 按距离排序,距离越近排名越高,加上score=distance,其中distance是索引点到坐标点之间的弧度值,系统根据弧度值排序。
&fl=*,score&sort=score asc&q={!geofilt score=distance sfield=poi_location_p pt=54.729696,-98.525391 d=10}。
2) 按距离排序,距离越远排名越高,加上score=reciDistance,其中reciDistance 范围是0~1 采用倒数的方式计算,故与distance的排序刚好相反
&fl=*,score&sort=score asc&q={!geofilt score=reciDistance sfield=poi_location_p pt=54.729696,-98.525391 d=10}
3) 距离仅作排序不做过滤,在条件中设置filter=false,其中d只是确定形状的作用,不起限制作用。
&fl=*,score&sort=score asc&q={!geofilt score=distance filter=false sfield=poi_location_p pt=54.729696,-98.525391 d=10}
4) 结合关键词查询和距离排序,此时关键字只能作为过滤条件(fq)不能作为查询条件,仅作为过滤域。
&fl=*,score& fq=store_name:农业&sort=score asc&q={!geofilt score=distance sfield=poi_location_p pt=54.729696,-98.525391 d=10}
5) 当关键字和距离都作为排序条件时,可以用qf参数设置权重
&fl=*,score& fq=store_name:农业&sort=score asc&q={!geofilt score=distance sfield=poi_location_p pt=54.729696,-98.525391
方式二:引入spatial4j/jts实现的:
SpatialContext: 用于获得距离计算Calculator以及解析形状等。其属于spatial4j包中,该包中还有各种Shape及判断各Shape间的
相交情况。JtsSpatialContext(jts包)用于处理多边形等情况。
引入该包的spatialContextFactory, spatialContextFactory="com.spatial4j.core.context.jts.JtsSpatialContextFactory"
方式三:采用LatLonType去计算 注意必须指定动态字段,subFieldSuffix=“_coordinate”,这一个会默认去检索这样结尾的字段。
个人另外用了一种取巧的方式把搜索词keyword放在 q中。把空间计算放在 fq过滤查询中。query.setParam("fq", "{!geofilt
score=distancesfield=job_coordinate d=5}");最后用geodist方法计算空间距离,最后安装距离值作排序。。感觉很nice。