user3225768
user3225768

Reputation: 51

How to get just the desired field from an array of sub documents in Mongodb using Java

I have just started using Mongo Db . Below is my data structure .

It has an array of skillID's , each of which have an array of activeCampaigns and each activeCampaign has an array of callsByTimeZone.

What I am looking for in SQL terms is :

Select activeCampaigns.callsByTimeZone.label,
       activeCampaigns.callsByTimeZone.loaded 
from X 
where skillID=50296 and activeCampaigns.campaign_id= 11371940 
      and activeCampaigns.callsByTimeZone='PT'

The output what I am expecting is to get

{"label":"PT", "loaded":1 }

The Command I used is

db.cd.find({ "skillID" : 50296 , "activeCampaigns.campaignId" : 11371940,
"activeCampaigns.callsByTimeZone.label" :"PT" },
{ "activeCampaigns.callsByTimeZone.label" : 1 , 
"activeCampaigns.callsByTimeZone.loaded" : 1 ,"_id" : 0})

The output what I am getting is everything under activeCampaigns.callsByTimeZone while I am expecting just for PT

DataStructure :

{
   "skillID":50296,
   "clientID":7419,
   "voiceID":1,
   "otherResults":7,
   "activeCampaigns":
   [{
       "campaignId":11371940,
       "campaignFileName":"Aaron.name.121.csv",
       "loaded":259,
       "callsByTimeZone":
       [{
           "label":"CT",
           "loaded":6
       },
       {
           "label":"ET",
           "loaded":241
       },
       {
           "label":"PT",
           "loaded":1
       }]
   }]
}

I tried the same in Java.

QueryBuilder query = QueryBuilder.start().and("skillID").is(50296)
                    .and("activeCampaigns.campaignId").is(11371940)
                    .and("activeCampaigns.callsByTimeZone.label").is("PT");

BasicDBObject fields = new BasicDBObject("activeCampaigns.callsByTimeZone.label",1)
                    .append("activeCampaigns.callsByTimeZone.loaded",1).append("_id", 0);

DBCursor cursor = coll.find(query.get(), fields);
String campaignJson = null;
while(cursor.hasNext()) {
    DBObject campaignDBO = cursor.next();
    campaignJson = campaignDBO.toString();
    System.out.println(campaignJson);
}

the value obtained is everything under callsByTimeZone array. I am currently parsing the JSON obtained and getting only PT values . Is there a way to just query the PT fields inside activeCampaigns.callsByTimeZone .

Thanks in advance .Sorry if this question has already been raised in the forum, I have searched a lot and failed to find a proper solution. Thanks in advance.

Upvotes: 0

Views: 1302

Answers (2)

Trisha
Trisha

Reputation: 3931

There are several ways of doing it, but you should not be using String manipulation (i.e. indexOf), the performance could be horrible.

The results in the cursor are nested Maps, representing the document in the database - a Map is a good Java-representation of key-value pairs. So you can navigate to the place you need in the document, instead of having to parse it as a String. I've tested the following and it works on your test data, but you might need to tweak it if your data is not all exactly like the example:

while (cursor.hasNext()) {
    DBObject campaignDBO = cursor.next();
    List callsByTimezone = (List) ((DBObject) ((List) campaignDBO.get("activeCampaigns")).get(0)).get("callsByTimeZone");
    DBObject valuesThatIWant;
    for (Object o : callsByTimezone) {
        DBObject call = (DBObject) o;
        if (call.get("label").equals("PT")) {
            valuesThatIWant = call;
        }
    }
}

Depending upon your data, you might want to add protection against null values as well.

The thing you were looking for ({"label":"PT", "loaded":1 }) is in the variable valueThatIWant. Note that this, too, is a DBObject, i.e. a Map, so if you want to see what's inside it you need to use get:

valuesThatIWant.get("label");  // will return "PT"
valuesThatIWant.get("loaded"); // will return 1

Because DBObject is effectively a Map of String to Object (i.e. Map<String, Object>) you need to cast the values that come out of it (hence the ugliness in the first bit of code in my answer) - with numbers, it will depend on how the data was loaded into the database, it might come out as an int or as a double:

String theValueOfLabel = (String) valuesThatIWant.get("label");   // will return "PT"
double theValueOfLoaded = (Double) valuesThatIWant.get("loaded"); // will return 1.0

I'd also like to point out the following from my answer:

((List) campaignDBO.get("activeCampaigns")).get(0)

This assumes that "activeCampaigns" is a) a list and in this case b) only has one entry (I'm doing get(0)).

You will also have noticed that the fields values you've set are almost entirely being ignored, and the result is most of the document, not just the fields you asked for. I'm pretty sure you can only define the top-level fields you want the query to return, so your code:

BasicDBObject fields = new BasicDBObject("activeCampaigns.callsByTimeZone.label",1)
    .append("activeCampaigns.callsByTimeZone.loaded",1)
    .append("_id", 0);

is actually exactly the same as:

BasicDBObject fields = new BasicDBObject("activeCampaigns", 1).append("_id", 0);

I think some of the points that will help you to work with Java & MongoDB are:

  • When you query the database, it will return you the whole document of the thing that matches your query, i.e. everything from "skillID" downwards. If you want to select the fields to return, I think those will only be top-level fields. See the documentation for more detail.
  • To navigate the results, you need to know that a DBObjects are returned, and that these are effectively a Map<String, Object> in Java - you can use get to navigate to the correct node, but you will need to cast the values into the correct shape.

Upvotes: 1

Ram Dwivedi
Ram Dwivedi

Reputation: 470

Replacing while loop from your Java code with below seems to give "PT" as output.

   `while(cursor.hasNext()) {
        DBObject campaignDBO = cursor.next();
        campaignJson = campaignDBO.get("activeCampaigns").toString();
        int labelInt = campaignJson.indexOf("PT", -1);
        String label = campaignJson.substring(labelInt, labelInt+2);
        System.out.println(label);
      }`

Upvotes: 0

Related Questions