2

フィールド名でグループ化することはできますか?または、値でグループ化できるように別の構造が必要ですか?

group by on値を使用でき、配列をほどくことができますが、「apples」、「pears」、「oranges」を明示的に指定しなくても、ここの3つの家の中でJohnが所有するリンゴ、梨、オレンジの合計を取得することは可能です。クエリの一部?(したがって、これは好きではありません);

// total all the fruit John has at each house
db.houses.aggregate([
    {
        $group: {
            _id: null,

            "apples":  { $sum: "$people.John.items.apples" },
            "pears":   { $sum: "$people.John.items.pears" }, 
            "oranges": { $sum: "$people.John.items.oranges" }, 
        }
    },
])

言い換えると、「アイテム」の下の最初のフィールド名でグループ化して、リンゴ:104、ナシ:202、オレンジ:306の合計だけでなく、バ​​ナナ、メロン、その他の可能性のあるものを取得できますか?または、データをカテゴリのようなキーと値のペアの配列に再構築する必要がありますか?

db.createCollection("houses");
db.houses.remove();
db.houses.insert(
[
    {
        House: "birmingham",
        categories : [
            {
                k : "location",
                v : { d : "central" }
            }
        ],
        people: {
            John: {
                items: {
                    apples: 2,
                    pears: 1,
                    oranges: 3,
                }
            },
            Dave: {
                items: {
                    apples: 30,
                    pears: 20,
                    oranges: 10,
                },
            },
        },
    },
    {
        House: "London", categories: [{ k: "location", v: { d: "central" } }, { k: "type", v: { d: "rented" } }],
        people: {
            John: { items: { apples: 2, pears: 1, oranges: 3, } },
            Dave: { items: { apples: 30, pears: 20, oranges: 10, }, },
        },
    },
    {
        House: "Cambridge", categories: [{ k: "type", v: { d: "rented" } }],
        people: {
            John: { items: { apples: 100, pears: 200, oranges: 300, } },
            Dave: { items: { apples: 0.3, pears: 0.2, oranges: 0.1, }, },
        },
    },
]
);

次に、さらに重要なことに、「house.categories.k」でグループ化することもできますか?言い換えれば、「賃貸」と「所有」または「友人」の家(「categories.k.type」でグループ化)に「リンゴ」「ジョン」がいくつあるかを知ることは可能ですか?

最後に-これが可能である場合でも、それは賢明ですか?最初は、オブジェクトの実際のフィールド名を使用してネストされたオブジェクトの辞書を作成することは非常に便利だと思いました。これは、ドキュメントデータベースの論理的な使用法のようであり、MRクエリを配列よりも簡単に記述できるように見えたためです。これがすべて悪い考えであるかどうか疑問に思い始めており、可変フィールド名を使用すると、集計クエリを作成するのが非常にトリッキー/非効率になります。

4

3 に答える 3

3

OK、それで私はこれを部分的に解決したと思います。少なくとも最初の質問のデータの形については。

// How many of each type of fruit does John have at each location
db.houses.aggregate([
    {
        $unwind: "$categories"
    },
    {
        $match: { "categories.k": "location" }
    },
    {
        $group: {
            _id: "$categories.v.d",
            "numberOf": { $sum: 1 },
            "Total Apples": { $sum: "$people.John.items.apples" },
            "Total  Pears": { $sum: "$people.John.items.pears" },
        }
    },
])

をもたらす;

{
        "result" : [
                {
                        "_id" : "central",
                        "numberOf" : 2,
                        "Total Apples" : 4,
                        "Total  Pears" : 2
                }
        ],
        "ok" : 1
}

「中央」しかないことに注意してください。ただし、DBに他の「場所」がある場合は、場所ごとに合計の範囲を取得します。「categories」の配列の代わりにプロパティに名前を付けていれば、$ unwindステップは必要ありませんが、ここで構造がそれ自体と対立していることがわかります。「カテゴリ」の下にある可能性が高いいくつかのキーワードがあります。サンプルデータは「タイプ」と「場所」を示していますが、これらの分類はすべて異なる値で約10個存在する可能性があります。したがって、名前付きフィールドを使用した場合、

"categories": {
  location: "london",
  type: "owned",
}

...私が抱えている問題はインデックス作成です。「場所」はユーザー定義のカテゴリであるため、単純にインデックスを作成する余裕はありません。10,000人のユーザーが家を分類する10,000の異なる方法を選択した場合、フィールドごとに1つずつ10,000のインデックスが必要になります。しかし、それを配列にすることで、配列フィールド自体に1つだけ必要になります。欠点は、$unwindステップです。以前、MapReduceでこれに遭遇しました。最後に実行したいのは、JavaScriptのForEachループで、配列を循環させることができる場合です。本当に必要なのは、はるかに高速であるため、名前でフィールドを除外することです。

今、これは私が探している果物をすでに知っているところではすべてうまくいっています、しかし私が知らなければ、それははるかに難しいです。ここで$unwindまたはForEach"people.John.items"を実行することはできません(私が見る限り)。できれば大喜びです。したがって、fruitの名前もユーザー定義であるため、次のように、それらも配列に変換する必要があるようです。

