私の記録にはすべて「写真」というフィールドがあります。このフィールドは文字列の配列です。
この配列ISが空ではない最新の10レコードが欲しいのですが。
私はぐるぐる回ったが、奇妙なことに私はこれについてあまり見つけていない。 $ whereオプションを読みましたが、それがネイティブ関数にとってどれほど遅いのか、そしてもっと良い解決策があるのであればと思いました。
それでも、それはうまくいきません。
ME.find({$where: 'this.pictures.length > 0'}).sort('-created').limit(10).execFind()
何も返しません。 this.pictures
をlengthビットなしで残してもうまくいきますが、もちろん空のレコードも返します。
キーを持たない文書もある場合は、次のものを使用できます。
ME.find({ pictures: { $exists: true, $not: {$size: 0} } })
$ sizeが関係している場合、MongoDBはインデックスを使用しません。そのため、これがより良い解決策です。
ME.find({ pictures: { $exists: true, $ne: [] } })
MongoDB 2.6リリース以降、演算子$gt
と比較できますが、予期しない結果につながる可能性があります(詳細な説明 この回答に を見つけることができます)。
ME.find({ pictures: { $gt: [] } })
特にmongodbドキュメントでもう少し見て、ちょっと戸惑いながら、これが答えでした。
ME.find({pictures: {$exists: true, $not: {$size: 0}}})
これはまたあなたのために働くかもしれません:
ME.find({'pictures.0': {$exists: true}});
クエリを実行するときには、精度とパフォーマンスという2つのことに気を配ってください。それを念頭に置いて、私はMongoDB v3.0.14でいくつかの異なるアプローチをテストしました。
TL; DR db.doc.find({ nums: { $gt: -Infinity }})
が最速かつ最も信頼性があります(少なくとも私がテストしたMongoDBバージョンで)。
編集:これはMongoDB v3.6では機能しなくなりました。潜在的な解決策については、この記事の下にあるコメントを参照してください。
リストフィールドなしの1kのドキュメント、空のリストのある1kのドキュメント、空でないリストのある5kのドキュメントを挿入しました。
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を使用しますが、空のリストを1つずつ除外する必要があります。
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: [] }})
IS使用しているインデックスに応じて危険があるため、予期しない結果が生じる可能性があります。これは文書を進めない無効なインデックススキャンのためです。
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
}
}
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
が少なくとも1つの配列要素を持つdocを適切に含み、pictures
が空の配列で、配列ではない、または欠落しているdocを除外します。
これを実現するには、次のいずれかを使用できます。
どちらも、要求されたキーを持たないオブジェクトに対して結果を返さないように注意します。
db.video.find({pictures: {$exists: true, $gt: {$size: 0}}})
db.video.find({comments: {$exists: true, $not: {$size: 0}}})
Mongo演算子$ existsの上にヘルパーメソッド Exists を使用することもできます。
ME.find()
.exists('pictures')
.where('pictures').ne([])
.sort('-created')
.limit(10)
.exec(function(err, results){
...
});
{ $where: "this.pictures.length > 1" }
$ whereを使用し、配列フィールドのサイズを返すthis.field_name.lengthを渡して、numberと比較して確認します。配列のサイズが1以上である必要がある配列の値が1つでもある場合、すべての配列フィールドの長さは複数になります。つまり、その配列にデータがあることを意味します。
ドキュメントに従って、$elemMatch
演算子を使用します。
$ elemMatchオペレーターは、指定されたすべての照会基準に一致する少なくとも1つのエレメントを持つ配列フィールドを含む文書を突き合わせます。
$elemMatches
は、値が配列であり、空ではないことを確認します。そのため、クエリは次のようになります。
ME.find({ pictures: { $elemMatch: {$exists: true }}})
PSこのコードの変種はMongoDB大学のM121コースにあります。
ME.find({pictures: {$type: 'array', $ne: []}})
3.2 より前のMongoDbバージョンを使用している場合は、$type: 4
の代わりに$type: 'array'
を使用してください。このソリューションでは $ size を使用していないので、インデックスに問題はありません( "クエリではクエリの$ size部分にインデックスを使用できません")。
これらを含む他の解決策(受け入れられた答え):
ME.find({pictures:{$ exists:true、$ not:{$ size:0}}}); ME.find({pictures:{$ exists:true、$ ne:[]}})
たとえば、「pictures」がnull
、undefined
、0などの場合でもドキュメントを返すので、wrongです。