1

I have a chats collection witch sample document

{
    "_id" : ObjectId("57f95e8e385bb61c5cf2cd18"),
    //some other fields
    "messages" : [
            {
                    "sender" : "57ec1aaa0ffe16123439d52b",
                    "message" : "Hello!",
                    "sentTime" : "Oct 13, 2016 1:51:31 AM"
            },
            {
                    "sender" : "57ec1aaa0ffe16123439d52b",
                    "message" : "Hello!",
                    "sentTime" : "Oct 13, 2016 1:51:33 AM"
            }
    ]
}

I want to search the collection on the basis of _id field and only get the data of the messages sub-document as the query result using java driver for MongoDB as I want to Map to POJO array using Gson.

Desired Output:

[
        {
                "sender" : "57ec1aaa0ffe16123439d52b",
                "message" : "Hello!",
                "sentTime" : "Oct 13, 2016 1:51:31 AM"
        },
        {
                "sender" : "57ec1aaa0ffe16123439d52b",
                "message" : "Hello!",
                "sentTime" : "Oct 13, 2016 1:51:33 AM"
        }
]

How do I go with this using Java driver for MongoDB

kritya
  • 3,312
  • 7
  • 43
  • 60

2 Answers2

0

We need to specify query and list of fields, here is an example of how to do it:

DB db = //get db

DBObject object = new BasicDBObject();
object.put("_id", new ObjectId("<some id>"));
DBObject fields = new BasicDBObject();
fields.put("messages", 1);
DBObject result = db.getCollection("test").findOne(object, fields);
System.out.println(result.get("messages"));
Darshan Mehta
  • 30,102
  • 11
  • 68
  • 102
  • That won't work. It gives a document with id field and messages field in it. The output is same as the first json i gave in the question except all other fields. But i need it to be just the sub document array like in the 2nd json i posted in question. Thanks – kritya Oct 13 '16 at 07:23
  • It's not possible to get just a sub document part. We can specify the fields like `messages.sender` but can't directly get that part. Also, you can't specify include and exclude fields at the same time and we will get id returned anyway. So this is as close as we can get. – Darshan Mehta Oct 13 '16 at 08:05
  • Then is it possible that I can sort the messages array on the basis of lets say sentTime? – kritya Oct 13 '16 at 08:10
  • You can implement something like this : http://stackoverflow.com/questions/13449874/how-to-sort-array-inside-collection-record-in-mongodb. However, it won't give you the exact format. – Darshan Mehta Oct 13 '16 at 08:20
  • Not related, but how does the performance of using aggregation compare with the find command in your answer ? as I am using it on servlets. Thanks – kritya Oct 13 '16 at 09:09
0

maybe it's a bit ugly, but it will work.

chats.aggregate(Arrays.asList(
    match(eq("_id", <your id here> )),
    unwind("$messages"),
    project(fields(excludeId(), 
                   computed("sender", "$messages.sender"), 
                   computed("message", "$messages.message"),
                   computed("sentTime", "$messages.sentTime")))
    )).forEach(printBlock);

printBlock just prints out each document (you can collect them to array there).

    Block<Document> printBlock = new Block<Document>() {
        @Override
        public void apply(final Document document) {
            System.out.println(document.toJson());
        }
    };  
Natalja Olefire
  • 442
  • 5
  • 9
  • I believe I can't hard card those. Sorry but I haven't used the aggregations framework. Can't I return the complete object? Also, how is the performance of aggregation compared to find for the same ? – kritya Oct 13 '16 at 16:37
  • Method aggregate will return you a AggregateIterable object, it has regular iterate method that can be used instead of forEach. As about performance, you will match only one document in the very first stage of aggregation, and only this one doc will go down the aggregation pipeline. So, in this particular case it shouldn't be a big problem. – Natalja Olefire Oct 13 '16 at 17:27
  • What we do there is like this. First, we select one object using "match", this single object goes down to the aggregation pipeline, than we "unwind" messages - it means that every message from our messages array will be placed in a separate document in the output (from official doc: "The $unwind pipeline stage deconstructs an array field from the input documents to output a document for each element"). And than from this resulting doc we take only message fields and map them to desired names using "project". – Natalja Olefire Oct 13 '16 at 17:28
  • And if you're new to aggregation, you need imports to run this code. import static com.mongodb.client.model.Aggregates.match; import static com.mongodb.client.model.Aggregates.project; import static com.mongodb.client.model.Aggregates.unwind; import static com.mongodb.client.model.Filters.eq; import static com.mongodb.client.model.Projections.computed; import static com.mongodb.client.model.Projections.excludeId; import static com.mongodb.client.model.Projections.fields; import java.util.Arrays; import org.bson.Document; import com.mongodb.Block; – Natalja Olefire Oct 13 '16 at 17:29
  • Thanks a lot for that much info! But my problem is not with the print block forEach but the only problem with that code I have is that I can't hard code those computed functions. As sometime later I might want to add some more keys or change something. That was what I meant by hard coding. Isn't there a work around for that. That was my question. Thanks again. – kritya Oct 13 '16 at 17:33