关于一种两两提及的查询需求 什么数据库支持的比较好?

需求

需要查询统计提到某一品牌的时候 同时提到了哪些其他品牌
目前在MongoDB中是这样保存品牌的
"brands" : [ 
      "西门子", 
      "ABB", 
      "GE"
  ],
  

显然这种结构很难满足上述查询需求 需要使用下面的这种结构 才能支持这种查询需求

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

不知道 除了MongoDB外 其他数据库 如ES等 对这种查询需求情况的支持怎么样?

阅读 1.8k
1 个回答

我的理解这算是数据分析的需求了,OLAP的需求依赖一条数据库查询直接产出结果不是很容易,如果是SQL可能会考虑存储过程,MongoDB可选的方法有Map/Reduce和Aggregation,优先选择后者。性能问题在这里先不讨论,先看看是否能达到你想要的效果。第二种数据结构应该没有问题,第一种结构我的解决方案如下:

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

这种方式先复制一个brands出来,然后做$unwind相当于brands集合自己与自己排列,而你需要的是组合。比如你的示例数据:

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

出来的结果是:

{ "_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 }

有些额外的数据,比如[ "ABB", "ABB" ][ "西门子", "ABB" ]/[ "ABB", "西门子" ]。我暂时还没想到很好的办法直接在aggregation pipeline中直接过滤掉这些数据,不过应该不影响你使用。如果有想到更彻底的办法我再回来补充。

补充回答

求助了一下场外观众,aggregation确实很强大。你可能需要查一下:$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"
    }
})

这个管道操作本质上的意义就是:

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

执行结果:

{ "_id" : [ "GE", "西门子" ], "count" : 1 }
{ "_id" : [ "ABB", "GE" ], "count" : 1 }
{ "_id" : [ "ABB", "西门子" ], "count" : 1 }
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题