天天看点

spring java mongodb geo 位置搜索服务 示例

1、配置 pom.xml,在原有的spring项目中添加以下2个即可。

<!-- mongo db 驱动-->
		<dependency>
		    <groupId>org.mongodb</groupId>
		    <artifactId>mongo-java-driver</artifactId>
		    <version>3.2.2</version>
		</dependency>
		
		<!-- spring-data-mongodb -->
		<dependency>
		    <groupId>org.springframework.data</groupId>
		    <artifactId>spring-data-mongodb</artifactId>
		    <version>1.9.2.RELEASE</version>
		</dependency>
           

2、配置 spring-data-mongo.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
	xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:mongo="http://www.springframework.org/schema/data/mongo"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
 	http://www.springframework.org/schema/data/mongo
    http://www.springframework.org/schema/data/mongo/spring-mongo-1.0.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">

	<!-- Default bean name is 'mongo' -->
	<mongo:mongo host="localhost" port="27017"/>
	
	<!-- Offers convenience methods and automatic mapping between MongoDB JSON documents and your domain classes. -->
  	<bean id="mongoTemplate" class="org.springframework.data.mongodb.core.MongoTemplate">
  			<constructor-arg ref="mongo"/>
		    <constructor-arg name="databaseName" value="test"/>
  	</bean>
</beans>
           

3、mongodb 父接口类

package com.zjp.cache;

import java.util.List;

import com.mongodb.DBObject;

/**
 * mongodb 父接口类
 * @author babylon
 * @version 1.1
 * @date 2016年7月12日-下午1:22:23
 */
public interface  MongoDao {
	
    public DBObject findOne(String collection, DBObject query, DBObject fields);
	 
    public List<DBObject> find(String collection, DBObject query, DBObject fields, DBObject orderBy, int pageNum, int pageSize);
 
    public List<DBObject> find(String collection, DBObject query, DBObject fields, DBObject orderBy, int limit);
 
    public void delete(String collection, DBObject dbObject);
 
    public void save(String collection, DBObject dbObject);
 
    public void update(String collection, DBObject query, DBObject update, boolean upsert, boolean multi);
 
    public Long count(String collection, DBObject query);
 
    public List<?> distinct(String collection, String key, DBObject query);
 
}
           

4、Mondodb 地址运算 接口类

package com.zjp.cache;
 
import com.mongodb.DBObject;
 
import java.util.List;
 
/**
 * Mondodb 地址运算 接口类
 *
 * @author babylon
 * @version 1.1
 * @date 2016年7月12日-下午1:42:06
 */
public interface MongoGeoDao extends MongoDao{
	
           
/**
     * 聚合查询,查询一个点附近的点,并返回每一个点到该中心点的距离,在坐标表分片的情况下$nearSphere不支持,
     * 可以使用该方法进行查询
     * @param collection    集合名称
     * @param query         查询条件
     * @param point         中心点坐标
     * @param limit         返回记录数量限制
     * @param maxDistance   最大距离
     * @return              非NULL的list
     */
    public List<DBObject> geoNear(String collection, DBObject query, Point point,int limit, long maxDistance) ;
	
    /**
     * 查询在圆形区域内的坐标点,需要指定中心点坐标和半径,半径单位是米
     *
     * @param collection    集合名称
     * @param locationField 坐标字段
     * @param center        中心点坐标[经度,纬度]
     * @param radius        半径 单位:米
     * @param fields        查询字段
     * @param query         查询条件
     * @param limit         返回记录限制数量
     * @return              非NULL的list
     */
    public List<DBObject> withinCircle(String collection,String locationField, Point center, long radius,
                                       DBObject fields, DBObject query, int limit);
 
    /**
     * 指定一个点,返回该点附近的坐标点且是由近到远,$nearSphere 需要建立索引2dsphere 或者2d,并且支持GeoJSON和一般坐标对
     * 注意: $nearSphere在分片的集群中无效,使用geoNear
     *
     * @param collection    集合名称
     * @param locationField 坐标字段
     * @param center        中心点坐标[经度,纬度]
     * @param minDistance   最近距离
     * @param maxDistance   最远距离
     * @param query         查询条件
     * @param fields        查询字段
     * @param limit         返回记录限制数量
     * @return              非NULL的list
     */
    public List<DBObject> nearSphere(String collection, String locationField, Point center, long minDistance, long maxDistance, DBObject query, DBObject fields, int limit);
 
 
    /**
     * 查询位于指定一个封闭多边形内的所有坐标点,给定的多边形坐标点必须首位相接形成封闭的多边形
     * 如三角形
     *       final LinkedList<double[]> polygon = new LinkedList<>();
     *       polygon.addLast(new double[] {  121.36, 31.18 });
     *       polygon.addLast(new double[] {  121.35, 31.36 });
     *       polygon.addLast(new double[] {  121.39, 31.17 });
     *       polygon.addLast(new double[] {  121.36, 31.18 });
     *
     * MongoDB将多边形的边界也作为查询形状的一部分
     * @param collection    集合名称
     * @param locationField 坐标字段
     * @param polygon       多边形坐标
     * @param fields        查询字段
     * @param query         查询条件
     * @param limit         返回记录限制数量
     * @return              非NULL的list
     */
    public List<DBObject> withinPolygon(String collection,String locationField,
                                        List<double[]> polygon,DBObject fields,DBObject query,int limit);
 
 
    /**
     * 查询位于指定多个封闭多边形内的所有坐标点,给定的多边形坐标点必须首位相接形成封闭的多边形
     * @param collection    集合名称
     * @param locationField 坐标字段
     * @param polygons      多边形坐标 数组
     * @param fields        查询字段
     * @param query         查询条件
     * @param limit         返回记录限制数量
     * @return              非NULL的list
     */
    public List<DBObject> withinMultiPolygon(String collection,String locationField,
                                        List<List<double[]>> polygons,DBObject fields,DBObject query,int limit);
 
 
    /**
     * 在矩形区域内查找坐标点,该方法仅仅在2d索引是支持,在2dsphere中不支持
     * @param collection    集合名称
     * @param locationField 坐标字段
     * @param bottomLeft    左下角
     * @param upperRight    右上角
     * @param fields        查询字段
     * @param query         查询条件
     * @param limit         返回记录限制数量
     * @return              非NULL的list
     */
    @Deprecated
    public List<DBObject> withinBox(String collection, String locationField,
                                    Point bottomLeft, Point upperRight, DBObject fields, DBObject query,int limit);
 
}
           

