web-dev-qa-db-ja.com

mongoDBの文字列フィールド値の長さ

フィールドのデータ型は文字列です。フィールド名の文字長が40を超えるデータを取得したい。

これらのクエリを試しましたが、エラーを返しました。 1。

db.usercollection.find(
{$where: "(this.name.length > 40)"}
).limit(2);

output :error: {
    "$err" : "TypeError: Cannot read property 'length' of undefined near '40)' ",
    "code" : 16722
}

これは2.4.9で動作していますが、私のバージョンは2.6.5です

58
SURYA GOKARAJU

MongoDB 3.6以降の場合:

$expr演算子を使用すると、クエリ言語内で集計式を使用できるため、$strLenCP次のように文字列の長さをチェックする演算子:

db.usercollection.find({ 
    "name": { "$exists": true },
    "$expr": { "$gt": [ { "$strLenCP": "$name" }, 40 ] } 
})

MongoDB 3.4以降の場合:

$redactパイプライン演算子を使用して集計フレームワークを使用することもできます。これにより、$cond演算子と特別な操作を使用$$KEEP論理条件が真であるドキュメントを「保持」するか、$$Pruneでドキュメントを「削除」します条件が偽の場合。

この操作は、コレクション内のフィールドを選択し、結果を保持する新しいフィールドを作成するパイプライン$projectに似ています論理条件クエリから、その後の$match)、ただし$redactは、より効率的な単一のパイプラインステージを使用します。

論理条件については、 String Aggregation Operators を使用できます$strLenCP演算子文字列の長さを確認してください。長さが $gt 指定値である場合、これは真の一致であり、ドキュメントは「保持」されます。それ以外の場合は、「プルーニング」されて破棄されます。


上記の概念を示す次の集約操作の実行を検討してください。

db.usercollection.aggregate([
    { "$match": { "name": { "$exists": true } } },
    {
        "$redact": {
            "$cond": [
                { "$gt": [ { "$strLenCP": "$name" }, 40] },
                "$$KEEP",
                "$$Prune"
            ]
        }
    },
    { "$limit": 2 }
])

$where を使用している場合は、括弧で囲まずにクエリを試してください。

db.usercollection.find({$where: "this.name.length > 40"}).limit(2);

より良いクエリは、フィールドの存在をチェックしてから長さをチェックすることです:

db.usercollection.find({name: {$type: 2}, $where: "this.name.length > 40"}).limit(2); 

または:

db.usercollection.find({name: {$exists: true}, $where: "this.name.length > 
40"}).limit(2); 

MongoDBはnon -- $where の前にクエリ操作を評価します $where 式とnon$whereクエリステートメントはインデックスを使用できます。はるかに優れたパフォーマンスは、文字列の長さを別のフィールドとして保存することです。その後、インデックスを作成したり、検索したりできます。 $where を適用すると、それに比べてはるかに遅くなります。 JavaScript式と $where 演算子を、他のデータを構造化できない場合の最後の手段として使用することをお勧めします方法、またはデータの小さなサブセットを扱う場合。


$where 演算子の使用を回避する別のより高速なアプローチは、- $regex演算子。以下を検索する次のパターンを考えます

db.usercollection.find({"name": {"$type": 2, "$regex": /^.{41,}$/}}).limit(2); 

-docsから:

フィールドにインデックスが存在する場合、MongoDBはインデックス内の値に対して正規表現を照合します。これはコレクションスキャンよりも高速です。正規表現が「プレフィックス式」である場合、さらなる最適化が発生する可能性があります。つまり、すべての潜在的な一致は同じ文字列で始まります。これにより、MongoDBはそのプレフィックスから「範囲」を構築し、その範囲内にあるインデックスの値とのみ一致します。

正規表現は、キャレット(^)または左アンカー(\A)で始まり、その後に単純な記号の文字列が続く「プレフィックス式」です。たとえば、正規表現/^abc.*/は、abcで始まるインデックスの値に対してのみ照合することにより最適化されます。

また、/^a/, /^a.*/,/^a.*$/は同等の文字列に一致しますが、パフォーマンス特性は異なります。適切なインデックスが存在する場合、これらの式はすべてインデックスを使用します。ただし、/^a.*//^a.*$/は遅いです。 /^a/は、プレフィックスの一致後にスキャンを停止できます。

120
chridam

これを実現できるmongodbの方法の1つを次に示します。

db.usercollection.find({ $where: 'this.name.length < 4' })
5
Rajdeep Gautam

同様のシナリオがありましたが、私の場合、文字列は第1レベルの属性ではありません。オブジェクトの中にあります。ここでは適切な答えが見つかりませんでした。だから私はあなたと私のソリューションを共有したいと思いました(これが同様の種類の問題を持つ人を助けることを願っています)。

Parent Collection 

{
"Child":
{
"name":"Random Name",
"Age:"09"
}
}

例:子供の名前の長さが10文字を超えるコレクションのみを取得する必要がある場合。

 db.getCollection('Parent').find({$where: function() { 
for (var field in this.Child.name) { 
    if (this.Child.name.length > 10) 
        return true;

}
}})
2

ドキュメントが多すぎる場合、$whereおよび$exprを使用したクエリは遅くなります。

$regexの使用は、$where$exprよりもはるかに高速です。

db.usercollection.find({ 
  "name": /^[\s\S]{40,}$/, // name.length >= 40
})

or 

db.usercollection.find({ 
  "name": { "$regex": "^[\s\S]{40,}$" }, // name.length >= 40
})

このクエリは次と同じ意味です

db.usercollection.find({ 
  "$where": "this.name && this.name.length >= 40",
})

or

db.usercollection.find({ 
    "name": { "$exists": true },
    "$expr": { "$gte": [ { "$strLenCP": "$name" }, 40 ] } 
})

コレクションの各クエリをテストしました。

# find
$where: 10529.359ms
$expr: 5305.801ms
$regex: 2516.124ms

# count
$where: 10872.006ms
$expr: 2630.155ms
$regex: 158.066ms
0
Fumiya Karasawa