2013-06-12 87 views
1

首先,對我的可憐的英語感到抱歉。Mongodb聚合過濾器像子文檔

如果我們有以下MongoDB中的文檔,

測試數據

{id:1, filter:{f1:'v1-1', f2:'v2-1', f3:['v3-1', 'v3-3']}} 
{id:2, filter:{f1:'v1-1', f2:'v2-2', f3:['v3-2', 'v3-3']}} 
{id:3, filter:{f1:'v1-1', f2:'v2-2', f3:['v3-1', 'v3-3']}} 

準備收集

db.test.drop() 
db.test.insert({id:1, filter:{f1:'v1-1', f2:'v2-1', f3:['v3-1', 'v3-3']}}) 
db.test.insert({id:2, filter:{f1:'v1-1', f2:'v2-2', f3:['v3-2', 'v3-3']}}) 
db.test.insert({id:3, filter:{f1:'v1-1', f2:'v2-2', f3:['v3-1', 'v3-3']}}) 

你可以考慮過濾領域爲用於過濾功能例如,在許多購物網站上,他們會告訴你有多少LE D電視和現場多少臺液晶電視。

我想使用MongoDB來計算每個過濾器選項的多少個文檔(包括數組字段中的每個項目),預期結果如下。

預期結果

[ 
    { 
     _id : { key: 'f1', value: 'v1-1' }, count: 3 
    }, 
    { 
     _id : { key: 'f2', value: 'v2-1' }, count: 1 
    }, 
    { 
     _id : { key: 'f2', value: 'v2-2' }, count: 2 
    }, 
    { 
     _id : { key: 'f3', value: 'v3-1' }, count: 2 
    }, 
    { 
     _id : { key: 'f3', value: 'v3-2' }, count: 1 
    }, 
    { 
     _id : { key: 'f3', value: 'v3-3' }, count: 3 
    } 
] 

這很容易使用的map/reduce得到結果,

的Map/Reduce的解決方案

map = function() { 
    for (k in this.filter) { 
     if (this.filter[k] instanceof Array) { 
      for (j in this.filter[k]) { 
       emit({ key: k, value: this.filter[k][j]}, 1); 
      } 
     } else { 
      emit({ key: k, value: this.filter[k]}, 1); 
     } 
    } 
} 

reduce = function (k, values) { 
    result = 0; 
    values.forEach(function(v) { result += v; }); 
    return result; 
} 

db.test.mapReduce(map, reduce, {out:{inline:1}}) 

但與性能問題map/reduce,它不能用於實時查詢。如果添加一些查詢條件,結果集可能會更改,所以我無法將地圖/縮減結果保存到另一個集合中以進行實時查詢。

我可以使用聚合框架來計算計數一個過濾器,

只有一個過濾器聚合解決方案

db.test.aggregate(
{$project: {"filter.f2":1, "_id":0}}, 
{$group: {"_id": {"key": {$ifNull: [null, "f2"]}, "value":"$filter.f2"}, "count" : {$sum: 1}}} 
) 

[ 
    { 
     "_id" : { "key" : "f2", "value" : "v2-2" }, "count" : 2 
    }, 
    { 
     "_id" : { "key" : "f2", "value" : "v2-1" }, "count" : 1 
    } 
] 

但我不知道該怎麼做了所有的過濾器選項。任何想法?

回答

1

如果你改變你的數據結構是這樣的,請注意,所有的值是數組,即使單值的:

{ 
    _id: 1, 
    filters: [{ 
    key: 'f1', 
    values: ['v1-1'] 
    },{ 
    key: 'f2', 
    values: ['v2-1'] 
    },{ 
    key: 'f3', 
    values: ['v3-1', 'v3-3'] 
    }] 
} 
{ 
    _id: 2, 
    filters: [{ 
    key: 'f1', 
    values: ['v1-1'] 
    },{ 
    key: 'f2', 
    values: ['v2-2'] 
    },{ 
    key: 'f3', 
    values: ['v3-2', 'v3-3'] 
    }] 
} 
{ 
    _id: 3, 
    filters: [{ 
    key: 'f1', 
    values: ['v1-1'] 
    },{ 
    key: 'f2', 
    values: ['v2-2'] 
    },{ 
    key: 'f3', 
    values: ['v3-1', 'v3-3'] 
    }] 
} 

你可以做一個聚合函數是這樣的:

db.test.aggregate({ 
    $unwind: "$filters" 
},{ 
    $project: { 
    _id: 1, 
    key: "$filters.key", 
    values: "$filters.values" 
    } 
},{ 
    $unwind: "$values" 
},{ 
    $group: { 
    _id: { 
     $concat: ["$key","|","$values"] 
    }, 
    count: { $sum: 1 } 
    } 
}) 

如果你願意,你可以跳過這個項目的步驟,我只是把它放在那裏作爲一個很好的東西。不管怎樣,你都需要兩次放鬆。

+0

很好的答案,謝謝!我使用的是mongodb 2.2,所以我需要在$ group部分使用'_id:{key:「$ key」,values:「$ values」}'。 – jxie