web-dev-qa-db-ja.com

配列サイズが1より大きい文書を照会します

次の形式のドキュメントを含むMongoDBコレクションがあります。

{
  "_id" : ObjectId("4e8ae86d08101908e1000001"),
  "name" : ["Name"],
  "zipcode" : ["2223"]
}
{
  "_id" : ObjectId("4e8ae86d08101908e1000002"),
  "name" : ["Another ", "Name"],
  "zipcode" : ["2224"]
}

現在、特定の配列サイズに一致するドキュメントを入手できます。

db.accommodations.find({ name : { $size : 2 }})

これはname配列に2つの要素を持つドキュメントを正しく返します。しかし、nameフィールドの配列サイズが2より大きいすべての文書を返すために$gtコマンドを実行することはできません。

db.accommodations.find({ name : { $size: { $gt : 1 } }})

1より大きいサイズのname配列を持つすべての文書を選択するにはどうすればよいですか(できれば現在のデータ構造を変更する必要なしに)。

554
emson

更新:

mongodbバージョンの場合 2.2+ これを行うためのより効率的な方法は @JohnnyHK で別の answer で説明されています。


1. $ where を使う

db.accommodations.find( { $where: "this.name.length > 1" } );

しかし...

Javascriptはこのページにリストされているネイティブの演算子よりも実行が遅くなりますが、非常に柔軟です。詳しくは、サーバーサイドの処理ページを参照してください。

2. extra field NamesArrayLengthを作成し、それをnames配列の長さで更新してから、クエリで使用します。

db.accommodations.find({"NamesArrayLength": {$gt: 1} });

それはより良い解決策になるでしょう、そしてはるかに速く働くでしょう(あなたはそれにインデックスを作成することができます)。

420
Andrew Orsich

MongoDB 2.2以降でこれを行うより効率的な方法があります。クエリオブジェクトキーで数値配列インデックスを使用できるようになりました。

// Find all docs that have at least a second name array element.
db.accommodations.find({'name.1': {$exists: true}})

このクエリは、部分フィルタ式を使用するインデックスでサポートできます(3.2以降が必要)。

db.accommodations.createIndex(
    {'name.1': 1},
    {partialFilterExpression: {'name.1': {$exists: true}}}
);
1135
JohnnyHK

解釈された$where句を使用していないので、これがあなたの質問に答える最も速い問い合わせであると私は思います:

{$nor: [
    {name: {$exists: false}},
    {name: {$size: 0}},
    {name: {$size: 1}}
]}

これは、「名前のないもの(存在しないか空の配列)または名前が1つだけのものを除くすべての文書」を意味します。

テスト:

> db.test.save({})
> db.test.save({name: []})
> db.test.save({name: ['George']})
> db.test.save({name: ['George', 'Raymond']})
> db.test.save({name: ['George', 'Raymond', 'Richard']})
> db.test.save({name: ['George', 'Raymond', 'Richard', 'Martin']})
> db.test.find({$nor: [{name: {$exists: false}}, {name: {$size: 0}}, {name: {$size: 1}}]})
{ "_id" : ObjectId("511907e3fb13145a3d2e225b"), "name" : [ "George", "Raymond" ] }
{ "_id" : ObjectId("511907e3fb13145a3d2e225c"), "name" : [ "George", "Raymond", "Richard" ] }
{ "_id" : ObjectId("511907e3fb13145a3d2e225d"), "name" : [ "George", "Raymond", "Richard", "Martin" ] }
>
113
Tobia

あなたも集約を使用することができます:

db.accommodations.aggregate(
[
     {$project: {_id:1, name:1, zipcode:1, 
                 size_of_name: {$size: "$name"}
                }
     },
     {$match: {"size_of_name": {$gt: 1}}}
])

//あなたはトランジットドキュメントに "size_of_name"を追加し、それを使って名前のサイズをフィルタリングします

50

上記のどれも私のために働きませんでした。これは私がそれを共有しているのでそうしました:

db.collection.find( {arrayName : {$exists:true}, $where:'this.arrayName.length>1'} )
33
Zloy Smiertniy

このようなことをしてみてください。

db.getCollection('collectionName').find({'ArrayName.1': {$exists: true}})

1が数値です。50を超えるレコードを取得する場合は、ArrayNameを実行してください。50ありがとう。

30
Aman Goel
db.accommodations.find({"name":{"$exists":true, "$ne":[], "$not":{"$size":1}}})
19
Yadvendar

$ expr (3.6 mongo version operator)を使用して、通常のクエリで集計関数を使用できます。

query operators vs aggregation comparison operators を比較してください。

db.accommodations.find({$expr:{$gt:[{$size:"$name"}, 1]}})
17
Veeram

私はこの解決法を見つけました、特定の長さより大きい配列フィールドを持つ項目を見つけるために

db.allusers.aggregate([
  {$match:{username:{$exists:true}}},
  {$project: { count: { $size:"$locations.lat" }}},
  {$match:{count:{$gt:20}}}
])

最初の$ match集合はすべての文書に当てはまる引数を使います。空白の場合、私は得るでしょう

"errmsg" : "exception: The argument to $size must be an Array, but was of type: EOO"
11
Barrard

MongoDB 3.6には$ expr https://docs.mongodb.com/manual/reference/operator/query/expr/ が含まれています。

$ matchの中の式を評価するために$ exprを使うことができます。

          {$match: {
                  $expr: {$gt: [{$size: "$yourArrayField"}, 0]}
              }
          }

または見つける

collection.find({$ expr:{$ gte:[{$ size: "$ yourArrayField"}、0]}});

0
Daniele Tassone