How to query a dynamic key - mongodb schema design

前端 未结 2 814
傲寒
傲寒 2020-12-11 13:09

I have the following document:

{
    _id     : 1,

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


        
相关标签:
2条回答
  • 2020-12-11 13:32

    Using MongoDB 3.4.4 and newer versions:

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

    The above pipeline will yield the final output

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

    Explanations

    The pipeline can be decomposed to show each individual operator's results.

    $objectToArray

    $objectToArray enables you to transform the root document with dynamic keys (denoted by the system variable $$ROOT) into an array that contains a element for each field/value pair in the original document. Each element in the return array is a document that contains two fields k and v. Running the pipeline with just the operator in a $project stage

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

    yields

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

    The $filter operator acts as a filtering mechanism for the array produced by the $objectToArray operator, works by selecting a subset of the array to return based on the specified condition which becomes your query.

    Consider the following pipeline which returns an array of the key/value pair that matches the condition { "samekeyA": "value1" }

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

    which yields

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

    $arrayToObject

    This will transform the filtered array above from

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

    to the original document with the dynamic key

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

    so running the pipeline

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

    will produce

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

    $replaceRoot

    This will promote the filtered dynamic key document to the top level and replaces all other fields. The operation replaces all existing fields in the input document, including the _id field.

    Essentially this transforms the above document

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

    to the desired final output

    {
        "key1" : {
            "samekeyA" : "value1",
            "samekeyB" : "value2"
        }
    }
    
    0 讨论(0)
  • 2020-12-11 13:37

    I'd recommend restructuring the model.

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

    And the query:

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

    At the moment (and probably in the future, too) it's not possible to query MongoDB collections with wildcards in fieldnames (thanks to @gWiz).

    0 讨论(0)
提交回复
热议问题