基于Solr的空间搜索 - 阿凡卢 - 博客园
Solr已经提供了3种filedType来进行空间搜索:
1) LatLonType(用于平面坐标,而不是大地坐标)
2) SpatialRecursivePrefixTreeFieldType(缩写为RPT)
3) BBoxField(用于边界索引查询)
本文重点介绍使用SpatialRecursivePrefixTreeFieldType,不仅可以用点,也可以用于多边形的查询。
1、配置Solr
首先看下数据:
Solr的schema.xml配置:
<field name="station_id" type="long" indexed="true" stored="true" required="true" multiValued="false" /> <field name="station_address" type="text_general" indexed="true" stored="true"/> <field name="station_position" type="location_rpt" indexed="true" stored="true"/> <uniqueKey>station_id</uniqueKey>
这里重点是station_position,它的type是location_rpt,它在Solr中的定义如下:
<!-- A specialized field for geospatial search. If indexed, this fieldType must not be multivalued. --> <fieldType name="location" class="solr.LatLonType" subFieldSuffix="_coordinate"/> <!-- An alternative geospatial field type new to Solr 4. It supports multiValued and polygon shapes. For more information about this and other Spatial fields new to Solr 4, see: http://wiki.apache.org/solr/SolrAdaptersForLuceneSpatial4 --> <fieldType name="location_rpt" class="solr.SpatialRecursivePrefixTreeFieldType" geo="true" distErrPct="0.025" maxDistErr="0.000009" units="degrees" /> <!-- Spatial rectangle (bounding box) field. It supports most spatial predicates, and has special relevancy modes: score=overlapRatio|area|area2D (local-param to the query). DocValues is required for relevancy. --> <fieldType name="bbox" class="solr.BBoxField" geo="true" units="degrees" numberType="_bbox_coord" /> <fieldType name="_bbox_coord" class="solr.TrieDoubleField" precisionStep="8" docValues="true" stored="false"/>
对solr.SpatialRecursivePrefixTreeFieldType的配置说明:
SpatialRecursivePrefixTreeFieldType
用于深度遍历前缀树的FieldType,主要用于获得基于Lucene中的RecursivePrefixTreeStrategy。
geo
默认为true,值为true的情况下坐标基于球面坐标系,采用Geohash的方式;值为false的情况下坐标基于2D平面的坐标系,采用Euclidean/Cartesian的方式。
distErrPct
定义非Point图形的精度,范围在0-0.5之间。该值决定了非Point的图形索引或查询时的level(如geohash模式时就是geohash编码的长度)。当为0时取maxLevels,即精度最大,精度越大将花费更多的空间和时间去建索引。
maxDistErr/maxLevels:maxDistErr
定义了索引数据的最高层maxLevels,上述定义为0.000009,根据GeohashUtils.lookupHashLenForWidthHeight(0.000009, 0.000009)算出编码长度为11位,精度在1米左右,直接决定了Point索引的term数。maxLevels优先级高于maxDistErr,即有maxLevels的话maxDistErr失效。详见SpatialPrefixTreeFactory.init()方法。不过一般使用maxDistErr。
units
单位是degrees。
worldBounds
世界坐标值:”minX minY maxX maxY”。 geo=true即geohash模式时,该值默认为”-180 -90 180 90”。geo=false即quad时,该值为Java double类型的正负边界,此时需要指定该值,设置成”-180 -90 180 90”。
2、建立索引
这里使用Solrj来建立索引:
//Index some base station data for test public void IndexBaseStation(){ BaseStationDb baseStationDb = new BaseStationDb(); List<BaseStation> stations = baseStationDb.getAllBaseStations(); Collection<SolrInputDocument> docList = new ArrayList<SolrInputDocument>(); for (BaseStation baseStation : stations) { //添加基站数据到Solr索引中 SolrInputDocument doc = new SolrInputDocument(); doc.addField("station_id", baseStation.getBaseStationId()); doc.addField("station_address", baseStation.getAddress()); String posString = baseStation.getLongitude()+" "+baseStation.getLatitude() ; doc.addField("station_position", posString); docList.add(doc); } try { server.add(docList); server.commit(); } catch (SolrServerException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } System.out.println("Index base station data done!"); }
这里使用“经度 纬度”这样的字符串格式将经纬度索引到station_position字段中。
3、查询
查询语法示例:
q={!geofilt pt=45.15,-93.85 sfield=poi_location_p d=5 score=distance}
q={!bbox pt=45.15,-93.85 sfield=poi_location_p d=5 score=distance}
q=poi_location_p:"Intersects(-74.093 41.042 -69.347 44.558)" //a bounding box (not in WKT)
q=poi_location_p:"Intersects(POLYGON((-10 30, -40 40, -10 -20, 40 20, 0 0, -10 30)))" //a WKT example
涉及到的字段说明:
字段 |
含义 |
q |
查询条件,如 q=poi_id:134567 |
fq |
过滤条件,如 fq=store_name:农业 |
fl |
返回字段,如fl=poi_id,store_name |
pt |
坐标点,如pt=54.729696,-98.525391 |
d |
搜索半径,如 d=10表示10km范围内 |
sfield |
指定坐标索引字段,如sfield=geo |
defType |
指定查询类型可以取 dismax和edismax,edismax支持boost函数相乘作用,dismax是通过累加方式计算最后的score. |
qf |
指定权重字段:qf=store_name^10+poi_location_p^5 |
score |
排序字段根据qf定义的字段defType定义的方式计算得到score排序输出 |
其中有几种常见的Solr支持的几何操作:
WITHIN:在内部
CONTAINS:包含关系
DISJOINT:不相交
Intersects:相交(存在交集)
1)点查询
测试代码:查询距离某个点pt距离为d的集合
SolrQuery params = new SolrQuery(); params.set("q", "*:*"); params.set("fq", "{!geofilt}"); //距离过滤函数 params.set("pt", "118.227985 39.410722"); //当前经纬度 params.set("sfield", "station_position"); //经纬度的字段 params.set("d", "50"); //就近 d km的所有数据 //params.set("score", "kilometers"); params.set("sort", "geodist() asc"); //根据距离排序:由近到远 params.set("start", "0"); //记录开始位置 params.set("rows", "100"); //查询的行数 params.set("fl", "*,_dist_:geodist(),score");//查询的结果中添加距离和score