1

Mongo docs$allは、オペレーターについてコメントしています。

現在のリリースでは、$all 演算子を使用するクエリは、クエリ配列の最初の要素に一致するすべてのドキュメントをスキャンする必要があります。その結果、クエリをサポートするインデックスを使用しても、特に配列の最初の要素があまり選択的でない場合は、操作に時間がかかる場合があります。

私の DB 構造 (以下に示す) を考慮$allして、Mongo API を使用してクエリを作成するたびに、最初の引数がmore selectiveフィールドになるようにする方法を考えています。

{ 
 _id: 1,
 records : [ {n: "Name", v: "Kevin"}, {n: "Age", v: 100} ]
}

私が最初に考えたのは、「選択的な」クエリ フィールドの動的リストを定期的に作成することでした。コードでクエリを実行するとき、演算子を介して検索されている 2 つ以上のフィールドを見て$all、より多く/最も「選択的」な項目を最初の要素として配置します。

この問題を処理するためのより良い方法またはベストプラクティスはありますか?

4

5 に答える 5

2

DB 構造がいくつかのフィールドに限定されている場合、最適な (そして簡単な) オプションは、DB 構造を次のように変更することだと思います。

{ 
 _id: 1,
 records : [ {n: "Name", v: "Kevin"}, {n: "Age", v: 100} ]
}

{ 
 _id: 1,
 Name: "Kevin",
 Age: 100
}

{Name: 1, Age: 1} のインデックスでデータベースにクエリを実行できるように

ただし、「レコード」配列に汎用キー/値構造があり、 $all 演算子を使用する必要がある場合は、集約フレームワーク (または MapReduce) を使用して「選択的」クエリ フィールドの動的リストを作成できます。

私の考えを例で説明します(コーディングのサンプルのみ):

おそらく、あなたのクエリは次のようなものです:

db.structure.find({ records: { 
    $all: [ {n: "Name", v: "Kevin"}, {n: "Age", v: 100} ] 
} } )

そして例えば

db.structure.find({ 
    records: {n: "Name", v: "Kevin"} 
} ).count() --> 1000

db.structure.find({ 
    records: {n: "Age", v: 100} 
} ).count() --> 100   // -> most selective!

したがって、最速のクエリは {n: "Age", v: 100} を最初の項目として使用することになります...

次のようなものでバッチを書くことができます

var result = db.structure.aggregate([
 { $unwind: "records" },
 { $group: {
     _id: "$records",
     record_count: { $sum: 1 }
   }
 }
]);
db.selective_items.save(result.result);

次に、$all 演算子を使用してクエリを実行する場合は、まず、selective_items コレクションをクエリして、record_count の値が小さいレコードを見つけ、正しい最初の要素を使用して $all クエリを作成する必要があります。

この解決策があなたの質問に答えることを願っています

于 2013-10-09T15:05:22.857 に答える
1

「選択的な」属性を追跡する際の問題は、データの再サンプリングを常に行う必要があることです。さらに、クエリに提供される値に関係なく、選択的なすべての属性がそのプロパティを保持すると想定しています。

可能な属性のセットの例を次に示します。

{ n:"lastName", v:"Kamsky" }    vs.   { n:"lastName", v:"Smith" }

{ n:"firstName", v:"Asya" }     vs.   { n:"firstName", v:"Jessica" }

{ n:"Age", v: 21 }              vs.   { n:"Age", v: 15 }

最初の 2 つの比較行では、姓と名は非常に選択的 (名または姓がまれな場合) か、特に選択的でない場合 (データベースの母集団全体で共通の場合) のいずれかです。

3 行目では、データベースの内容がわからない場合、「Age」値のいずれかが選択的であるかどうかを判断できません。クエリしている大学生のコレクションの場合、2 番目の値は非常に選択的になりますが、高校生の場合、最初の値は非常に選択的なものになります。

あなたのクエリのいずれかが不等式である場合、他の要素が選択的でない限り、それらは最初の位置の要素には適していないと思います。

しかし、何が選択的で何が選択的でないかをどのように追跡できますか?

「統計」を追跡するためにできることは次のとおりです(いわば):

