MongoDB按字段聚合存在

Eri*_*nan 40 mongodb aggregation-framework

我很难相信这个问题还没有在某个地方被问过和回答,但是我找不到它的任何痕迹.

我有一个需要按布尔值分组的MongoDB聚合查询:是否存在另一个字段.

例如,让我们从这个集合开始:

> db.test.find()
{ "_id" : ObjectId("53fbede62827b89e4f86c12e"),
  "field" : ObjectId("53fbede62827b89e4f86c12d"), "name" : "Erik" }
{ "_id" : ObjectId("53fbee002827b89e4f86c12f"), "name" : "Erik" }
{ "_id" : ObjectId("53fbee092827b89e4f86c131"),
  "field" : ObjectId("53fbee092827b89e4f86c130"), "name" : "John" }
{ "_id" : ObjectId("53fbee122827b89e4f86c132"), "name" : "Ben" }
Run Code Online (Sandbox Code Playgroud)

2个文件有"字段",2个没有.注意,"field"的每个值可能不同; 我们只想对它的存在进行分组(或者非null也适用于我,我没有存储任何空值).

我已经尝试过使用$ project,但是$ exists不存在,$ cond和$ ifNull没有帮助我.该字段似乎总是存在,即使它不存在:

> db.test.aggregate(
  {$project:{fieldExists:{$cond:[{$eq:["$field", null]}, false, true]}}},
  {$group:{_id:"$fieldExists", count:{$sum:1}}}
)
{ "_id" : true, "count" : 4 }
Run Code Online (Sandbox Code Playgroud)

我希望以下更简单的聚合工作,但由于某种原因,$ exists不支持这种方式:

> db.test.aggregate({$group:{_id:{$exists:"$field"}, count:{$sum:1}}})
assert: command failed: {
  "errmsg" : "exception: invalid operator '$exists'",
  "code" : 15999,
  "ok" : 0
} : aggregate failed
Error: command failed: {
  "errmsg" : "exception: invalid operator '$exists'",
  "code" : 15999,
  "ok" : 0
} : aggregate failed
    at Error (<anonymous>)
    at doassert (src/mongo/shell/assert.js:11:14)
    at Function.assert.commandWorked (src/mongo/shell/assert.js:244:5)
    at DBCollection.aggregate (src/mongo/shell/collection.js:1149:12)
    at (shell):1:9
2014-08-25T19:19:42.344-0700 Error: command failed: {
  "errmsg" : "exception: invalid operator '$exists'",
  "code" : 15999,
  "ok" : 0
} : aggregate failed at src/mongo/shell/assert.js:13
Run Code Online (Sandbox Code Playgroud)

有谁知道如何从这样的集合中获得所需的结果?

预期结果:

{ "_id" : true, "count" : 2 }
{ "_id" : false, "count" : 2 }
Run Code Online (Sandbox Code Playgroud)

kdk*_*eck 66

我昨晚解决了同样的问题,这样:

> db.test.aggregate({$group:{_id:{$gt:["$field", null]}, count:{$sum:1}}})
{ "_id" : true, "count" : 2 }
{ "_id" : false, "count" : 2 }
Run Code Online (Sandbox Code Playgroud)

有关其工作原理的完整说明,请参见http://docs.mongodb.org/manual/reference/bson-types/#bson-types-comparison-order.

  • 这当然很聪明,我没有更好的解决方案,但感觉就像一个黑客。 (8认同)
  • 要检查该值是否不存在或为 null,请使用 `{ $lte: ["$field", null] }` (5认同)
  • 我不知道为什么没有提到这一点,但对我来说 `$match: { var_name: { $exists : true } }` 工作得很好。我还没有测试过这个来对聚合进行分组。 (2认同)

Del*_*con 13

我通过检查未定义来解决它

$ne : [$var_to_check, undefined]
Run Code Online (Sandbox Code Playgroud)

要么

$ne:  [ { $type : "$var_to_check"}, 'missing'] }
Run Code Online (Sandbox Code Playgroud)

如果定义了var,则返回true

  • 我真的推荐第二种方法而不是标记为已回答的方法。其他方法似乎是hacky。https://docs.mongodb.com/manual/reference/operator/aggregation/type/ (2认同)

DSa*_*Sav 11

用于检查字段是否存在且不为空的语义透明解决方案:

{ $ne: [{ $ifNull: ["$field", null] }, null] }
Run Code Online (Sandbox Code Playgroud)

要检查它是否丢失,请替换$ne$eq


Nei*_*unn 9

所述$exists操作员是一个"查询"操作符,所以它被用于基本上为"过滤器"的结果,而不是确定的逻辑条件.

作为"逻辑"运算符,聚合框架支持$ifNull运算符.这将返回它存在的字段值或其未提供或以其他方式求值的备用提供值null

db.test.aggregate([
    { "$group": {
        "_id": { "$ifNull": [ "$field", false ] },
        "count": { "$sum": 1 }
    }}
])
Run Code Online (Sandbox Code Playgroud)

但是,当然,即使这不是一个"真/假"的比较,所以除非你真的想要返回它所在的字段的实际值,那么你可能会更$cond喜欢你的声明:

db.test.aggregate([
    { "$group": {
        "_id": { "$cond": [{ "$eq": [ "$field", null ] }, true, false ] },
        "count": { "$sum": 1 }
    }}
])
Run Code Online (Sandbox Code Playgroud)

在哪里$ifNull可以非常有用的是替换不存在的否则会导致错误使用的数组字段$unwind.然后,您可以执行返回单个元素或空数组的操作,这样就不会在其余的管道处理中导致问题.

  • 实际上,$ eq始终返回false,即使该字段存在.但如果使用$ gt就可以了.使用"_id":{"$ cond":[{"$ gt":["$ field",null]},true,false]} (10认同)
  • 正如我在 OP 中指出的,您的解决方案给出了错误的结果: { "_id" : false, "count" : 4 } 不过感谢您的回答。 (2认同)
  • $ eq不检查字段是否存在.谢谢你@RomanBlachman的$ gt小费 (2认同)

egv*_*gvo 8

简而言之

{'$project': {
    'field_exists': {'$or': [
        {'$eq': ['$field', null]}, 
        {'$gt': ['$field', null]},
    ]},
}}
Run Code Online (Sandbox Code Playgroud)

细节

$exists意味着该字段存在,即使它是null空值或任何其他空值。这就是为什么本页上的所有答案都是不正确的。

让我们测试一下。检查一下:

// Let's take any collection that have docs
db.getCollection('collection').aggregate([
  // Get arbitrary doc, no matter which, we won't use it
  {"$limit": 1},
  // Project our own fields (just create them with $literal)
  {'$project': {
    '_id': 0,
    'null_field': {'$literal': null},
    'not_null_field': {'$literal': {}},
  }},
])
Run Code Online (Sandbox Code Playgroud)

我们会得到这个:

{
    "null_field" : null,
    "not_null_field" : {}
}
Run Code Online (Sandbox Code Playgroud)

然后让我们澄清一下该文档中存在哪些字段:

  1. null_field- 存在
  2. not_null_field- 存在
  3. non_existent_field- 没有。

好的,是时候测试我上面提到的项目阶段了。让我们为我们感兴趣的每个字段添加它:

{'$project': {
    'null_field_exists': {'$or': [
        {'$eq': ['$null_field', null]}, 
        {'$gt': ['$null_field', null]},
    ]},
    'not_null_field_exists': {'$or': [
        {'$eq': ['$not_null_field', null]}, 
        {'$gt': ['$not_null_field', null]},
    ]},
    'non_existent_field_exists': {'$or': [
        {'$eq': ['$non_existent_field', null]}, 
        {'$gt': ['$non_existent_field', null]},
    ]},
}},
Run Code Online (Sandbox Code Playgroud)

我们得到的是:

{
    "null_field_exists" : true,
    "not_null_field_exists" : true,
    "non_existent_field_exists" : false
}
Run Code Online (Sandbox Code Playgroud)

正确的!

还有一个小注意事项:我们用于null比较,因为它是最小的值,至少有价值(较小的是不存在)。


Blu*_*héo 6

我的回答是:

{$match:{
    $and:[{
        name:{
            $exists:true
        }
    }, {
        $expr:{
            $eq:["$$id", "$_id"]
        }
    }]
}}
Run Code Online (Sandbox Code Playgroud)

我在管道阶段的查找中使用它。此帖子 2 规则第一个,名称必须存在。第二件事是这两个集合之间的关系。我确信您可以针对您的问题修改此内容。


Alo*_*wal 6

在猫鼬中,仅在工作后

$ne:  [ { $type : "$var_to_check"}, 'missing'] }
Run Code Online (Sandbox Code Playgroud)


小智 5

邓诺(Dunno)过去是这样,但现在在2019年有了干净的解决方案。在聚合管道中执行此操作

$match: {"my_field": {$ne: null}}
Run Code Online (Sandbox Code Playgroud)

好东西在我的lang'ne'意味着不是:)

  • “ne”的意思是“不等于”。 (3认同)
  • 上述查询将返回 my_field 不存在的文档。这不是我们所期望的。 (2认同)
  • 我们是会说“ne”的骑士! (2认同)