2012-03-22 3 views
2

Mongo docs状態:Mongoのカスタムマルチキー

モンゴのマルチキー機能値の自動インデックス配列することができます。

いいですね。しかし、マルチキーに基づいてソートするのはどうですか?より具体的には、配列一致率に従ってコレクションをソートする方法は?例えば

、私はパターン[ 'fruit', 'citrus' ]とこのようになりますコレクション、持っている:

{ 
    title: 'Apples', 
    tags: [ 'fruit' ] 
}, 

{ 
    title: 'Oranges', 
    tags: [ 'fruit', 'citrus' ] 
}, 

{ 
    title: 'Potato', 
    tags: [ 'vegetable' ] 
} 

を今、私は、タグパターンに各エントリの割合を一致させるために応じて、コレクションをソートします。オレンジは最初に、リンゴは2番目に、ジャガイモは最後に来なければなりません。

最も効果的で簡単な方法は何ですか?

+0

あなたはソートの目的が何であるかをより明確にすることができますか?一致率に応じてソートするのですか?例えば配列のすべての要素(この場合は果物と柑橘類)に一致するすべての文書、次に果物に一致する文書、次に柑橘類​​だけの文書などがあります。 – Barrie

+1

@Barrie、はい、正確です。質問が更新されました。 – katspaugh

答えて

4

MongoDB 2.1以降、集約フレームワークを使用して同様の計算を行うことができます。構文は次の2つの理由から、マップ減らす方法よりもはるかに高速である必要があり

{ 
    "_id" : "Oranges", 
    "numTagMatches" : 2 
}, 
{ 
    "_id" : "Apples", 
    "numTagMatches" : 1 
} 

これを返す

db.fruits.aggregate(
    {$match : {tags : {$in : ["fruit", "citrus"]}}}, 
    {$unwind : "$tags"}, 
    {$group : {_id : "$title", numTagMatches : {$sum : 1}}}, 
    {$sort : {numTagMatches : -1}}) 

のようなものです。まず、実装がjavascriptではなく、ネイティブのC++であるためです。第2に、 "$ match"はまったくマッチしない項目をフィルタリングするためです(もしこれがあなたが望むものでないならば、 "$ match"部分を省略して "$ sum"部分をタグが「果物」または「柑橘類」に等しいかどうかに応じて1または0)。

ここで注意すべき点は、mongo 2.1はまだ製造には推奨されていないことです。実稼働中の場合は、2.2まで待つ必要があります。しかし、あなた自身で実験しているのであれば、集約フレームワークがより効果的でなければならないので、2.1で遊ぶことができます。

+0

ありがとう、matulef!構文も非常にいいです。 – katspaugh

2

注::以下の説明はMongo 2.0以前では必要です。それ以降のバージョンでは、新しい集約フレームワークを考慮する必要があります。

私たちが索引付けした入力文をあいまい一致させようとしているときに、同様の操作を行います。一致を得るたびにmap reduceを使用してオブジェクトIDを出力し、それらを合計することができます。結果をクライアントにロードし、最初に最も高い値で並べ替える必要があります。

db.plants.mapReduce(
    function() { 
     var matches = 0; 
     for (var i = 0; i < targetTerms.length; i++) { 
      var term = targetTerms[i]; 
      for (var j = 0; j < this.tags.length; j++) { 
       matches += Number(term === this.tags[j]); 
      } 
     } 
     emit(this._id, matches); 
    }, 

    function (prev, curr) { 
     var result = 0; 
     for (var i = 0; i < curr.length; i++) { 
      result += curr[i]; 
     } 
     return result; 
    }, 

    { 
     out: { inline: 1 }, 

     scope: { 
      targetTerms: [ 'fruit', 'oranges' ], 
     } 
    } 
); 

あなたは、彼らが上記のマップ機能で利用できるように{targetTerms: ['fruit', 'citrus' ]}としてコールを減らすマップでscopeパラメータを使用して['fruit', 'citrus' ]入力値を渡す必要があります。

+0

ありがとう、ニコラス! – katspaugh