5、mongodb 服务实现类

package com.zjp.service.impl;
 
import com.mongodb.AggregationOptions;
import com.mongodb.BasicDBObject;
import com.mongodb.Cursor;
import com.mongodb.DBObject;
import com.zjp.cache.MongoGeoDao;
import com.zjp.cache.Point;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
 
/**
 * mongodb 服务实现类
 *
 * @author babylon
 * @version 1.1
 * @date 2016年7月12日-下午1:36:50
 */
@Repository
public class MongoDaoImpl implements MongoGeoDao {
	
    private static Logger logger = LoggerFactory.getLogger(MongoDaoImpl.class);
    
    @Autowired
    private MongoTemplate mongoTemplate;
 
    @Override
    public DBObject findOne(String collection, DBObject query, DBObject fields) {
        return mongoTemplate.getCollection(collection).findOne(query, fields);
    }
 
    @Override
    public List<DBObject> find(String collection, DBObject query, DBObject fields, DBObject orderBy, int pageNum, int pageSize) {
        List<DBObject> list = new ArrayList<>();
        Cursor cursor = mongoTemplate.getCollection(collection).find(query, fields).skip((pageNum - 1) * pageSize).limit(pageSize).sort(orderBy);
        while (cursor.hasNext()) {
            list.add(cursor.next());
        }
        return list.size() > 0 ? list : null;
    }
 
    @Override
    public List<DBObject> find(String collection, DBObject query, DBObject fields, DBObject orderBy, int limit) {
        List<DBObject> list = new ArrayList<>();
        Cursor cursor = mongoTemplate.getCollection(collection).find(query, fields).sort(orderBy).limit(limit);
        while (cursor.hasNext()) {
            list.add(cursor.next());
        }
        return list.size() > 0 ? list : null;
    }
 
    @Override
    public void delete(String collection, DBObject dbObject) {
        mongoTemplate.getCollection(collection).remove(dbObject);
    }
 
    @Override
    public void save(String collection, DBObject dbObject) {
        mongoTemplate.getCollection(collection).save(dbObject);
    }
 
    @Override
    public void update(String collection, DBObject query, DBObject update, boolean upsert, boolean multi) {
        mongoTemplate.getCollection(collection).update(query, update, upsert, multi);
    }
 
    @Override
    public Long count(String collection, DBObject query) {
        return mongoTemplate.getCollection(collection).count(query);
    }
 
	@Override
    public List<?> distinct(String collection, String key, DBObject query) {
        return mongoTemplate.getCollection(collection).distinct(key, query);
    }
 
    @Override
    public List<DBObject> geoNear(String collection, DBObject query, Point point, int limit, long maxDistance) {
        if(query==null)
            query = new BasicDBObject();
 
        List<DBObject> pipeLine = new ArrayList<>();
        BasicDBObject aggregate = new BasicDBObject("$geoNear",
	    		new BasicDBObject("near",new BasicDBObject("type","Point").append("coordinates",new double[]{118.783799, 31.979234}))
				        .append("distanceField","dist.calculated")
				        .append("query", new BasicDBObject())
				        .append("num", 5)
				        .append("maxDistance", 5000)
				        .append("spherical",true)
	    		);
        pipeLine.add(aggregate);
        Cursor cursor=mongoTemplate.getCollection(collection).aggregate(pipeLine, AggregationOptions.builder().build());
        List<DBObject> list = new LinkedList<>();
        while (cursor.hasNext()) {
            list.add(cursor.next());
        }
        return list;
    }
 
