如何查询动态键 - mongodb schema design

anv*_*rik 1 database-design mongodb aggregation-framework

我有以下文件:

{
    _id     : 1,

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

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

    key3 :    {
                  samekeyA : "value5",
                  samekeyB : "value6"
              }  
}
Run Code Online (Sandbox Code Playgroud)

以上; key1,key2并被key3用来证明我不知道完整的密钥,除了它的前缀;但内在的钥匙samekeyAsamekeyB我知道的那些。我需要查询,例如:db.coll.find({"key*.samekeyA":"value1"})

我认为没有一种 mongo 方式 - 正则表达式键查询? - 来实现这一点,所以有什么想法吗?我应该改造我的文档-tree-吗?

chr*_*dam 7

使用 MongoDB 3.4.4 及更新版本:

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

上述管道将产生最终输出

{
    "key1" : {
        "samekeyA" : "value1",
        "samekeyB" : "value2"
    }
}
Run Code Online (Sandbox Code Playgroud)

说明

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

$objectToArray

$objectToArray使您能够将具有动态键(由系统变量表示$$ROOT)的根文档转换为包含原始文档中每个字段/值对的元素的数组。返回数组中的每个元素都是一个包含两个字段 k 和 v 的文档。 在$project阶段中仅使用运算符运行管道

db.coll.aggregate([
    { "$project": {
        "keys": { "$objectToArray": "$$ROOT" }
    } }
])
Run Code Online (Sandbox Code Playgroud)

产量

{
    "_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"
            }
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

$filter

$filter操作者充当由所述产生的阵列的过滤机制$objectToArray操作,工程通过选择基于成为查询中指定的条件返回阵列的一个子集。

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

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

这产生

{
    "_id" : 1,
    "keys" : [ 
        {
            "k" : "key1",
            "v" : {
                "samekeyA" : "value1",
                "samekeyB" : "value2"
            }
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

$arrayToObject

这将从上面的过滤数组转换

[ 
    {
        "k" : "key1",
        "v" : {
            "samekeyA" : "value1",
            "samekeyB" : "value2"
        }
    }
]
Run Code Online (Sandbox Code Playgroud)

使用动态键转到原始文档

{
    "key1" : {
        "samekeyA" : "value1",
        "samekeyB" : "value2"
    }
}
Run Code Online (Sandbox Code Playgroud)

所以运行管道

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

会产生

{
    "_id" : 1,
    "key" : {
        "key1" : {
            "samekeyA" : "value1",
            "samekeyB" : "value2"
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

$replaceRoot

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

本质上,这转换了上述文档

{
    "_id" : 1,
    "key" : {
        "key1" : {
            "samekeyA" : "value1",
            "samekeyB" : "value2"
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

到所需的最终输出

{
    "key1" : {
        "samekeyA" : "value1",
        "samekeyB" : "value2"
    }
}
Run Code Online (Sandbox Code Playgroud)