javamongodbmongo-collection

MongoCollection : How to get value of nested key


I have some mongo data that looks like this

{
    "_id": {
        "$oid": "5984cfb276c912dd03c1b052"
    },
    "idkey": "123",
    "objects": [{
        "key1": "481334",
        "key2": {
            "key3":"val3",
            "key4": "val4"
        }
    }]

}

I want to know what the value of key4 is. I also need to filter the results byidkey and key1. So I tried

doc = mongoCollection.find(and(eq("idKey", 123),eq("objects.key1", 481334))).first();

and this works. But i want to check the value of key4 without having to unwrap the entire object. Is there some query i can perform that gives me just the value of key4? Note that I can update the value of key4 as

mongoCollection.updateOne(and(eq("idKey", 123), eq("objects.key1", 481334)),Updates.set("objects.$.key2.key4", "someVal"));

Is there a similar query i can run just to get the value of key4?

Upadte

thanks a lot @dnickless for your help. I tried both of your suggestions but i am getting null. Here is what i tried

existingDoc = mongoCollection.find(and(eq("idkey", 123), eq("objects.key1", 481334))).first();

this gives me

Document{{_id=598b13ca324fb0717c509e2d, idkey="2323", objects=[Document{{key1="481334", key2=Document{{key3=val3, key4=val4}}}}]}}

so far so good. next i tried

mongoCollection.updateOne(and(eq("idkey", "123"), eq("objects.key1", "481334")),Updates.set("objects.$.key2.key4", "newVal"));

now i tried to get the updated document as

updatedDoc = mongoCollection.find(and(eq("idkey", "123"),eq("objects.key1","481334"))).projection(Projections.fields(Projections.excludeId(), Projections.include("key4", "$objects.key2.key4"))).first();

for this i got

Document{{}}

and finally i tried

updatedDoc = mongoCollection.aggregate(Arrays.asList(Aggregates.match(and(eq("idkey", "123"), eq("objects.key1", "481334"))),
                            Aggregates.unwind("$objects"), Aggregates.project(Projections.fields(Projections.excludeId(), Projections.computed("key4", "$objects.key2.key4")))))
                    .first();

and for this i got

Document{{key4="newVal"}}

so i'm happy :) but can you think of a reason why the firs approach did not work?

Final answer

thanks for the update @dnickless

document = collection.find(and(eq("idkey", "123"), eq("objects.key1", "481334"))).projection(fields(excludeId(), include("key4", "objects.key2.key4"))).first();


Solution

  • Your data sample contains a lowercase "idkey" whereas your query uses "idKey". In my examples below, I use the lowercase version. Also you are querying for integers 123 and 481334 as opposed to strings which would be correct looking at your sample data. I'm going for the string version with my below code in order to make it work against the provided sample data.

    You have two options:

    Either you simply limit your result set but keep the same structure using a simple find + projection:

    document = collection.find(and(eq("idkey", "123"), eq("objects.key1", "481334"))).projection(fields(excludeId(), include("objects.key2.key4"))).first();
    

    Or, probably nicer in terms of output (not necessarily speed, though), you use the aggregation framework in order to really just get what you want:

    document = collection.aggregate(Arrays.asList(match(and(eq("idkey", "123"), eq("objects.key1", "481334"))), unwind("$objects"), project(fields(excludeId(), computed("key4", "$objects.key2.key4"))))).first();