背景:
最近开始做mongoDB,公司原有的mysql数据库需要在短期内转库为mongoDB,在重构代码过程中,旧有的一些业务逻辑就需要改动了。比较棘手的,就是地理位置数据做排序,这一点上,我打算用mongoDB的geoNear经纬度查询功能来实现。
经过:
在mongoDB中,我写了一段nosql,可以按输入的定点位置以经纬度排序:
db.getCollection("projectInternalUseData").aggregate({
$geoNear: {
near: [100.0, 50.0],
distanceField: "calculated",
spherical: true
}
})
上诉代码能正常运行,需要几个条件:
1. projectInternalUseData集合必须有一个 2d 经纬索引
2.该索引的字段,必须存放一个数组作为坐标:[ x , y ]
建立索引语句如下:
db.getCollection("projectInternalUseData").createIndex({ "trueCoordinates" , "2dsphere" });
旧版本(3.4以下)是用 ensureIndex 函数的
经过上述测试,nosql查询无误,数据没问题,下一步,在java中实现查询,
这里用的是spring-data-mongodb,有关maven配置就不赘述了
代码如下:
List<DBObject> list = new LinkedList<>();
List<DBObject> pipeLine = new ArrayList<>();
BasicDBObject aggregate = new BasicDBObject("$geoNear",
new BasicDBObject("near",new double[]{point.getX(), point.getY()})
.append("distanceField","calculated")
//.append("query", new BasicDBObject())
//.append("maxDistance", 5000)
.append("spherical",true)
);
pipeLine.add(aggregate);
Cursor cursor = mongoTemplate.getCollection("projectInternalUseData")
.aggregate(pipeLine, AggregationOptions.builder().build());
while (cursor.hasNext()) {
list.add(cursor.next());
}
System.out.println("查询结果:" + list.size());
for ( DBObject obj : list ){
System.out.println( obj );
}
运行测试,报出错误:“The 'cursor' option is required, except for aggregate with the explain argument ”
解决:
英语能力有限,简单地翻译,这个异常提示是:选项光标不可缺失,除非aggregete带上了解释参数。
于是,我先百度,谷歌,看看其他博客的方案:
1. 提升版本,将mongoDB版本回退到 3.2 < 已回退过,没用,排除 >
2. 需要用Cursor来接收结果,不能像find函数那样直接得到List集合 < 我的代码就是用Cursor接收的,没用 >
既然以上方法都没用,那就关注一个点:Cursor是个什么东西,一定是这个东西导致异常
查询mongoDB的API,有如下结果:
按上面的意思,cursor是用来接收document返回结果的,按理说,我的代码是没问题的。
但是,既然这是从3.6之后更变过,那就说明,在此之前,原本document是可以封装成List集合来得到结果的,
从那之后,需要用cursor来接收,而现阶段,find函数依旧保留List集合的类型返回,那就说明,aggregate( pipeline,options) 方法执行时有问题,进入该方法,并设置断点:
debug模式开启后,断点最终定在上图541行代码,
也就是this.executor.execute( operation, readPreference );
而能涉及到Cursor的,就在540这一行代码:
AggregateOperation<DBObject> operation = (
new AggregateOperation(
this.getNamespace(), stages, this.getDefaultDBObjectCodec()
)
).readConcern( this.getReadConcern() ).
maxTime( options.getMaxTime( TimeUnit.MILLISECONDS ),TimeUnit.MILLISECONDS ).
allowDiskUse( options.getAllowDiskUse() ).
batchSize( options.getBatchSize() ).
useCursor( options.getOutputMode() == OutputMode.CURSOR );
operation的初始化,伴随着一大串options参数对象的链式追加,每一个追加方法,都返回了一个operation对象。
在最后一个 .useCursor( true / false ); 方法中,终于看到了 CURSOR字样,
并且,由options.getOutputMode() 方法名可以得出:参数对象 options可以切换输出输出模式
默认的模式,是List集合输出,当输出模式转换为 OutjputMode.CURSOR时,意味着此时才能用Cursor对象来接收结果。
改正:
那么,对应aggregate( List<?> pipeLine , AggregationOptions options, ReadReference readReference )方法,
只需要在传入options参数前,给该参数设置好输出模式为CURSOR就可以了:
执行结果:
异常解决