{
    "people" : {
        "John" : {
            "items" : [
                { k:"apples", v:100 },
                { k:"pears", v:200 },
                { k:"oranges", v:300 },
            ]
        },
    }
}

これで、果物(どの果物を探すべきかわからない場合)を場所ごとに合計することができます。

db.houses.aggregate([
    {
        $unwind: "$categories"
    },
    {
        $match: { "categories.k": "location" }
    },
    {
        $unwind: "$people.John.items" 
    },
    {
        $group: { // compound key - thanks to Jenna
            _id: { fruit:"$people.John.items.k", location:"$categories.v.v" },
            "numberOf": { $sum: 1 },
            "Total Fruit": { $sum: "$people.John.items.v" },
        }
    },
])

だから今私は2つの$unwindsをやっています。あなたがそれがグロテスクに非効率に見えると思っているなら、あなたは正しいでしょう。それぞれ10のカテゴリと10種類の果物を含む、10,000件の家のレコードしかない場合、このクエリの実行には30分かかります。OK、$unwindの前に$matchを移動すると状況が大幅に改善されることがわかりますが、出力が間違っています。すべてのカテゴリのエントリは必要ありません。「場所」のカテゴリだけを除外したいと思います。

于 2012-09-02T21:31:33.243 に答える
2

このコメントを作成したと思いますが、応答テキストボックスでフォーマットする方が簡単です。

{ _id: 1,
  house: "New York",
  people: {
      John: {
          items: {apples: 1, oranges:2}
      }
      Dave: {
          items: {apples: 2, oranges: 1}
      }
  }
}

{ _id: 2,
      house: "London",
      people: {
          John: {
              items: {apples: 3, oranges:2}
          }
          Dave: {
              items: {apples: 1, oranges:3}
          }
      }
}

私があなたの質問を理解していることを確認するために、これはあなたが達成しようとしていることですか?

{location: "New York", johnFruit:3}
{location: "London", johnFruit: 5}

カテゴリはhouseの下にネストされていないため、「house.categories.k」でグループ化することはできませんが、$groupの_idに複合キーを使用して次の結果を得ることができます。

{ $group: _id: {house: "$House", category: "$categories.k"} 

「k」には、おそらくグループ化しようとしている情報は含まれていません。また、「categories.k.type」の場合、typeはkの値であるため、この構文は使用できません。「categories.vd」でグループ化する必要があります。

現在のスキーマでは、$ unwind、$ project、場合によっては$ match、最後に$ groupを使用してこの集計を実行できる可能性がありますが、コマンドはきれいではありません。可能であれば、この集計をはるかに簡単にするためにデータを再構築することを強くお勧めします。スキーマについてサポートが必要な場合は、お知らせください。

于 2012-08-31T18:14:07.050 に答える
0

これが可能な解決策かどうかはわかりませんが、distinct()を使用して異なる場所の数を決定することから集計プロセスを開始し、場所ごとに個別の集計コマンドを実行するとどうなりますか?Distinct()は効率的ではないかもしれませんが、後続のすべての集計で$ matchを使用できるため、カテゴリのインデックスを使用できます。同じロジックを使用して、「categories.type」の果物を数えることができます。

{
    "_id" : 1,
    "house" : "New York",
    "people" : {
        "John" : [{"k" : "apples","v" : 1},{"k" : "oranges","v" : 2}],
        "Dave" : [{"k" : "apples","v" : 2},{"k" : "oranges","v" : 1}]
    },
    "categories" : [{"location" : "central"},{"type" : "rented"}]
}
{
    "_id" : 2,
    "house" : "London",
    "people" : {
        "John" : [{"k" : "apples","v" : 3},{"k" : "oranges","v" : 2}],
        "Dave" : [{"k" : "apples","v" : 3},{"k" : "oranges","v" : 1}]
    },
    "categories" : [{"location" : "suburb"},{"type" : "rented"}]
}
{
    "_id" : 3,
    "house" : "London",
    "people" : {
        "John" : [{"k" : "apples","v" : 0},{"k" : "oranges","v" : 1}],
        "Dave" : [{"k" : "apples","v" : 2},{"k" : "oranges","v" : 4}]
    },
    "categories" : [{"location" : "central"},{"type" : "rented"}]
}

「categories.location」の一意の値ごとにaggregate()コマンドを実行して、distinct()を実行し、結果を反復処理します。

db.agg.distinct("categories.location")
[ "central", "suburb" ]

db.agg.aggregate(
    {$match: {categories: {location:"central"}}}, //the index entry is on the entire 
    {$unwind: "$people.John"},                    //document {location:"central"}, so 
    {$group:{                                     //use this syntax to use the index
         _id:"$people.John.k", 
         "numberOf": { $sum: 1 },
         "Total Fruit": { $sum: "$people.John.v"}
        }
     }
 )


{
    "result" : [
        {
            "_id" : "oranges",
            "numberOf" : 2,
            "Total Fruit" : 3
        },
        {
            "_id" : "apples",
            "numberOf" : 2,
            "Total Fruit" : 1
        }
    ],
    "ok" : 1
}
于 2012-09-04T17:34:12.830 に答える