简体   繁体   中英

Mongodb, reducing a list by control

I m building an application in which I use to manage projects. A projects looks like the following :

{
  "_id": ObjectId("..."),
  "title": "MySuperProject",
  "files": [
    {
      "title":"My skiing day !",
      "right":[{
        "role":"USER",
        "access":["read"]
      }]
    },
    {
      "title":"My little dog, so cute !",
      "right":[{
        "role":"OTHER",
        "access":["read"]
      }]
    }
  ]
}

We can see two different roles here : USER and OTHER .

When I get the above project with the USER role, I need to have the following representation, without the OTHER file :

{
  "_id": ObjectId("..."),
  "title": "MySuperProject",
  "files": [
    {
      "title":"My skiing day !",
      "right":{
        "role":"USER",
        "access":["read"]
      }
    }]
}

Does it exist a way to reduce a list inside of a document based on query or should I make it manually on the result ?

I m working on nodejs and mongoose .

Thanks for your help

EDIT : In fact the right key was an ARRAY

This is one of the classic use cases of the $redact stage. You could aggregate it as below:

var role = "USER";
var projectTitle = "MySuperProject";

db.t.aggregate([
  {
    $match: {
      "title":projectTitle
    }
  },
  {
    $redact: {
      $cond: [{
        $eq: [role, {
          $ifNull: ["$role", role]
        }]
      }, "$$DESCEND", "$$PRUNE"]
    }
  }
])

output:

{
        "_id" : 1,
        "title" : "MySuperProject",
        "files" : [
                {
                        "title" : "My skiing day !",
                        "right" : [
                                {
                                        "role" : "USER",
                                        "access" : [
                                                "read"
                                        ]
                                }
                        ]
                },
                {
                        "title" : "My little dog, so cute !",
                        "right" : [ ]
                }
        ]
}

At each level, a document is evaluated, only if the document at the particular level, returns true to the $cond presented by the $redact stage, we $$DESCEND into its sub-documents, else $$PRUNE .

It would list you all the files per project, and an array of access roles for each file. If you would want to exclude files that the "user" has no rights on, you could $redact again:

db.t.aggregate([
  {
    $match: {
      "title": projectTitle
    }
  },
  {
    $redact: {
      $cond: [{
        $eq: [role, {
          $ifNull: ["$role", role]
        }]
      }, "$$DESCEND", "$$PRUNE"]
    }
  },
  {
    $redact: {
      $cond: [{
        $gt: [{
          $size: {
            $ifNull: ["$right", [1]]
          }
        }, 0]
      }, "$$DESCEND", "$$PRUNE"]
    }
  },
])

output:

{
        "_id" : 1,
        "title" : "MySuperProject",
        "files" : [
                {
                        "title" : "My skiing day !",
                        "right" : [
                                {
                                        "role" : "USER",
                                        "access" : [
                                                "read"
                                        ]
                                }
                        ]
                }
        ]
}

The above approach avoids the costly $unwind stage. It is always advisable to take different approaches and see which one suits you the best.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM