The 'cursor' option is required, except for aggregate with the explain argument 错误处理

在将mysql转为mongoDB的过程中遇到geoNear查询问题,报错'The 'cursor' option is required...'。通过分析发现,从3.6版本后aggregate查询需使用Cursor接收结果。解决方案是在调用aggregate方法时设置AggregationOptions的输出模式为Cursor。

背景:

最近开始做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就可以了:

执行结果:

异常解决

“ERROR: FOR UPDATE is not allowed with aggregate functions” 这个错误通常在数据库操作中出现,具体是当在使用 `FOR UPDATE` 子句的同时使用了聚合函数。 ### 错误原因 `FOR UPDATE` 子句用于在事务中锁定所选的行,防止其他事务对这些行进行修改,直到当前事务结束。而聚合函数(如 `SUM`、`AVG`、`COUNT` 等)用于对一组行进行计算,返回一个单一的值。二者的使用目的和机制存在冲突: - `FOR UPDATE` 关注的是对具体的行进行锁定,以保证在事务处理过程中这些行的数据不会被其他事务修改。 - 聚合函数是对一组数据进行统计计算,它并不针对具体的某一行,而是对整个分组或者整个结果集进行操作。当在查询中同时使用这两者时,数据库无法确定应该锁定哪些具体的行,因为聚合函数已经将数据进行了汇总处理,所以会抛出此错误。 ### 解决办法 #### 1. 分离查询 将使用聚合函数的查询和使用 `FOR UPDATE` 的查询分开执行。先执行聚合函数查询得到所需的汇总数据,然后再根据这些汇总数据执行带有 `FOR UPDATE` 的查询。 以下是一个示例,假设要对某个表 `sales` 进行操作,原错误查询可能如下: ```sql SELECT SUM(amount) FROM sales WHERE product_id = 1 FOR UPDATE; ``` 分离查询的正确做法: ```sql -- 第一步:执行聚合查询 SELECT SUM(amount) FROM sales WHERE product_id = 1; -- 第二步:根据需要执行带有 FOR UPDATE 的查询 SELECT * FROM sales WHERE product_id = 1 FOR UPDATE; ``` #### 2. 避免使用 `FOR UPDATE` 如果聚合函数的查询不需要锁定行,可以直接去掉 `FOR UPDATE` 子句。例如: ```sql -- 去掉 FOR UPDATE 子句 SELECT SUM(amount) FROM sales WHERE product_id = 1; ``` #### 3. 重新设计查询逻辑 如果确实需要锁定某些行并进行聚合计算,可以考虑使用子查询或者公共表表达式(CTE)来重新设计查询逻辑,确保 `FOR UPDATE` 和聚合函数的使用不冲突。 ```sql -- 使用 CTE 示例 WITH locked_rows AS ( SELECT * FROM sales WHERE product_id = 1 FOR UPDATE ) SELECT SUM(amount) FROM locked_rows; ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值