MongoDB: 如何将数组的 $size 与另一个文档项进行比较?

4

MongoDB:如何在mongo控制台和通过JavaScript、Node.js实现类似以下操作:

db.turnys.find( { users:{$size:seats } } )

turnydb.turnys的样子是这样的:

[
  {
    "gId": "5335e4a7b8cf51bcd054b423",
    "seats": 2,
    "start": "2014-03-30T14:23:29.688Z",
    "end": "2014-03-30T14:25:29.688Z",
    "rMin": 800,
    "rMax": 900,
    "users": [],
    "_id": "533828e1d1a483b7fd8a707a"
  },
  {
    "gId": "5335e4a7b8cf51bcd054b423",
    "seats": 2,
    "start": "2014-03-30T14:23:29.688Z",
    "end": "2014-03-30T14:25:29.688Z",
    "rMin": 900,
    "rMax": 1000,
    "users": [],
    "_id": "533828e1d1a483b7fd8a707b"
  },
...
]
5个回答

2
虽然您可以像其他人建议的那样使用$where来进行跨字段比较,但如果可能的话,我不建议这样做:

此搜索无法使用索引。 Mongo将扫描每个文档,并针对每个文档执行JavaScript表达式来执行跨字段比较。 这将是一项昂贵且缓慢的操作。

在即将推出的MongoDB(2.6)版本中,您将能够在聚合中使用$size 运算符来比较两个字段。

但是,为了获得最佳性能,我建议您存储一个物化的布尔变量,该变量将存储users数组中的元素数量是否等于存储在seats中的数量。 如果您不想获取整个数组以获取计数并针对字段“座位”进行比较,则可以使用$inc(正和负)轻松存储当前用户数量。

如果必须使用$where,我建议您尝试进行一些过滤(如果可能),这可能有助于避免完整的表扫描。 也许是日期范围,或检查users数组是否为空等。

2
使用$where运算符在任何情况下都存在真正的问题,特别是考虑到你所提出的一些“后来”的问题。你需要阅读这个运算符的手册页面(在链接中给出)。
简要概括如下:
  • 运算符$where调用完整的集合扫描以进行比较。这在“现实世界”中是不好的,因为没有任何方法可以将此搜索“约束”为以任何方式使用索引,从而减少工作集。

  • 本质上,这个操作符会调用“任意JavaScript执行”。这意味着两件事情。1.) 每个文档都必须从其原生BSON形式展开为“JavaScript”对象形式(以成本)进行每个比较。2.) 你正在调用一个“JavaScript”解释器,它不是本地代码,对于每个和每个比较都是如此。

其中之一的答案给了您有效的警告,但没有提供实际答案。所以这里是:

正如所述,如果这对您很重要,您实际上应该在文档中保持“数组”元素的“计数”。但尽管您可能认为这是有效的,但适当的比较如下所示。
因此,假设您确实像建议的那样使用了$inc来保持文档中的total_users字段。
db.collection.aggregate([
    { "$project": {
        "gId": 1,
        "alloc": { "$eq": [ "$total_users", "$seats" ] }
    }},
    { "$match": { "alloc": 1 } }
])

在文档中,您可以使用$project将所有所需的字段进行投影,只要像“alloc”字段一样使用“逻辑”比较,并使用$eq运算符进行比较即可。

在未来的版本中(编写时非常快),您甚至可以在“聚合”操作中找到数组的$size。因此,您可以执行以下操作,而无需在“数组”成员上保持计数器:

db.collection.aggregate([
    { "$project": {
        "gId": 1,
        "alloc": { "$eq": [ { "$size": "$users" }, "$seats" ] }
    }},
    { "$match": { "alloc": 1 } }
])

当然,这将会有一个“代价”,所以仍然最好的选择是根据您的需求在文档中保留该“计数器”。虽然这两种方法都仍然会导致“集合扫描”(在这种情况下,由于没有其他$match条件),但最终结果比其他答案中显示的“JavaScript”执行快得多。使用“本地代码”方法作为最佳实践解决方案。

2
您可以使用 $where 查询:
db.turnys.find({ $where: "this.users.length == this.seats" })

1
你可以使用$where运算符,其参数可以是包含JS表达式的字符串或直接使用JavaScript函数:
> db.turnys.find(function() { return this.users.length == this.seats })

1
你可以使用以下任意一种方式。
db.flight.find({"$where":"function() { return this.users.length == this.seats}" })

或者

db.turnys.find({ $where: "this.users.length == this.seats" })

或者

db.turnys.find(function() { return this.users.length == this.seats })

这些都能满足你的目的,但是要注意效率和"$where"查询不应该被使用,除非必须:它们比常规查询慢得多,因此尽量避免使用带有"$where"的查询。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接