    @Override
    public List<DBObject> withinCircle(String collection,String locationField, Point center,
                                       long radius, DBObject fields, DBObject query, int limit) {
        LinkedList<Object> circle = new LinkedList<>();
        //Set the center coordinate
        circle.addLast(new double[]{center.getLng(),center.getLat()});
        //Set the radius. unit:meter
        circle.addLast(radius/6378137.0);
 
        if(query==null)
            query = new BasicDBObject();
        query.put(locationField, new BasicDBObject("$geoWithin", new BasicDBObject("$centerSphere", circle)));
        logger.info("withinCircle:{}",query.toString());
        return mongoTemplate.getCollection(collection).find(query, fields).limit(limit).toArray();
    }
 
    @Override
    public List<DBObject> nearSphere(String collection, String locationField, Point center,
                                     long minDistance, long maxDistance, DBObject query, DBObject fields, int limit) {
        if(query==null)
            query = new BasicDBObject();
 
        query.put(locationField,
                new BasicDBObject("$nearSphere",
                    new BasicDBObject("$geometry",
                            new BasicDBObject("type","Point")
                                    .append("coordinates",new double[]{center.getLng(),center.getLat()}))
                            .append("$minDistance",minDistance)
                            .append("$maxDistance",maxDistance)
        ));
        logger.info("nearSphere:{}",query.toString());
        return mongoTemplate.getCollection(collection).find(query, fields).limit(limit).toArray();
    }
 
    @Override
    public List<DBObject> withinPolygon(String collection, String locationField,
                                        List<double[]> polygon, DBObject fields, DBObject query, int limit) {
        if(query==null)
            query = new BasicDBObject();
 
        List<List<double[]>> polygons = new LinkedList<>();
        polygons.add(polygon);
        query.put(locationField, new BasicDBObject("$geoWithin",
                new BasicDBObject("$geometry",
                        new BasicDBObject("type","Polygon")
                        .append("coordinates",polygons))));
        logger.info("withinPolygon:{}",query.toString());
        return mongoTemplate.getCollection(collection).find(query, fields).limit(limit).toArray();
    }
 
    @Override
    public List<DBObject> withinMultiPolygon(String collection, String locationField, List<List<double[]>> polygons, DBObject fields, DBObject query, int limit) {
        if(query==null)
            query = new BasicDBObject();
 
        List<List<List<double[]>>> list = new LinkedList<>();
        for (List<double[]> polygon : polygons) {
            List<List<double[]>> temp = new LinkedList<>();
            temp.add(polygon);
            list.add(temp);
        }
        query.put(locationField, new BasicDBObject("$geoWithin",
                new BasicDBObject("$geometry",
                        new BasicDBObject("type","MultiPolygon")
                                .append("coordinates",list))));
        logger.info("withinMultiPolygon:{}",query.toString());
        return mongoTemplate.getCollection(collection).find(query, fields).limit(limit).toArray();
    }
 
    @Override
    public List<DBObject> withinBox(String collection, String locationField, Point bottomLeft, Point upperRight, DBObject fields, DBObject query, int limit) {
        if(query==null)
            query = new BasicDBObject();
 
        LinkedList<double[]> box = new LinkedList<>();
        box.add(new double[]{bottomLeft.getLng(), bottomLeft.getLat()});
        box.add(new double[]{upperRight.getLng(), upperRight.getLat()});
 
        query.put(locationField, new BasicDBObject("$geoWithin", new BasicDBObject("$box", box)));
        logger.info("withinBox:{}",query.toString());
        return mongoTemplate.getCollection(collection).find(query, fields).limit(limit).toArray();
    }
 
}
           

6、添加数据

db.point.test.insert({"address" : "南京 禄口国际机场","loc" : { "type": "Point", "coordinates": [118.783799,31.979234]}})  
db.point.test.insert({"address" : "南京 浦口公园","loc" : { "type": "Point", "coordinates": [118.639523,32.070078]}})  
db.point.test.insert({"address" : "南京 火车站","loc" : { "type": "Point", "coordinates": [118.803032,32.09248]}})  
db.point.test.insert({"address" : "南京 新街口","loc" : { "type": "Point", "coordinates": [118.790611,32.047616]}})  
db.point.test.insert({"address" : "南京 张府园","loc" : { "type": "Point", "coordinates": [118.790427,32.03722]}})  
db.point.test.insert({"address" : "南京 三山街","loc" : { "type": "Point", "coordinates": [118.788135,32.029064]}})  
db.point.test.insert({"address" : "南京 中华门","loc" : { "type": "Point", "coordinates": [118.781161,32.013023]}})  
db.point.test.insert({"address" : "南京 安德门","loc" : { "type": "Point", "coordinates": [118.768964,31.99646]}})
           

7、必须加索引 (为什么:https://docs.mongodb.com/manual/reference/operator/aggregation/geoNear/)

db.point.test.ensureIndex( { loc : "2dsphere" } )  
           

8、调用实例

@Autowired
private MongoDaoImpl mongoDao;			
           
DBObject query = new BasicDBObject();
Point point = new Point();
point.setLng(118.783799);
point.setLat(31.979234);
int limit = 5;
Long maxDistance = 5000L; // 米
List<DBObject> list = mongoDao.geoNear("point.test", query, point, limit, maxDistance);
for(DBObject obj : list)
   System.out.println(obj);