简体   繁体   中英

How to fetch single matched document from nested array mongodb?

I have nested array object in my MongoDB document, but the predicate is not return as expected, which in this case I only want matched document only.

Document Structure

{
"_id": {
    "$oid": "5fddb34848be35283c36955b"
},
"projectId": {
    "$oid": "5fddb30d48be35283c36955a"
},
"urls": [
    {
        "_id": {
            "$oid": "5fddb34848be35283c36955c"
        },
        "group": "User",
        "data": [
            {
                "option": {
                    "applyDifferentContentType": false,
                    "allowInjection": false
                },
                "_id": {
                    "$oid": "5fddad1e48be35283c369558"
                },
                "url": "/users/create",
                "method": "patch",
                "headers": {
                    "Content-Type": "multipart/form-data"
                }
            },
            {
                "option": {
                    "applyDifferentContentType": false,
                    "allowInjection": false
                },
                "_id": {
                    "$oid": "5fddad1e48be35283c369558"
                },
                "url": "/users/update",
                "method": "post",
                "headers": {
                    "Content-Type": "application/json"
                }
            },
            {
                "option": {
                    "applyDifferentContentType": false,
                    "allowInjection": false
                },
                "_id": {
                    "$oid": "5fddad1e48be35283c369558"
                },
                "url": "/users/delete",
                "method": "post",
                "headers": {
                    "Content-Type": "application/json"
                }
            }
        ]
    },
    {
        "_id": {
            "$oid": "5fddb34f48be35283c36955d"
        },
        "group": "Project",
        "data": [
            {
                "option": {
                    "applyDifferentContentType": true,
                    "allowInjection": false
                },
                "_id": {
                    "$oid": "5fddad1e48be35283c369558"
                },
                "url": "/project/create",
                "method": "post",
                "headers": {
                    "Content-Type": "application/json"
                }
            },
            {
                "option": {
                    "applyDifferentContentType": false,
                    "allowInjection": false
                },
                "_id": {
                    "$oid": "5fddad1e48be35283c369558"
                },
                "url": "/projects/update",
                "method": "post",
                "headers": {
                    "Content-Type": "application/url-encoded"
                }
            }
        ]
    },
    {
        "_id": {
            "$oid": "5fddb37d48be35283c36955e"
        },
        "group": "Contact",
        "data": [
            {
                "option": {
                    "applyDifferentContentType": false,
                    "allowInjection": false
                },
                "_id": {
                    "$oid": "5fddad1e48be35283c369558"
                },
                "url": "/contact/create",
                "method": "post",
                "headers": {
                    "Content-Type": "multipart/form-data"
                }
            }
        ]
    }
],
"__v": 0 }

Fetch Query

const result = await URLPayload.find({ 
  "projectId": projectId,
  "urls.data": {
    $elemMatch: {
      "_id": dataId
    }
  }
})
.lean();

projectId => 5fddb30d48be35283c36955a
dataId => 5fddad1e48be35283c369558

But above predicate is not giving expected result. How to return only matched single document from nested array object?

I have nested array object in my MongoDB document, but the predicate is not return as expected, which in this case I only want matched document only.

I'm not used working with $oid inside the _id element but this should work.

    const ids = {
        projectId: "5fddb30d48be35283c36955a",
        urlId: "5fddb34848be35283c36955c",
        dataId: "5fddad1e48be35283c369558"
    }

    await URLPayload.findOne({ "projectId.$oid": ids.projectId },
        {
            "$elemMatch": { "urls.$[url].data": { "_id.$oid": ids.dataId } }
        }, {
        "arrayFilters": [
            { "url._id.$oid": ids.urlId }
        ]
    }, (err, urlPayloadResult) => {
        if (err) {
            console.log(err)
        } else {
            console.log({ message: urlPayloadResult })
        }
    })

If not work in your case just remove the ".$oid" element.

Let me know if this work.

You can use aggregation pipeline,

  • make your you have to convert projectId and dataId input fields string type to object type using mongoose.Types.ObjectId
  • $match your conditions,
  • $project to show required documents
  • $reduce to iterate loop of urls array
  • $filter to iterate loop of urls.data array and filter matching _id ,
  • $first will return object from the array, you can use $arrayElemAt instead of $first for older version of MongoDB
const result = await URLPayload.aggregate([
  {
    $match: {
      projectId: mongoose.Types.ObjectId(projectId),
      "urls.data._id": mongoose.Types.ObjectId(dataId)
    }
  },
  {
    $project: {
      urlData: {
        $reduce: {
          input: "$urls",
          initialValue: {},
          in: {
            $first: {
              $filter: {
                input: "$$this.data",
                cond: { $eq: ["$$this._id", mongoose.Types.ObjectId(dataId)] }
              }
            }
          }
        }
      }
    }
  }
])

Playground

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