Solr 地理空间搜索实践(Spatial Search)

在移动开发中越来越多的App都有周边搜索,有找附近的人的,附近的酒店,附近的餐馆的。。越来越多的人和企业都使用位置感知的搜索服务。创建位置感知搜索服务通常属于昂贵的专用解决方案的一部分,并且一般由地理空间专家完成。。本文实现了搜索服务器solr的地理感知这批数据。

构建一个常见的业务场景:

搜索周边5KM以内的宾馆,且按照距离排序。


利用Solr来实现空间搜索,我总结了3种方式

本人使用为版本为solr4.10。采用solr自带的 空间查询类型,配置schema.xml

<fieldType name="location_rpt" class="solr.SpatialRecursivePrefixTreeFieldType" geo="true" distErrPct="0.025" maxDistErr="0.000009" units="degrees" />
 
<field name="job_coordinate" type="location_rpt" indexed="true" stored="true" omitNorms="true"  multiValued="false" required="false" />

				/**
				 * 空间搜索
				 */
				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<docs.size();i++){  
	        	SolrDocument sid=docs.get(i);
	            System.out.println(sid);  
	        }  
	    } 


排序规则:

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"

   <fieldType name="location_rpt" class="solr.SpatialRecursivePrefixTreeFieldType" spatialContextFactory="com.spatial4j.core.context.jts.JtsSpatialContextFactory" distErrPct="0.025" maxDistErr="0.000009" units="degrees"/>

方式三:采用LatLonType去计算 注意必须指定动态字段,subFieldSuffix=“_coordinate”,这一个会默认去检索这样结尾的字段。

<fieldType name="location" class="solr.LatLonType" subFieldSuffix="_coordinate"/>
<dynamicField name="*_coordinate" type="double" indexed="true" stored="true"/><!--  默认subFieldSuffix="_coordinate"  必须要指定该坐标类型的动态字段 -->
<field name="job_coordinate" type="location" indexed="true" stored="true"  />

注意:这3种搜索方式 ,都必须把空间函数写在q中,才能返回计算的距离score和排序。

个人另外用了一种取巧的方式把搜索词keyword放在 q中。把空间计算放在 fq过滤查询中。query.setParam("fq", "{!geofilt

score=distancesfield=job_coordinate d=5}");最后用geodist方法计算空间距离,最后安装距离值作排序。。感觉很nice。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值