鍍金池/ 問答/Java  數(shù)據(jù)庫/ 關(guān)于一種兩兩提及的查詢需求 什么數(shù)據(jù)庫支持的比較好?

關(guān)于一種兩兩提及的查詢需求 什么數(shù)據(jù)庫支持的比較好?

需求

需要查詢統(tǒng)計(jì)提到某一品牌的時(shí)候 同時(shí)提到了哪些其他品牌
目前在MongoDB中是這樣保存品牌的
"brands" : [ 
      "西門子", 
      "ABB", 
      "GE"
  ],
  

顯然這種結(jié)構(gòu)很難滿足上述查詢需求 需要使用下面的這種結(jié)構(gòu) 才能支持這種查詢需求

"brand_and_mentioned_brands":[
  {
    "main_brand": "西門子", 
    "mentioned_brands": ["ABB","GE"]
  },
  {
    "main_brand": "ABB",
    "mentioned_brands": ["西門子","GE"]
  },
  {
    "main_brand": "GE",
    "mentioned_brands": ["西門子","ABB"]
  }
]

不知道 除了MongoDB外 其他數(shù)據(jù)庫 如ES等 對(duì)這種查詢需求情況的支持怎么樣?

回答
編輯回答
熊出沒

我的理解這算是數(shù)據(jù)分析的需求了,OLAP的需求依賴一條數(shù)據(jù)庫查詢直接產(chǎn)出結(jié)果不是很容易,如果是SQL可能會(huì)考慮存儲(chǔ)過程,MongoDB可選的方法有Map/Reduce和Aggregation,優(yōu)先選擇后者。性能問題在這里先不討論,先看看是否能達(dá)到你想要的效果。第二種數(shù)據(jù)結(jié)構(gòu)應(yīng)該沒有問題,第一種結(jié)構(gòu)我的解決方案如下:

db.test.aggregate([
    {$project: {brands: "$brands", brands2: "$brands"}},
    {$unwind: "$brands"},
    {$unwind: "$brands2"},
    {$project: {pair: ["$brands", "$brands2"]}},
    {$group: {_id: "$pair", count: {$sum: 1}}}
]);

這種方式先復(fù)制一個(gè)brands出來,然后做$unwind相當(dāng)于brands集合自己與自己排列,而你需要的是組合。比如你的示例數(shù)據(jù):

{"brands" : [ "西門子", "ABB", "GE" ]}

出來的結(jié)果是:

{ "_id" : [ "GE", "GE" ], "count" : 1 }
{ "_id" : [ "GE", "ABB" ], "count" : 1 }
{ "_id" : [ "GE", "西門子" ], "count" : 1 }
{ "_id" : [ "ABB", "ABB" ], "count" : 1 }
{ "_id" : [ "ABB", "西門子" ], "count" : 1 }
{ "_id" : [ "ABB", "GE" ], "count" : 1 }
{ "_id" : [ "西門子", "GE" ], "count" : 1 }
{ "_id" : [ "西門子", "ABB" ], "count" : 1 }
{ "_id" : [ "西門子", "西門子" ], "count" : 1 }

有些額外的數(shù)據(jù),比如[ "ABB", "ABB" ][ "西門子", "ABB" ]/[ "ABB", "西門子" ]。我暫時(shí)還沒想到很好的辦法直接在aggregation pipeline中直接過濾掉這些數(shù)據(jù),不過應(yīng)該不影響你使用。如果有想到更徹底的辦法我再回來補(bǔ)充。

補(bǔ)充回答

求助了一下場(chǎng)外觀眾,aggregation確實(shí)很強(qiáng)大。你可能需要查一下:$map, $reduce, $let, $range這些操作符的用法

db.test.aggregate({
    $project: {
        tuples: {
            $reduce: {
                initialValue: [],
                input: {
                    $range: [0, {
                        $subtract: [{
                            $size: "$brands"
                        }, 1]
                    }]
                },
                in: {
                    $let: {
                        vars: {
                            i1: "$$this"
                        },
                        in: {
                            $concatArrays: ["$$value", {
                                $map: {
                                    input: {
                                        $range: [{
                                            $add: [1, "$$i1"]
                                        }, {
                                            $size: "$brands"
                                        }]
                                    },
                                    in: [{
                                        $arrayElemAt: ["$brands", "$$i1"]
                                    }, {
                                        $arrayElemAt: ["$brands", "$$this"]
                                    }]
                                }
                            }]
                        }
                    }
                }
            }
        }
    }
}, {
    $unwind: "$tuples"
}, {
    $sortByCount: {
        $setUnion: "$tuples"
    }
})

這個(gè)管道操作本質(zhì)上的意義就是:

for(var i = 0; i < array.length - 1; i++)
  for(var j = i + 1; j < array.length - 1; j++) {...}

執(zhí)行結(jié)果:

{ "_id" : [ "GE", "西門子" ], "count" : 1 }
{ "_id" : [ "ABB", "GE" ], "count" : 1 }
{ "_id" : [ "ABB", "西門子" ], "count" : 1 }
2017年4月14日 03:58