var X = 3;  // assign a threshold equal to some number that's "too high" to scan 
db.<collection>.aggregate( 
     {$unwind  :  "$records"}, 
     {$group   :  {_id:{n:"$records.n",v:"$records.v"}, count:{$sum:1}}}, 
     {$group   :  {_id:"$_id.n", totalDistinctValues:{$sum:1}, values:{$push:{value:"$_id.v", appears:"$count"}} } },
     {$project :  {_id:0, AttributeName:"$_id", totalDistinctValues:1, values:1}}, 
     {$match   :  {"values.appears":{$not:{$gte: X }} }}, 
     {$sort    :  {totalDistinctValues:1}}, 
     {$limit   :  10}
)

上記の集計は、最もユニークな (異なる) 値を持つ 10 個の属性を返します。さらに、各ドキュメントには、可能な値のリストとそれが表示される回数が含まれます。正確なフィールドと可能性のあるデータ分布に応じて、これを微調整する余地がたくさんあります。これをコレクションに保存してクエリを実行することはできますが、これは理にかなっていないため、アプリケーションにキャッシュするだけです。あなたが説明したユースケースに固執します。

私の小さなテストコレクションでは、返された結果は次のとおりです(「雇用主」フィールドもありましたが、レコードの半分が同じ雇用主をリストしていたため、それを排除しました):

[
    {
        "totalDistinctValues" : 5,
        "AttributeName" : "firstName",
        "values" : [
            {
                "value" : "Sheldon",
                "appears" : 1
            },
            {
                "value" : "Raj",
                "appears" : 1
            },
            {
                "value" : "Penny",
                "appears" : 1
            },
            {
                "value" : "Asya",
                "appears" : 1
            },
            {
                "value" : "John",
                "appears" : 2
            }
        ]
    },
    {
        "totalDistinctValues" : 6,
        "AttributeName" : "lastName",
        "values" : [
            {
                "value" : "Kumar",
                "appears" : 1
            },
            {
                "value" : "Smith",
                "appears" : 1
            },
            {
                "value" : "Lane",
                "appears" : 1
            },
            {
                "value" : "Williams",
                "appears" : 1
            },
            {
                "value" : "Kamsky",
                "appears" : 1
            },
            {
                "value" : "Cooper",
                "appears" : 1
            }
        ]
    },
    {
        "totalDistinctValues" : 6,
        "AttributeName" : "Age",
        "values" : [
            {
                "value" : 31,
                "appears" : 1
            },
            {
                "value" : 21,
                "appears" : 1
            },
            {
                "value" : 22,
                "appears" : 1
            },
            {
                "value" : 29,
                "appears" : 1
            },
            {
                "value" : 49,
                "appears" : 1
            },
            {
                "value" : 59,
                "appears" : 1
            }
        ]
    }
]

結論: この集計を実行してデータの実際の分布を確認すると、一部の属性が自然に選択的であり、クエリに常に (または頻繁に) 存在することが明らかになる場合があります。それらを最初に置きます。それ以外の場合は、統計を使用してより選択的な属性を最初に動的に配置し、そのようなクエリのインデックス使用の改善を追跡しているhttps://jira.mongodb.org/browse/SERVER-2348に投票してください。

于 2013-10-15T17:00:07.643 に答える
0

配列フィールドにインデックスが定義されている場合、フィールド値に対して複数のカウントを実行すると、$all フェーズでの不適切な順序よりも高速になります。ただし、これは極端に最適ではないように聞こえますが、そうではないため、インデックスを使用して選択性 (要素の数) を決定します。これにより、カウント クエリを完全に並列に実行しながら、より高度なアプリケーション設計が可能になるため、異なるスレッドで同時に実行できます。インデックスでカバーされたクエリを実行する方が最適ですが、残念ながら配列フィールドではサポートされていません。

リソースが不足している場合 (多数のユーザー リクエスト、および $all 配列内の多数の要素)、常にクエリを最適化しようとはしません。この場合、$all 配列内に複数の要素がある場合です。チェックする合理的な量として常に最大 3 を選択し、それらの中から最適なものを選択します。または、たとえば、最初の 3 つの並列カウントが 200 未満のドキュメントを返す場合、$all 配列の最初の要素としてそれを受け入れます。もちろん、処理する必要があるのが多数の同時ユーザー リクエストなのか、一部だけなのかによって異なります (低レイテンシ用に最適化されたリクエストをいくつか使用すると、データベースに負荷をかけるリスクなしに、一度に複数のカウント クエリを準備して実行できます)。

于 2013-10-13T14:29:07.120 に答える