I have the Java Mongo Collection in the following format.
{
"field1": ["f1","f2"],
"field2": ["g1","g2"],
"Ans": a1
}
{
"field1": ["f4","f5"],
"field2": ["g5","g6"],
"Ans": a2
}
{
"field1": ["f1","f6"],
"field2": ["g2","g3"],
"Ans": a3
}
I need to do the "OR" condition in field1 and field2 separately
From the result i need to do and condition to select the row.
For example: f1,f5,g2,g3 is the input i get.
i need to do or condtion in field 1 with f1,f5 so i will get all the three rows
i need to do or condtion in field 2 with g,g3 so i will get 1st and 3rd rows
while doing and condition of both i need to get only 1st and 3rd row.
Follwing is the code im using
DBObject query1 = new BasicDBObject("field1","f1");
DBObject query2 = new BasicDBObject("field1","f5");
BasicDBList condtionalOperator = new BasicDBList();
condtionalOperator.add(query1);
condtionalOperator.add(query2);
DBObject query = new BasicDBObject("$or", condtionalOperator);
Similar OR query for field 2 and And query for their result
I know its pretty long. Any one knows simple code than this?
(I'm responding using the Mongo shell JavaScript syntax, since it's a bit less verbose than Java. The equivalent Java should be straightforward to produce.)
db.collection.find({$and:[
{field1:{$in:["f1","f5"]}},
{field2:{$in:["g1","g3"]}}
]});
The $in operator basically does an OR operation, so the first operand of the $and is essentially equivalent to {$or:[{field1:"f1"},{field1:"f5"}]}.
Related
I have a node called quotes in Firebase. I'm facing issues while fetching data in Android for a particular range. I want to fetch 3 continues quotes id starting from 2. Here is my query database:
"quotes" : {
"-L75elQJaD3EYPsd4oWS" : {
"authorName" : "Hellen v",
"famousQuote" : "When one door of happiness closes, another opens; but often we look so long at the closed door that we do not see the one which has been opened for us.",
"id" : "1",
"uploadedBy" : "Admin"
},
"-L7GOvDNI-o_H8RvNwoN" : {
"authorName" : "Rocky Balboa",
"famousQuote" : "It's not about how hard you can hit; it's about how hard you can get hit and keep moving forward.",
"id" : "2",
"uploadedBy" : "Admin"
},
"-L7GP9oBv5NR1T6HlDd4" : {
"authorName" : "African proverb",
"famousQuote" : "If you want to go fast, go alone. If you want to go far, go together.",
"id" : "3",
"uploadedBy" : "Admin"
},
"-L7GPjM1F3_7Orcz0Q1q" : {
"authorName" : "A.P.J Abdul Kalam",
"famousQuote" : "Don’t take rest after your first victory because if you fail in second, more lips are waiting to say that your first victory was just luck.",
"id" : "4",
"uploadedBy" : "Admin"
},
Below is the rule which I'm using for quotes
"quotes": {
".indexOn": ".value"
}
How can I get quotes which has id 2,3 and 4?
If you have more than 4 records in your database, to solve this, you can use a query in which you should combine startAt() and endAt() methods to limit both ends of your query like this:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
Query query = rootRef.child("quotes").orderByChild("id").startAt("2").endAt("4");
query.addListenerForSingleValueEvent(/* ... */);
Please see here more informations about Firebase Query's startAt() method:
Create a query constrained to only return child nodes with a value greater than or equal to the given value, using the given orderBy directive or priority as default.
And here are more informations about Firebase Query's endAt() method:
Create a query constrained to only return child nodes with a value less than or equal to the given value, using the given orderBy directive or priority as default.
Edit: According to your comment, if you only want the items that have the id property set to 2, 3 and 4, you should use nested queries like this:
Query queryTwo = rootRef.child("quotes").orderByChild("id").equalsTo("2");
queryTwo.addListenerForSingleValueEvent(
List<Item> list = new ArrayList();
list.add(itemTwo);
Query queryThree = rootRef.child("quotes").orderByChild("id").equalsTo("3");
queryThree.addListenerForSingleValueEvent(
list.add(itemThree);
Query queryFour = rootRef.child("quotes").orderByChild("id").equalsTo("4");
queryFour.addListenerForSingleValueEvent(
list.add(itemFour);
//Do what you need to do with the list that contains three items
);
);
);
I have the following MongoDB document structure that I am trying to query;
{
"key": [{
"1": [
2,
3,
4
]
},
{
"2": [
1
]
}
]
}
What I want is all documents having inside the key field having their sub field as "1". The array associated with that is [2,3,4] which are java Long values. I am trying to do the above logic with the following code with no luck;
BasicDBObject query = new BasicDBObject("key.1", null);
MongoCursor<BasicDBObject> cursor = collection.find(query).iterator();
while (cursor.hasNext()) {
System.out.println(cursor.next());
}
The reason I am associating key.1 with null in the query object is because I don't care what the values in the array is.
You're mixing couple of things here.
key.1 is conflicting dot notation syntax for querying the sub fields with numeric string name.
This will conflict with indexed based access for the array key.
BasicDBObject query = new BasicDBObject("key.1", null); in effect is asking Mongo to query for a key array for a value / document at index 1 for a null value . This would have matched if you had something like
{
"key":
[
{"1":"one"},
null
]
}
Okay now coming back to post. You've to use $exist operator if you don't care about value. The shell filter will be {"key":{"$elemMatch":{"1":{ $exists: true}}}}. Note the use of $elemMatch to do field level comparison as dot notation is conflicting with index style access. For string name fields the dot notation & elemMatch works similarly for embedded arrays for queries involving single query condition.
Java code
BasicDBObject exists = new BasicDBObject("$exists", true);
BasicDBObject field = new BasicDBObject("1", exists);
BasicDBObject elemMatch = new BasicDBObject("$elemMatch", field);
BasicDBObject query = new BasicDBObject("key", elemMatch);
More info about operators and syntax.
https://docs.mongodb.com/manual/reference/operator/query/exists/
https://docs.mongodb.com/manual/reference/operator/query/elemMatch/#op._S_elemMatch
https://docs.mongodb.com/manual/core/document/
I have a method which takes in a Collection of Objects that are to be deleted.
This is the way I am deleting them now
public void deleteAll(Collection<Object> objs){
for(Object obj : objs) {
collection.remove("{ _id: # }", obj.getId());
}
}
I am doing something very similar for update where I am looping through the passed collection of Objects. This seems to be very time consuming.
Is there a better way of doing the update/delete?
It's possible to both remove and update multiple documents with a single query.
remove
You need to use a query with a selector using $in, and an array of _id values to match.
With Jongo, you can build the list to match with $in into the query in a couple of different ways
// pass an array of ids
ObjectId[] ids = {id1, id2, id3};
collection.remove("{ _id: { $in: # } }", ids);
// or pass each id separately
collection.remove("{ _id: { $in:[#, #, #] }}", id1, id2, id3);
update
Exact same concept as above using $in to select the objects you want to update, however you also have to set the multi option, so that the update applies to all the documents it matches against, not just the first.
With Jongo this is done like so
ObjectId[] ids = {id1, id2, id3};
collection
.update("{ _id: { $in: # } }", ids)
.multi()
.with({ $set: { foo: "bar" });
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.
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.
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);
}`
In a project I'm working on, at one point I read a query to mongodb from a string. I've been using com.mongodb.util.JSON.parse(querystring) to read the query, which worked fine until I started reading queries that contained operators like $max and $min. At that point, rather than using mongodb's $max operator, the parser instead creates a "$max" field. For instance,
the input string:
{ $query : { state : "AL" } , $max : { pop : 9058 } }
is parsed to the DBObject:
{ "$query" : { "state" : "AL"} , "$max" : { "pop" : 9058}}
When I then look for a DBCursor with that query document, I get a cursor of size 0 (no matching document found in the databse), presumably because there are no documents with "$query" or "$max" fields.
Is there something I can use besides JSON.parse()? I'm not averse to writing my own function for it, but how can I get a DBObject that recognizes the $ operators as operators and not fields?
Any advice would be appreciated!
The following code snippet using query modification operator $max seems to work fine.
/* {$query:{state:"AL"}, "$max":{pop:10000}}*/
String s = "{$query:{state:\"AL\"}, \"$max\":{pop:10000}}";
DBObject dbObject = (DBObject) JSON.parse(s);
System.out.println("\nFind all: ");
DBCursor cursor = collection.find(dbObject);
try {
while (cursor.hasNext()) {
DBObject cur = cursor.next();
System.out.println(cur);
}
} finally {
cursor.close();
}
Make sure you have specified index on pop.
db.zips.getIndexes()
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"ns" : "test.zips",
"name" : "id"
},
{
"v" : 1,
"key" : {
"pop" : 1
},
"ns" : "test.zips",
"name" : "pop_1"
}
]
See the following link for detail.
http://docs.mongodb.org/manual/reference/operator/max/
Just in case you are interested in using aggregation operators $max or $min, the following link provide details and sample code.
http://docs.mongodb.org/ecosystem/tutorial/use-aggregation-framework-with-java-driver/
So it turns out the DBObject as given up there worked out fine. It returns a cursor with a size of 0, true, but the DBCursor's length is actually the thing I was looking for. (Previously, I had been checking whether the cursor's size was 0, and if it was, returning null.)
I'm not quite sure what the difference between size and length is in a DBCursor (the difference between size and count is apparent, but I'm not sure what length is supposed to be), but it works now. In the case above, size and count were both 0 but length was the desired number.