MongoDb near/geonear query with variable distance

心已入冬 提交于 2019-11-30 07:47:15

You won't be able to do this with a normal query as you can't dynamically set the distance per document. As of MongoDB 2.4 you can do this with the aggregation framework as they have added the geoNear operator to the starts of pipelines.

The first stage will be the geoNear which is very similar to the geonear command. We will also get the distance from the point specified (10,10) to the document as a result.

The second stage we will need to use the project operator to ad the different between the maximumDistance field and the computed geoNear distance.

Lastly, we match those documents that have a positive delta ((max - distance) > 0).

Here is the pipeline using the Asynchronous Java Driver's helper classes.

package example;

import static com.allanbank.mongodb.builder.AggregationProjectFields.include;
import static com.allanbank.mongodb.builder.QueryBuilder.where;
import static com.allanbank.mongodb.builder.expression.Expressions.field;
import static com.allanbank.mongodb.builder.expression.Expressions.set;
import static com.allanbank.mongodb.builder.expression.Expressions.subtract;

import com.allanbank.mongodb.bson.element.ArrayElement;
import com.allanbank.mongodb.builder.Aggregate;
import com.allanbank.mongodb.builder.AggregationGeoNear;
import com.allanbank.mongodb.builder.GeoJson;

public class AggregateGeoNear {
    public static void main(String[] args) {
        Aggregate aggregate = Aggregate
                .builder()
                .geoNear(
                        AggregationGeoNear.builder()
                                .location(GeoJson.p(10, 10))
                                .distanceField("distance"))
                .project(
                        include("name", "location", "maximumDistance"),
                        set("delta",
                                subtract(field("maximumDistance"),
                                        field("distance"))))
                .match(where("delta").greaterThanOrEqualTo(0)).build();

        System.out
                .println(new ArrayElement("pipeline", aggregate.getPipeline()));
    }
}

And here is the pipeline is created:

pipeline : [
  {
    '$geoNear' : {
      near : [
        10, 
        10
      ],
      distanceField : 'distance',
      spherical : false,
      uniqueDocs : true
    }
  }, 
  {
    '$project' : {
      name : 1,
      location : 1,
      maximumDistance : 1,
      delta : {
        '$subtract' : [
          '$maximumDistance', 
          '$distance'
        ]
      }
    }
  }, 
  {
    '$match' : {
      delta : { '$gte' : 0 }
    }
  }
]

HTH - Rob.

P.S. The builders above are using a pre-release of the 1.2.0 version of the driver. The code is going through the build matrix as I type and should be released by Friday, March 22, 2013.

regarding how to solve it with Spring Data, I have solve it by building a new GeoNearOperation implementation, since it's mandatory to include the maxDistance field name within the projection:

public class GeoNearOperation2  implements AggregationOperation  {

private NearQuery nearQuery;

public GeoNearOperation2(NearQuery nearQuery) {
    this.nearQuery = nearQuery;
}

public DBObject toDBObject(AggregationOperationContext context) {
    DBObject dbObject = context.getMappedObject(nearQuery.toDBObject());
    dbObject.put("distanceField", "distance");
    return new BasicDBObject("$geoNear",dbObject);
}

}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!