651

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

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

我已经用谷歌搜索了,但奇怪的是我没有找到太多关于这个的东西。我已经阅读了 $where 选项,但我想知道这对本机函数有多慢,以及是否有更好的解决方案。

即便如此,这也行不通:

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

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

4

13 回答 13

1124

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

ME.find({ pictures: { $exists: true, $not: {$size: 0} } })

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

ME.find({ pictures: { $exists: true, $ne: [] } })

如果您的属性可能具有无效值(如或其他),那么您可以按照此答案的建议null boolean添加额外的检查:$types

使用 mongo >= 3.2:

ME.find({ pictures: { $exists: true, $type: 'array', $ne: [] } })

使用 mongo < 3.2:

ME.find({ pictures: { $exists: true, $type: 4, $ne: [] } })

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

ME.find({ pictures: { $gt: [] } })
于 2014-08-05T15:24:27.547 回答
198

经过一番查看,尤其是在 mongodb 文档中,以及令人费解的部分,这就是答案:

ME.find({pictures: {$exists: true, $not: {$size: 0}}})
于 2013-02-09T16:02:48.853 回答
127

这也可能对您有用:

ME.find({'pictures.0': {$exists: true}});
于 2013-07-16T02:58:52.243 回答
38

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

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

编辑:这不再适用于 MongoDB v3.6!请参阅此帖子下的评论以获取潜在的解决方案。

设置

我插入了 1k 个带有 oa 列表字段的文档,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 });

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

测试

db.doc.find({'nums': {'$exists': true}})返回错误的结果(对于我们想要完成的事情)。

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

--

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
}

--

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
    }
  }
}

--

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
    }
  }
}

--

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
    }
  }
}

--

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
    }
  }
}

--

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
}

--

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
  }
}
于 2017-03-04T20:49:39.793 回答
32

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

ME.find({pictures: {$gt: []}})

在 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 ] }

因此,它正确地包含了pictures具有至少一个数组元素的文档,并排除了pictures为空数组、不是数组或缺失的文档。

于 2015-02-06T15:10:19.163 回答
13

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

ME.find({pictures: {$type: 'array', $ne: []}})

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

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

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

错误的,因为即使“图片”是nullundefined、 0 等,它们也会返回文档。

于 2018-09-23T11:51:53.460 回答
6

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

db.video.find({pictures: {$exists: true, $gt: {$size: 0}}})
db.video.find({comments: {$exists: true, $not: {$size: 0}}})
于 2015-06-15T08:27:04.020 回答
4
db.find({ pictures: { $elemMatch: { $exists: true } } })

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

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

于 2020-12-03T17:51:47.467 回答
3

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

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

$elemMatches确保该值是一个数组并且它不为空。所以查询将类似于

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

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

于 2019-05-19T20:43:46.647 回答
0

您还可以在 Mongo 运算符 $exists 上使用辅助方法Exists

ME.find()
    .exists('pictures')
    .where('pictures').ne([])
    .sort('-created')
    .limit(10)
    .exec(function(err, results){
        ...
    });
于 2016-02-10T19:28:01.620 回答
0
{ $where: "this.pictures.length > 1" }

使用 $where 并传递 this.field_name.length ,它返回数组字段的大小并通过与数字比较来检查它。如果任何数组的值大于数组大小必须至少为 1。所以所有数组字段的长度都大于 1,这意味着它在该数组中有一些数据

于 2018-02-28T06:16:51.703 回答
0

这也有效:

db.getCollection('collectionName').find({'arrayName': {$elemMatch:{}}})
于 2020-11-13T19:17:30.517 回答
-9
ME.find({pictures: {$exists: true}}) 

就这么简单,这对我有用。

于 2015-02-09T20:29:28.370 回答