Answers:
如果您还有没有密钥的文档,则可以使用:
ME.find({ pictures: { $exists: true, $not: {$size: 0} } })
如果涉及$ size,MongoDB不会使用索引,因此这是一个更好的解决方案:
ME.find({ pictures: { $exists: true, $ne: [] } })
从MongoDB 2.6版本开始,您可以与运算符进行比较,$gt
但可能会导致意外的结果(您可以在此答案中找到详细的说明):
ME.find({ pictures: { $gt: [] } })
mongoengine
ME.find({ pictures: { $gt: [] } })
即使在较新的MongoDB版本中,也要小心,危险。如果您在列表字段上有一个索引,并且在查询过程中使用了该索引,则会得到意外的结果。例如:db.doc.find({'nums': { $gt: [] }}).hint({ _id: 1 }).count()
返回正确的数字,而db.doc.find({'nums': { $gt: [] }}).hint({ nums: 1 }).count()
返回0
。
经过一番仔细研究之后,尤其是在mongodb文档中,又将一些东西弄乱了,这就是答案:
ME.find({pictures: {$exists: true, $not: {$size: 0}}})
pictures
字段的记录。
这也可能对您有用:
ME.find({'pictures.0': {$exists: true}});
pictures.2
存在但pictures.1
不存在的情况?
$exists
运营商是一个布尔值,不偏移。@tenbatsu应该使用true
而不是1
。
Would there ever be a case where pictures.2 exists but pictures.1 does not?
是的,这种情况可能会发生。
pictures
是子文档而不是数组时才可能发生。例如pictures: {'2': 123}
pictures
。
查询时,您关心两件事-准确性和性能。考虑到这一点,我在MongoDB v3.0.14中测试了几种不同的方法。
TL; DR db.doc.find({ nums: { $gt: -Infinity }})
是最快和最可靠的(至少在我测试过的MongoDB版本中)。
编辑:这不再适用于MongoDB v3.6!请参阅此帖子下的评论,以寻求可能的解决方案。
我插入了1k个带有w / 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
}
}
seen_events
String数组,该数组也已建立索引。使用搜寻{ $gt: -Infinity }
,我立即得到0个文件。使用{ $exists: true, $ne: [] }
我,我得到了更可能的1,2m文档,而在FETCH阶段浪费了很多时间:gist.github.com/N-Coder/b9e89a925e895c605d84bfeed648d82c
db.test_collection.find({"seen_events.0": {$exists: true}})
不好,因为它使用了集合扫描。2. db.test_collection.find({seen_events: {$exists: true, $ne: []}})
是不好,因为其IXSCAN匹配所有文档,然后在慢速FETCH阶段执行了过滤; 3。同样适用db.test_collection.find({seen_events: {$exists: true, $not: {$size: 0}}})
; 4。其他所有查询均返回无效结果;
seen_events
都包含字符串,则可以使用以下命令:db.test_collection.find({seen_events: {$gt: ''}}).count()
。要确认其性能良好,请签出db.test_collection.find({seen_events: {$gt: ''}}).explain(true).executionStats
。您可以通过模式验证来强制看到的事件是字符串:docs.mongodb.com/manual/core/schema-validation
从2.6版本开始,另一种方法是将字段与一个空数组进行比较:
ME.find({pictures: {$gt: []}})
在外壳中进行测试:
> 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
至少包含一个数组元素的docs,并排除其中pictures
为空数组,非数组或缺失的docs 。
db.ME.createIndex({ pictures: 1 })
,然后db.ME.find({pictures: {$gt: []}})
将返回零个结果,至少在MongoDB中v3.0.14
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:[]}})
是错误的,因为即使例如'pictures'为,,0等null
,它们也会返回文档undefined
。
使用$elemMatch
操作员:根据文档
$ elemMatch运算符匹配包含一个包含至少一个与所有指定查询条件匹配的元素的数组字段的文档。
$elemMatches
确保该值是一个数组并且不为空。因此查询将类似于
ME.find({ pictures: { $elemMatch: {$exists: true }}})
PS在MongoDB大学的M121课程中可以找到此代码的变体。
{ $where: "this.pictures.length > 1" }
使用$ where并传递this.field_name.length,它返回数组字段的大小,并通过与数字比较来检查它。如果任何数组的值大于数组大小的值必须至少为1,则所有数组字段的长度均大于一,这表示该数组中有一些数据