MongoDB 使用 $and 和切片和匹配

Posted

技术标签:

【中文标题】MongoDB 使用 $and 和切片和匹配【英文标题】:MongoDB using $and with slice and a match 【发布时间】:2018-07-02 15:22:33 【问题描述】:

我正在使用 spring 数据包中的 @Query,我想查询文档中数组的 last 元素。

例如数据结构可能是这样的:


   name : 'John',
   scores: [10, 12, 14, 16]
,

   name : 'Mary',
   scores: [78, 20, 14]
,

所以我建立了一个查询,但是它抱怨 “错误消息 'unknown operator: $slice' on server”

查询的 $slice 部分在单独运行时很好:

db.getCollection('users').find(, scores:  $slice: -1 )

但是,一旦我将它与更复杂的检查结合起来,就会出现上述错误。

db.getCollection('users').find"$and":[  , "scores" :  "$slice" : -1 ,"scores": "16"])

此查询将返回最后得分为 16 的用户列表,在我的示例中将返回 John,但不返回 Mary。

我已将其放入标准的 mongo 查询中(用于调试事物),但理想情况下我需要将其放入 spring-data @query 构造中 - 它们应该非常相似。

有没有办法做到这一点,而不求助于手动 java 调用?除了需要标准查询之外,我没有看到太多关于 @Query 的文档。

正如链接帖子所评论的那样,它指的是聚合,它如何与@Query一起使用,加上主要答案之一使用$where,这效率低下。

【问题讨论】:

MongoDB - Query on the last element of an array?的可能重复 @Veeram 我在那里看不到合适的答案,他们要么使用聚合 - 这在 @Query 指令中如何工作 - 我在文档中看不到任何内容,也是主要答案之一使用$where 又不适合,因为它非常慢。可能有 1000 个条目。 没有@Query 答案。我认为最好的答案是以相反的顺序存储文档,您可以在其中访问具有 .0 索引的顶部文档。像db.getCollection('users').find("scores.0": 16) 这样的东西。如果这不是一个选项,您必须使用 MongoTemplate 来访问聚合方法。 想一想你现在可以使用 3.6 中的 $expr 运算符在查找查询中使用聚合函数。所以像@Query 中的$expr:$gt:[$arrayElemAt:["scores", -1], 16] 这样的东西。您可以在 3.6 shell 中尝试db.getCollection('users').find($expr:$gt:[$arrayElemAt:["‌​‌​scores", -1], 16])。我已经更新了链接的帖子以包含新的答案。在 Spring Query 指令中测试并且它有效!试试@Query("$expr:$gt:[$arrayElemAt:[\"scores\", -1], ?0]") @Veeram 谢谢,我会看看是否可以在我的环境中使用它。 【参考方案1】:

不幸的是,解决问题的一般方法是数据,尽管@Veeram 的响应是正确的,但这意味着您没有命中索引。这是一个问题,您当然拥有非常大的数据集,并且您会看到返回时间不断减少。这是 $where,$arrayElemAt 无法帮助您解决的问题。他们必须对数据进行预处理,这意味着要进行完整的收集扫描。我们使用这些结构分析了几个查询,它们涉及“COLSCAN”。

理想的解决方案是创建一个包含最后一项的字段,例如:


   name : 'John',
   scores: [10, 12, 14, 16],
   lastScore: 16
,

   name : 'Mary',
   scores: [78, 20, 14],
   lastScore: 14

您可以创建一个监听器来维护它,如下所示:

@Component
public class ScoreListener extends AbstractMongoEventListener<Scores>

然后您就可以嗅探数据并进行任何更新:

@Override
public void onBeforeConvert(BeforeConvertEvent<Scores> event) 
      // process any score and set lastScore

别忘了更新你的索引(!):

@CompoundIndex(name = "lastScore", def = ""
        + "'lastScore': 1"
        + " ")

虽然这确实包含数据轻微重复的缺点,但在当前的 Mongo (3.4) 中,这确实是这样做的唯一方法,并且在搜索机制中包含索引。速度差异很大,从近一分钟的响应时间到几毫秒。

在 Mongo 3.6 中可能有更好的方法来做到这一点,但是我们固定在这个版本上,所以这必须是我们的解决方案。

【讨论】:

以上是关于MongoDB 使用 $and 和切片和匹配的主要内容,如果未能解决你的问题,请参考以下文章

mongodb 中 Aggregation 的管道和分片集合( Pipeline and Sharded Collections)

$element 匹配或 $unwind 与 AND/Multiple 条件与 mongoDB 聚合

centos 安装 mongodb

在 mongodb 中使用 $and 和 $match

[PHP]新版的mongodb扩展安装和使用

MongoDB 快速入门