0

I have a collection that has a sub-document in it and I'm trying to find an easy way to get an exact document.

{ 
    "_id" : ObjectId("59b0303bfe409a21fccc9523"), 
    "CreatedOn" : ISODate("2017-07-22T15:55:00.000+0000"), 
    "UpdatedOn" : ISODate("2017-07-22T15:55:00.000+0000"), 
    "Guid" : "4F1D7541-FF27-4FEF-9BC4-CF27D2CB92BA", 
    "Company" : "Demo Co.",
    "Departments" : [
        {
            "CreatedOn" : ISODate("2017-07-22T15:55:00.000+0000"), 
            "UpdatedOn" : ISODate("2017-07-22T15:55:00.000+0000"), 
            "Guid" : "D5950FC8-91B1-48A3-8B6A-F3A72FAC9175", 
            "Title" : "Execute" 
        }
    ]
}

what I'd like to do is get a certain department based on its guid but so far I'm not finding anything that is clean to get just the document I want.

I'd want to get a result like this:

{
    "CreatedOn" : ISODate("2017-07-22T15:55:00.000+0000"), 
    "UpdatedOn" : ISODate("2017-07-22T15:55:00.000+0000"), 
    "Guid" : "D5950FC8-91B1-48A3-8B6A-F3A72FAC9175", 
    "Title" : "Execute" 
}

I've been looking around the mongodb documentation but nothing is working so far, the closest I'm getting is using array_search, but I'd like to fetch it straight from mongo itself if possible to avoid hacking it with PHP.

Eman
  • 1,093
  • 2
  • 26
  • 49

3 Answers3

0

Perhaps something like this : db.collection.find( { "Departments.Guid": "some_value" } ) https://docs.mongodb.com/manual/tutorial/query-embedded-documents/

DrNio
  • 1,936
  • 1
  • 19
  • 25
  • I tried that and it doesn't work, it just return the parent document with everything in the child document, I just want one of the child documents – Eman Dec 03 '17 at 22:52
  • `db.collection.find( { "Departments.Guid": "some_value" } ).toArray()['Departments']` how about this ? the result of a `find` is a `cursor` so you have to add a little bit more logic – DrNio Dec 03 '17 at 23:17
  • in that case my PHP hack seems to be the best option then. – Eman Dec 03 '17 at 23:22
  • i wouldn't call it hack, perhaps `.map` could help https://docs.mongodb.com/manual/reference/method/js-cursor/ – DrNio Dec 03 '17 at 23:28
0

Assuming that the GUID is unique to the subdoc in the Departments array (i.e. looking for a GUID yields ONLY one possible match in a parent doc) but GUIDs might be the same across OTHER parent docs, then this produces the output result you seek. If GUID is unique across all arrays AND parent docs, well then this works as well.

Basically, we filter for Departments.Guid = target GUID (below is "myGUID"). This leaves us with an array of 0 or 1 elements, from which we extract the subdoc with $arrayElemAt and then "lift" that doc into position as the root doc (as opposed to assigning the doc to a named field). If no subdoc matches (array post-filter is length 0), then $arrayElemAt yields null and qq will not exist. We want to eliminate these docs before calling $replaceRoot which requires a real existing field to lift into the root.

db.foo.aggregate([
  {$project: { qq: {$arrayElemAt: [
                        { $filter: {
                            input: "$Departments",
                            as: "zz",
                            cond: {$eq: [ "$$zz.Guid", "myGUID" ]}
                            }}
                        , 0] } }}
  ,{$match: { qq: {$exists: true}} }
  ,{$replaceRoot: { newRoot: "$qq"} }
                ]);
Buzz Moschetti
  • 7,057
  • 3
  • 23
  • 33
0

You can try out this,

db.foo.find({"_id" : ObjectId("59b0303bfe409a21fccc9523")})
.map(function(u){
        return u.Departments
                .filter(p => p.Guid == "D5950FC8-91B1-48A3-8B6A-F3A72FAC9175")
    }
)

This will only return:

[
    [ 
        {
            "CreatedOn" : ISODate("2017-07-22T21:25:00.000+05:30"),
            "UpdatedOn" : ISODate("2017-07-22T21:25:00.000+05:30"),
            "Guid" : "D5950FC8-91B1-48A3-8B6A-F3A72FAC9175",
            "Title" : "Execute"
        }
    ]
]
Anirudh Bagri
  • 2,346
  • 1
  • 21
  • 33
  • Not a performant solution. The OP is trying to look up based on GUID, not _id. If you drop the predicate in find() altogether, the map() function in the javascript driver is client side, not server side, and will drag every record in the collection across the wire and exec the function. Lots of bytes and no oppty to use indexes (should that become desirable). – Buzz Moschetti Dec 04 '17 at 13:29
  • if that is the case, then we can replace find method with this db.foo.find( { "Departments.Guid": ""D5950FC8-91B1-48A3-8B6A-F3A72FAC9175"" } ) – Anirudh Bagri Dec 04 '17 at 13:48