查找数组字段不为空的MongoDB记录

ske*_*rit 444 mongoose mongodb

我的所有记录都有一个名为"图片"的字段.该字段是一个字符串数组.

我现在想要这个数组不为空的最新10条记录.

我已经google了一下,但奇怪的是我在这方面找不到多少.我已经阅读了$ where选项,但我想知道本机函数有多慢,以及是否有更好的解决方案.

即便如此,这不起作用:

ME.find({$where: 'this.pictures.length > 0'}).sort('-created').limit(10).execFind()
Run Code Online (Sandbox Code Playgroud)

什么都不返回 离开this.pictures没有长度位确实有效,但当然它也返回空记录.

Chr*_*is' 727

如果您还有没有密钥的文档,您可以使用:

ME.find({ pictures: { $exists: true, $not: {$size: 0} } })
Run Code Online (Sandbox Code Playgroud)

如果涉及$ size,MongoDB不使用索引,所以这是一个更好的解决方案:

ME.find({ pictures: { $exists: true, $ne: [] } })
Run Code Online (Sandbox Code Playgroud)

自MongoDB 2.6发布以来,您可以与运营商进行比较,$gt但可能会导致意外结果(您可以在此答案中找到详细说明):

ME.find({ pictures: { $gt: [] } })
Run Code Online (Sandbox Code Playgroud)

  • 小心,`ME.find({pictures:{$ gt:[]}})`危险,即使在较新的MongoDB版本中也是如此.如果列表字段上有索引,并且在查询期间使用了该索引,则会得到意外结果.例如:`db.doc.find({'nums':{$ gt:[]}}).提示({_id:1}).count()`返回正确的数字,而`db.doc.find ({'nums':{$ gt:[]}}).提示({nums:1}).count()`返回`0`. (48认同)
  • 对我来说这是正确的方法,因为它确保数组存在且不为空. (6认同)
  • @ wojcikstefan的评论需要加以评论,以防止人们使用最后的建议,实际上,在某些情况下,不会返回匹配的文件. (5认同)
  • 请参阅下面我的详细答案,了解为什么这可能对您不起作用:http://stackoverflow.com/a/42601244/1579058 (3认同)
  • 使用 $exists 返回图片为空、未定义等的文档。这是一个[更好的解决方案](/sf/ask/1035277911/空/52465830#52465830) (2认同)

ske*_*rit 169

经过一番观察,特别是在mongodb文件中,以及令人费解的一点,这就是答案:

ME.find({pictures: {$exists: true, $not: {$size: 0}}})
Run Code Online (Sandbox Code Playgroud)

  • 这不起作用.我不知道这是否有用,但这也会返回没有"图片"键的对象. (27认同)
  • 令人难以置信的是这个答案有63个upvotes,实际上@rdsoze说的是真的 - 查询也将返回*不具有'pictures`字段的记录. (17认同)
  • @rdsoze问题的*第一行*表示*"我的所有记录都有一个名为"pictures"的字段.这个字段是一个数组"*.更重要的是,这是一个非常现实和常见的场景.这个答案没有错,它可以准确地解决这个问题,并且批评或贬低它,因为它没有解决*不同的*问题是愚蠢的. (15认同)
  • 注意,如果涉及$ size,mongoDB将不使用索引[link](http://docs.mongodb.org/manual/reference/operator/query/size/).最好包括{$ ne:[]}和{$ ne:null}. (5认同)
  • @Cec所有文档都说,如果您在查询中使用 $size ,它不会使用任何索引来为您提供更快的结果。因此,如果您在该字段上有索引并且想要使用它,请坚持使用其他方法,例如 {$ne:[]},如果这对您有用,那么它将使用您的索引。 (2认同)

小智 102

这可能也适合你:

ME.find({'pictures.0': {$exists: true}});
Run Code Online (Sandbox Code Playgroud)

  • 真好!这也使您可以检查最小尺寸。您知道数组是否总是按顺序索引吗?会不会存在“ pictures.2”但不存在“ pictures.1”的情况? (2认同)
  • `$ exists`运算符是布尔值,而不是偏移量.@tenbatsu应该使用`true`而不是`1`. (2认同)
  • @anushr`将来我会有其中pictures.2存在,但pictures.1没有的情况下?`是的,这种情况下可能会发生. (2认同)
  • @TheBndr 只有当“pictures”是子文档而不是数组时,才会发生这种情况。例如`图片:{'2':123}` (2认同)
  • 这是很好且直观的方法,但是请注意性能是否很重要-即使您在“图片”上有索引,它也会进行完整的集合扫描。 (2认同)

woj*_*fan 32

在查询时你关心两件事 - 准确性和性能.考虑到这一点,我在MongoDB v3.0.14中测试了一些不同的方法.

TL; DR db.doc.find({ nums: { $gt: -Infinity }})是最快且最可靠的(至少在我测试的MongoDB版本中).

编辑:这不再适用于MongoDB v3.6!有关可能的解决方案,请参阅此帖子下的评论.

建立

我插入了带有列表字段的1k文档,带有空列表的1k文档和带有非空列表的5个文档.

for (var i = 0; i < 1000; i++) { db.doc.insert({}); }
for (var i = 0; i < 1000; i++) { db.doc.insert({ nums: [] }); }
for (var i = 0; i < 5; i++) { db.doc.insert({ nums: [1, 2, 3] }); }
db.doc.createIndex({ nums: 1 });
Run Code Online (Sandbox Code Playgroud)

我认识到这不足以像我在下面的测试中那样认真对待性能,但它足以呈现所选查询计划的各种查询和行为的正确性.

测试

db.doc.find({'nums': {'$exists': true}}) 返回错误的结果(我们正在努力实现的目标).

MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': {'$exists': true}}).count()
1005
Run Code Online (Sandbox Code Playgroud)

-

db.doc.find({'nums.0': {'$exists': true}})返回正确的结果,但使用完整的集合扫描也很慢(COLLSCAN解释中的注意阶段).

MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums.0': {'$exists': true}}).count()
5
MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums.0': {'$exists': true}}).explain()
{
  "queryPlanner": {
    "plannerVersion": 1,
    "namespace": "test.doc",
    "indexFilterSet": false,
    "parsedQuery": {
      "nums.0": {
        "$exists": true
      }
    },
    "winningPlan": {
      "stage": "COLLSCAN",
      "filter": {
        "nums.0": {
          "$exists": true
        }
      },
      "direction": "forward"
    },
    "rejectedPlans": [ ]
  },
  "serverInfo": {
    "host": "MacBook-Pro",
    "port": 27017,
    "version": "3.0.14",
    "gitVersion": "08352afcca24bfc145240a0fac9d28b978ab77f3"
  },
  "ok": 1
}
Run Code Online (Sandbox Code Playgroud)

-

db.doc.find({'nums': { $exists: true, $gt: { '$size': 0 }}})返回错误的结果.那是因为索引扫描无效推进没有文件.如果没有索引,它可能会准确但很慢.

MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $exists: true, $gt: { '$size': 0 }}}).count()
0
MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $exists: true, $gt: { '$size': 0 }}}).explain('executionStats').executionStats.executionStages
{
  "stage": "KEEP_MUTATIONS",
  "nReturned": 0,
  "executionTimeMillisEstimate": 0,
  "works": 2,
  "advanced": 0,
  "needTime": 0,
  "needFetch": 0,
  "saveState": 0,
  "restoreState": 0,
  "isEOF": 1,
  "invalidates": 0,
  "inputStage": {
    "stage": "FETCH",
    "filter": {
      "$and": [
        {
          "nums": {
            "$gt": {
              "$size": 0
            }
          }
        },
        {
          "nums": {
            "$exists": true
          }
        }
      ]
    },
    "nReturned": 0,
    "executionTimeMillisEstimate": 0,
    "works": 1,
    "advanced": 0,
    "needTime": 0,
    "needFetch": 0,
    "saveState": 0,
    "restoreState": 0,
    "isEOF": 1,
    "invalidates": 0,
    "docsExamined": 0,
    "alreadyHasObj": 0,
    "inputStage": {
      "stage": "IXSCAN",
      "nReturned": 0,
      "executionTimeMillisEstimate": 0,
      "works": 1,
      "advanced": 0,
      "needTime": 0,
      "needFetch": 0,
      "saveState": 0,
      "restoreState": 0,
      "isEOF": 1,
      "invalidates": 0,
      "keyPattern": {
        "nums": 1
      },
      "indexName": "nums_1",
      "isMultiKey": true,
      "direction": "forward",
      "indexBounds": {
        "nums": [
          "({ $size: 0.0 }, [])"
        ]
      },
      "keysExamined": 0,
      "dupsTested": 0,
      "dupsDropped": 0,
      "seenInvalidated": 0,
      "matchTested": 0
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

-

db.doc.find({'nums': { $exists: true, $not: { '$size': 0 }}})返回正确的结果,但性能不好.它在技术上做了一个索引扫描,但它仍然推进所有文档,然后必须过滤它们).

MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $exists: true, $not: { '$size': 0 }}}).count()
5
MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $exists: true, $not: { '$size': 0 }}}).explain('executionStats').executionStats.executionStages
{
  "stage": "KEEP_MUTATIONS",
  "nReturned": 5,
  "executionTimeMillisEstimate": 0,
  "works": 2016,
  "advanced": 5,
  "needTime": 2010,
  "needFetch": 0,
  "saveState": 15,
  "restoreState": 15,
  "isEOF": 1,
  "invalidates": 0,
  "inputStage": {
    "stage": "FETCH",
    "filter": {
      "$and": [
        {
          "nums": {
            "$exists": true
          }
        },
        {
          "$not": {
            "nums": {
              "$size": 0
            }
          }
        }
      ]
    },
    "nReturned": 5,
    "executionTimeMillisEstimate": 0,
    "works": 2016,
    "advanced": 5,
    "needTime": 2010,
    "needFetch": 0,
    "saveState": 15,
    "restoreState": 15,
    "isEOF": 1,
    "invalidates": 0,
    "docsExamined": 2005,
    "alreadyHasObj": 0,
    "inputStage": {
      "stage": "IXSCAN",
      "nReturned": 2005,
      "executionTimeMillisEstimate": 0,
      "works": 2015,
      "advanced": 2005,
      "needTime": 10,
      "needFetch": 0,
      "saveState": 15,
      "restoreState": 15,
      "isEOF": 1,
      "invalidates": 0,
      "keyPattern": {
        "nums": 1
      },
      "indexName": "nums_1",
      "isMultiKey": true,
      "direction": "forward",
      "indexBounds": {
        "nums": [
          "[MinKey, MaxKey]"
        ]
      },
      "keysExamined": 2015,
      "dupsTested": 2015,
      "dupsDropped": 10,
      "seenInvalidated": 0,
      "matchTested": 0
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

-

db.doc.find({'nums': { $exists: true, $ne: [] }})返回正确的结果并略快,但性能仍然不理想.它使用IXSCAN,它只使用现有列表字段推进文档,但随后必须逐个过滤掉空列表.

MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $exists: true, $ne: [] }}).count()
5
MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $exists: true, $ne: [] }}).explain('executionStats').executionStats.executionStages
{
  "stage": "KEEP_MUTATIONS",
  "nReturned": 5,
  "executionTimeMillisEstimate": 0,
  "works": 1018,
  "advanced": 5,
  "needTime": 1011,
  "needFetch": 0,
  "saveState": 15,
  "restoreState": 15,
  "isEOF": 1,
  "invalidates": 0,
  "inputStage": {
    "stage": "FETCH",
    "filter": {
      "$and": [
        {
          "$not": {
            "nums": {
              "$eq": [ ]
            }
          }
        },
        {
          "nums": {
            "$exists": true
          }
        }
      ]
    },
    "nReturned": 5,
    "executionTimeMillisEstimate": 0,
    "works": 1017,
    "advanced": 5,
    "needTime": 1011,
    "needFetch": 0,
    "saveState": 15,
    "restoreState": 15,
    "isEOF": 1,
    "invalidates": 0,
    "docsExamined": 1005,
    "alreadyHasObj": 0,
    "inputStage": {
      "stage": "IXSCAN",
      "nReturned": 1005,
      "executionTimeMillisEstimate": 0,
      "works": 1016,
      "advanced": 1005,
      "needTime": 11,
      "needFetch": 0,
      "saveState": 15,
      "restoreState": 15,
      "isEOF": 1,
      "invalidates": 0,
      "keyPattern": {
        "nums": 1
      },
      "indexName": "nums_1",
      "isMultiKey": true,
      "direction": "forward",
      "indexBounds": {
        "nums": [
          "[MinKey, undefined)",
          "(undefined, [])",
          "([], MaxKey]"
        ]
      },
      "keysExamined": 1016,
      "dupsTested": 1015,
      "dupsDropped": 10,
      "seenInvalidated": 0,
      "matchTested": 0
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

-

db.doc.find({'nums': { $gt: [] }})由于使用的索引可能会产生意想不到的结果,因此是危险的.这是因为索引扫描无效,没有文件.

MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $gt: [] }}).count()
0
MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $gt: [] }}).hint({ nums: 1 }).count()
0
MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $gt: [] }}).hint({ _id: 1 }).count()
5

MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $gt: [] }}).explain('executionStats').executionStats.executionStages
{
  "stage": "KEEP_MUTATIONS",
  "nReturned": 0,
  "executionTimeMillisEstimate": 0,
  "works": 1,
  "advanced": 0,
  "needTime": 0,
  "needFetch": 0,
  "saveState": 0,
  "restoreState": 0,
  "isEOF": 1,
  "invalidates": 0,
  "inputStage": {
    "stage": "FETCH",
    "filter": {
      "nums": {
        "$gt": [ ]
      }
    },
    "nReturned": 0,
    "executionTimeMillisEstimate": 0,
    "works": 1,
    "advanced": 0,
    "needTime": 0,
    "needFetch": 0,
    "saveState": 0,
    "restoreState": 0,
    "isEOF": 1,
    "invalidates": 0,
    "docsExamined": 0,
    "alreadyHasObj": 0,
    "inputStage": {
      "stage": "IXSCAN",
      "nReturned": 0,
      "executionTimeMillisEstimate": 0,
      "works": 1,
      "advanced": 0,
      "needTime": 0,
      "needFetch": 0,
      "saveState": 0,
      "restoreState": 0,
      "isEOF": 1,
      "invalidates": 0,
      "keyPattern": {
        "nums": 1
      },
      "indexName": "nums_1",
      "isMultiKey": true,
      "direction": "forward",
      "indexBounds": {
        "nums": [
          "([], BinData(0, ))"
        ]
      },
      "keysExamined": 0,
      "dupsTested": 0,
      "dupsDropped": 0,
      "seenInvalidated": 0,
      "matchTested": 0
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

-

db.doc.find({'nums.0’: { $gt: -Infinity }}) 返回正确的结果,但性能不佳(使用完整的集合扫描).

MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums.0': { $gt: -Infinity }}).count()
5
MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums.0': { $gt: -Infinity }}).explain('executionStats').executionStats.executionStages
{
  "stage": "COLLSCAN",
  "filter": {
    "nums.0": {
      "$gt": -Infinity
    }
  },
  "nReturned": 5,
  "executionTimeMillisEstimate": 0,
  "works": 2007,
  "advanced": 5,
  "needTime": 2001,
  "needFetch": 0,
  "saveState": 15,
  "restoreState": 15,
  "isEOF": 1,
  "invalidates": 0,
  "direction": "forward",
  "docsExamined": 2005
}
Run Code Online (Sandbox Code Playgroud)

-

db.doc.find({'nums': { $gt: -Infinity }})令人惊讶的是,这非常有效!它提供了正确的结果并且速度很快,从索引扫描阶段推进了5个文档.

MacBook-Pro(mongod-3.0.14) test> db.doc.find({'nums': { $gt: -Infinity }}).explain('executionStats').executionStats.executionStages
{
  "stage": "FETCH",
  "nReturned": 5,
  "executionTimeMillisEstimate": 0,
  "works": 16,
  "advanced": 5,
  "needTime": 10,
  "needFetch": 0,
  "saveState": 0,
  "restoreState": 0,
  "isEOF": 1,
  "invalidates": 0,
  "docsExamined": 5,
  "alreadyHasObj": 0,
  "inputStage": {
    "stage": "IXSCAN",
    "nReturned": 5,
    "executionTimeMillisEstimate": 0,
    "works": 15,
    "advanced": 5,
    "needTime": 10,
    "needFetch": 0,
    "saveState": 0,
    "restoreState": 0,
    "isEOF": 1,
    "invalidates": 0,
    "keyPattern": {
      "nums": 1
    },
    "indexName": "nums_1",
    "isMultiKey": true,
    "direction": "forward",
    "indexBounds": {
      "nums": [
        "(-inf.0, inf.0]"
      ]
    },
    "keysExamined": 15,
    "dupsTested": 15,
    "dupsDropped": 10,
    "seenInvalidated": 0,
    "matchTested": 0
  }
}
Run Code Online (Sandbox Code Playgroud)

  • @NCode 找到了解决方案!如果你确定所有非空 `seen_events` 都包含字符串,你可以使用这个:`db.test_collection.find({seen_events: {$gt: ''}}).count()`。要确认它表现良好,请查看 `db.test_collection.find({seen_events: {$gt: ''}}).explain(true).executionStats`。您可以通过模式验证 *强制* 看到的事件是字符串:https://docs.mongodb.com/manual/core/schema-validation/ (2认同)

Joh*_*yHK 28

从2.6版本开始,另一种方法是将字段与空数组进行比较:

ME.find({pictures: {$gt: []}})
Run Code Online (Sandbox Code Playgroud)

在shell中测试它:

> db.ME.insert([
{pictures: [1,2,3]},
{pictures: []},
{pictures: ['']},
{pictures: [0]},
{pictures: 1},
{foobar: 1}
])

> db.ME.find({pictures: {$gt: []}})
{ "_id": ObjectId("54d4d9ff96340090b6c1c4a7"), "pictures": [ 1, 2, 3 ] }
{ "_id": ObjectId("54d4d9ff96340090b6c1c4a9"), "pictures": [ "" ] }
{ "_id": ObjectId("54d4d9ff96340090b6c1c4aa"), "pictures": [ 0 ] }
Run Code Online (Sandbox Code Playgroud)

所以它正确地包含了pictures至少有一个数组元素的文档,并排除了文档,其中pictures是一个空数组,而不是数组,或者缺少.

  • 小心这个答案可能会给你带来麻烦,如果你尝试使用索引.执行`db.ME.createIndex({pictures:1})`然后``db.ME.find({pictures:{$ gt:[]}})`将返回零结果,至少在MongoDB v3.0.14中 (6认同)

SC1*_*000 10

检索所有且仅检索“图片”为数组且不为空的文档

ME.find({pictures: {$type: 'array', $ne: []}})
Run Code Online (Sandbox Code Playgroud)

如果使用3.2之前的 MongoDb 版本,请使用$type: 4代替$type: 'array'。请注意,此解决方案甚至不使用$size,因此索引没有问题(“查询不能对查询的 $size 部分使用索引”)

其他解决方案,包括这些(已接受的答案):

ME.find({ 图片: { $exists: true, $not: {$size: 0} } }); ME.find({ 图片: { $exists: true, $ne: [] } })

错误的,因为它们返回文档,例如,“图片”是nullundefined、 0 等。


小智 9

db.find({ pictures: { $elemMatch: { $exists: true } } })
Run Code Online (Sandbox Code Playgroud)

$elemMatch匹配包含数组字段且至少有一个元素与指定查询匹配的文档。

因此,您将所有数组与至少一个元素相匹配。


小智 5

您可以使用以下任何一种方法来实现。
两者还注意不要为其中没有请求的键的对象返回结果:

db.video.find({pictures: {$exists: true, $gt: {$size: 0}}})
db.video.find({comments: {$exists: true, $not: {$size: 0}}})
Run Code Online (Sandbox Code Playgroud)


小智 5

使用$elemMatch运算符:根据文档

$elemMatch 运算符匹配包含数组字段的文档,该数组字段至少有一个元素与所有指定的查询条件匹配。

$elemMatches确保该值是一个数组并且不为空。所以查询会是这样的

ME.find({ pictures: { $elemMatch: {$exists: true }}})

PS 此代码的变体可以在 MongoDB 大学的 M121 课程中找到。