如何查询动态密钥 - mongodb模式设计

时间:2013-11-19 21:37:48

标签: mongodb database-design aggregation-framework

我有以下文件:

{
    _id     : 1,

    key1    : {
                  samekeyA : "value1",
                  samekeyB : "value2"
              },

    key2 :    {
                  samekeyA : "value3",
                  samekeyB : "value4"
              },

    key3 :    {
                  samekeyA : "value5",
                  samekeyB : "value6"
              }  
}

以上; key1key2key3用来证明我不知道完整密钥,除了它的前缀之外;但我认识的内键samekeyAsamekeyB。我需要查询:db.coll.find({"key*.samekeyA":"value1"})

我认为没有mongo方式 - 正则表达式关键查询? - 要实现这一点,那么有什么想法吗?我应该改造我的文件 - 树吗?

2 个答案:

答案 0 :(得分:2)

我建议重组模型。

{
    _id     : 1,

    data: [   {
                  key      : "key1",
                  samekeyA : "value1",
                  samekeyB : "value2"
              },

              {
                  key      : "key2",
                  samekeyA : "value3",
                  samekeyB : "value4"
              },

              {
                  key      : "key3",
                  samekeyA : "value5",
                  samekeyB : "value6"
              }  
    ]
}

查询:

db.col.find({"data.samekeyA": "value1"})

目前(也可能在未来),不可能在字段名中使用通配符查询MongoDB集合(感谢@gWiz)。

答案 1 :(得分:1)

使用MongoDB 3.4.4和更高版本:

db.coll.aggregate([
    { "$replaceRoot": {
        "newRoot": {
            "$arrayToObject": {
                "$filter": {
                    "input": { "$objectToArray": "$$ROOT" },
                    "as": "el",
                    "cond": {
                        "$eq": [
                            "$$el.v.samekeyA",
                            "value1"
                        ]
                    }
                }
            }
        }   
    } }
])

上述管道将产生最终输出

{
    "key1" : {
        "samekeyA" : "value1",
        "samekeyB" : "value2"
    }
}

说明

可以分解管道以显示每个操作员的结果。

$objectToArray

$objectToArray 使您可以使用动态键转换根文档 (由系统变量 $$ROOT 表示)放入一个数组,该数组包含原始文档中每个字段/值对的元素。返回数组中的每个元素都是一个包含两个字段k和v的文档。 在 $project 阶段仅由操作员运行管道

db.coll.aggregate([
    { "$project": {
        "keys": { "$objectToArray": "$$ROOT" }
    } }
])

收益

{
    "_id" : 1,
    "keys" : [ 
        {
            "k" : "_id",
            "v" : 1
        }, 
        {
            "k" : "key1",
            "v" : {
                "samekeyA" : "value1",
                "samekeyB" : "value2"
            }
        }, 
        {
            "k" : "key2",
            "v" : {
                "samekeyA" : "value3",
                "samekeyB" : "value4"
            }
        }, 
        {
            "k" : "key3",
            "v" : {
                "samekeyA" : "value5",
                "samekeyB" : "value6"
            }
        }
    ]
}

$filter

$filter 运算符充当 $objectToArray 运算符生成的数组的过滤机制,它通过选择数组的子集来工作根据指定条件返回 成为您的查询。

考虑以下管道,该管道返回与条件{ "samekeyA": "value1" }相匹配的键/值对的数组

db.coll.aggregate([
    { "$project": {
        "keys": { 
            "$filter": {
                "input": { "$objectToArray": "$$ROOT" },
                "as": "el",
                "cond": {
                    "$eq": [
                        "$$el.v.samekeyA",
                        "value1"
                    ]
                }
            }  
        }
    } }
])

产生

{
    "_id" : 1,
    "keys" : [ 
        {
            "k" : "key1",
            "v" : {
                "samekeyA" : "value1",
                "samekeyB" : "value2"
            }
        }
    ]
}

$arrayToObject

这将从

转换上面的过滤数组
[ 
    {
        "k" : "key1",
        "v" : {
            "samekeyA" : "value1",
            "samekeyB" : "value2"
        }
    }
]

使用动态键到原始文档

{
    "key1" : {
        "samekeyA" : "value1",
        "samekeyB" : "value2"
    }
}

所以要运行管道

db.coll.aggregate([
    { "$project": {
        "key": {
            "$arrayToObject": {
                "$filter": {
                    "input": { "$objectToArray": "$$ROOT" },
                    "as": "el",
                    "cond": {
                        "$eq": [
                            "$$el.v.samekeyA",
                            "value1"
                        ]
                    }
                }
            }
        }   
    } }
])

会产生

{
    "_id" : 1,
    "key" : {
        "key1" : {
            "samekeyA" : "value1",
            "samekeyB" : "value2"
        }
    }
}

$replaceRoot

这会将筛选后的动态密钥文档提升到最高级别,并替换所有其他字段。该操作将替换输入文档中的所有现有字段,包括_id字段。

从本质上讲,这可以转换上面的文档

{
    "_id" : 1,
    "key" : {
        "key1" : {
            "samekeyA" : "value1",
            "samekeyB" : "value2"
        }
    }
}

达到所需的最终输出

{
    "key1" : {
        "samekeyA" : "value1",
        "samekeyB" : "value2"
    }
}