How do I implement this MongoDB aggregation in Java - java

I have the following working MongoDB aggregation shell command:
db.followrequests.aggregate([{
$match: {
_id: ObjectId("551e78c6de5150da91c78ab9")
}
}, {
$unwind: "$requests"
}, {
$group: {
_id: "$_id",
count: {
$sum: 1
}
}
}]);
Which returns:
{ "_id" : ObjectId("551e78c6de5150da91c78ab9"), "count" : 7 }
I need to implement this in Java, I am trying the following:
List<DBObject> aggregationInput = new ArrayList<DBObject>();
BasicDBObject match = new BasicDBObject();
match.put("$match", new BasicDBObject().put("_id",new ObjectId(clientId)));
aggregationInput.add(match);
BasicDBObject unwind = new BasicDBObject();
unwind.put("$unwind", "$requests");
aggregationInput.add(unwind);
BasicDBObject groupVal = new BasicDBObject();
groupVal.put("_id", "$_id");
groupVal.put("count", new BasicDBObject().put("$sum", 1));
BasicDBObject group = new BasicDBObject();
group.put("$group", groupVal);
aggregationInput.add(group);
AggregationOutput output = followRequestsCol.aggregate(aggregationInput);
for (DBObject result : output.results()) {
System.out.println(result);
}
I am getting an exception:
mongodb the match filter must be an expression in an object.
Can you please help me identify the error in the above code. Thanks!

Try to print the value of aggregationInput and you will realise that .put() does not return a BasicDBObject but just the previous value associated to the key you update. Therefore, when you do:
match.put("$match", new BasicDBObject().put("_id",new ObjectId(clientId)));
You are actually setting $match to null, as new BasicDBObject().put("_id",new ObjectId(clientId)) returns null.
Update you code to something like:
List <DBObject> aggregationInput = new ArrayList <DBObject> ();
BasicDBObject match = new BasicDBObject();
BasicDBObject matchQuery = new BasicDBObject();
matchQuery.put("_id", new ObjectId());
match.put("$match", matchQuery);
aggregationInput.add(match);
BasicDBObject unwind = new BasicDBObject();
unwind.put("$unwind", "$requests");
aggregationInput.add(unwind);
BasicDBObject groupVal = new BasicDBObject();
groupVal.put("_id", "$_id");
groupVal.put("count", new BasicDBObject().put("$sum", 1));
BasicDBObject group = new BasicDBObject();
group.put("$group", groupVal);
aggregationInput.add(group);
AggregationOutput output = followRequestsCol.aggregate(aggregationInput);
for (DBObject result : output.results()) {
System.out.println(result);
}
Or, slightly more readable, use the fluent BasicDBObjectBuilder:
final DBObject match = BasicDBObjectBuilder.start()
.push("$match")
.add("_id", new ObjectId())
.get();
aggregationInput.add(match);
And it should work fine.

Each {} must be new DBObject. Use also .append(key,value) method to make more elegant.
Try this:
List<DBObject> pipeline = new ArrayList<DBObject>(Arrays.asList(
new BasicDBObject("$match", new BasicDBObject("_id",
new ObjectId("551e78c6de5150da91c78ab9"))),
new BasicDBObject("$unwind", "$requests"),
new BasicDBObject("$group",
new BasicDBObject("_id","$_id").append("count", new BasicDBObject("$sum", 1)))));
AggregationOutput output = followRequestsCol.aggregate(pipeline);
for (DBObject result : output.results()) {
System.out.println(result);
}

This is the final working version, based on the above suggestions
// Use mongodb aggregation framework to determine the count of followers
Integer returnCount = 0;
List aggregationInput = new ArrayList();
BasicDBObject match = new BasicDBObject();
BasicDBObject matchQuery = new BasicDBObject();
matchQuery.put("_id", new ObjectId(clientId));
match.put("$match", matchQuery);
aggregationInput.add(match);
BasicDBObject unwind = new BasicDBObject();
unwind.put("$unwind", "$requests");
aggregationInput.add(unwind);
BasicDBObject groupVal = new BasicDBObject();
groupVal.put("_id", null);
BasicDBObject sum = new BasicDBObject();
sum.put("$sum", 1);
groupVal.put("count", sum);
BasicDBObject group = new BasicDBObject();
group.put("$group", groupVal);
aggregationInput.add(group);
AggregationOutput output = followRequestsCol.aggregate(aggregationInput);
for (DBObject result : output.results()) {
returnCount = (Integer) result.get("count");
break;
}
return returnCount;

Related

Aggregation to combine collections attempt

I want to perform an aggregation in Java: here's my attempt
Example of dept collection.
{
"_id" : ObjectId("5d4dc8635dd32dbcba4ae0ae"),
"name" : "Sales"
}
Example of employee_dept collection
{
"_id" : ObjectId("5d5411be6cd7524f36a7933f"),
"dept_id" : ObjectId("5d4dc8635dd32dbcba4ae0ae"),
"employee_id" : ObjectId("5d4dc8635dd32dbcba4ae0af")
}
Example of output expected
{
"_id" :"5d4dc8635dd32dbcba4ae0ae",
"name" : "Sales"
}
Java code
DBObject match = new BasicDBObject("$match", new BasicDBObject("employee_id", "5d4dc8635dd32dbcba4ae0af"));
// build the $lookup operations
DBObject lookupFields = new BasicDBObject("from", "dept");
lookupFields.put("localField", "dept_id");
lookupFields.put("foreignField", "_id");
lookupFields.put("as", "dept");
DBObject lookup = new BasicDBObject("$lookup", lookupFields);
// build the $projection operations
DBObject projectFields = new BasicDBObject("name", 1);
projectFields.put("_id", 1);
DBObject project = new BasicDBObject("$project", projectFields);
List<DBObject> pipeline = Arrays.asList(match, lookup, project);
AggregateIterable aggregateIterable = dbCollection.aggregate(pipeline);
for(Object result: aggregateIterable) {
System.out.println(result);
}
Issue: aggregateIterable is not getting output due to some reason
B) if you don't mind adding how to project for $employee_dept._id and employee_id within the following?
Document project = new Document("$project", new BasicDBObject("name", "$dept.name")
.append("e_id", "$employee_department._id")
.append("employee_id", "$employee_department.employee_id")
.append("dept_id", "$dept._id"));
Issues:
The comparison of employee_id of type ObjectId with a string
In projection, the name and _id are inside 'dept' array and not at
the root level
Fixed code:
Document match = new Document("$match", new Document("employee_id", new ObjectId("5d4dc8635dd32dbcba4ae0af")));
// build the $lookup operations
Document lookupFields = new Document("from", "dept");
lookupFields.put("localField", "dept_id");
lookupFields.put("foreignField", "_id");
lookupFields.put("as", "dept");
Document lookup = new Document("$lookup", lookupFields);
// build unwind operation
Document unwind = new Document("$unwind", "$dept");
// build the $projection operations
Document projectFields = new Document("name", "$dept.name");
projectFields.put("_id", new Document("$toString", "$dept._id"));
Document project = new Document("$project", projectFields);
List<Document> pipeline = Arrays.asList(match, lookup, unwind, project);
AggregateIterable<Document> aggregateIterable = groupDAO.database.getCollection("employee_dept")
.aggregate(pipeline);
for (Document result : aggregateIterable) {
System.out.println(result.toJson());
}

How to implement $group aggregate code in mongo java

I want to use mongo aggregate in Java, but I cannot define $group code as:
In javascript:
$group = {
'_id':null,
'money_bank':{
'$sum':{
'$cond':[{'$eq'=>{'$type':'bank'}},'$amount',0]
}
}
In java:
BasicDBList eqList = new BasicDBList();
eqList.add("$type");
eqList.add("bank");
DBObject eqObject = BasicDBObjectBuilder.start().add("$eq", eqList).get();
BasicDBList condList = new BasicDBList();
condList.add(eqObject);
condList.add("$amount");
condList.add(0);
DBObject conObj = BasicDBObjectBuilder.start().add("$cond", condList).get();
DBObject sumObj = BasicDBObjectBuilder.start().add("$sum", conObj).get();
DBObject moneyObj = BasicDBObjectBuilder.start().add("money_bank", sumObj).get();
DBObject idObj = BasicDBObjectBuilder.start().add("_id", null).get();
BasicDBList groupList = new BasicDBList();
groupList.add(idObj);
groupList.add(moneyObj);
DBObject group = new BasicDBObject("$group", groupList);
But when I executed code, the error:
"errmsg" : "exception: a group's fields must be specified in an object"
Please help me in Java.
You've slightly misunderstood the correct way to turn this into a Java call, two of the places you've used a BasicDBList you actually needed to use a BasicDBObject instead - that's what that error means, it means you need to use an object not a list.
I've made the change to your code, but I haven't tested it against MongoDB, I leave that up to you.
The minimal changes needed to give you the equivalent of the Javascript is:
BasicDBObject eqArgs = new BasicDBObject("$type", "bank");
DBObject eqObject = BasicDBObjectBuilder.start().add("$eq", eqArgs).get();
BasicDBList condList = new BasicDBList();
condList.add(eqObject);
condList.add("$amount");
condList.add(0);
DBObject conObj = BasicDBObjectBuilder.start().add("$cond", condList).get();
DBObject sumObj = BasicDBObjectBuilder.start().add("$sum", conObj).get();
BasicDBObject groupArgs = new BasicDBObject();
groupArgs.append("_id", null);
groupArgs.append("money_bank", sumObj);
DBObject group = new BasicDBObject("$group", groupArgs);
// Using an assert to check it's correct, remove in production code
Assert.assertThat(group.toString(), is("{ \"$group\" : { \"_id\" : null , " +
"\"money_bank\" : { " +
"\"$sum\" : { " +
"\"$cond\" : [ { \"$eq\" : { \"$type\" : \"bank\"}} , " +
"\"$amount\" , 0]" +
"}" +
"}" +
"}" +
"}"));
However you can simplify it even more (although I admit the Java is still uglier than the JavaScript):
BasicDBList condList = new BasicDBList();
condList.add(new BasicDBObject("$eq", new BasicDBObject("$type", "bank")));
condList.add("$amount");
condList.add(0);
DBObject sumObj = new BasicDBObject("$sum", new BasicDBObject("$cond", condList));
DBObject group = new BasicDBObject("$group", new BasicDBObject("_id", null).append("money_bank", sumObj));
For some reason the BasicDBObjectBuilder is more verbose than using BasicDBObject.

QueryBuilder and 'in' method in mongo issue

This statement works fine in mongo console:
db.system_integrator.find( { person_name: { $in: [ "Mick Jagger", "Bob Dyla", "Tony Orlando" ] } } )
I'm trying to build equivalent one in java using QueryBuilder as following:
DBObject query = new BasicDBObject();
query.put(dbFieldName, "[ \"Mick Jagger\", \"Bob Dyla\", \"Tony Orlando\" ]");
qb.in( query );
however this way of building a query doesn't work. What I'm doing wrong here?
You can do this in Java as follows :
BasicDBList list = new BasicDBList();
list.add("Mick Jagger");
list.add("Bob Dyla");
list.add("Tony Orlando");
QueryBuilder qb = new QueryBuilder();
qb.put("person_name").in(list);
or
BasicDBList list = new BasicDBList();
list.add("Mick Jagger");
list.add("Bob Dyla");
list.add("Tony Orlando");
DBObject query = new BasicDBObject();
query.put("person_name", new BasicDBObject("$in", list));

How to perform match on sum of two fields

I have a query in MySQL that contains the following condition:
WHERE START_TIME < ? AND START_TIME+DURATION >= ?
How should I migrate this to MongoDB using Java driver and aggregation framework?
The first condition will become:
DBObject match = new BasicDBObject("$match", new BasicDBObject("start_time", "{ $lt : "+timestamp+"}") );
But I'm not sure about the second.
Thanks.
EDIT
I've tried to work with Asya Kamsky answer, this is what I got but it's not working:
BasicDBList dbList = new BasicDBList();
dbList.add("$start_time");
dbList.add("$duration");
DBObject matchLT = new BasicDBObject("$match", new BasicDBObject("start_time", new BasicDBObject("$lt",timestamp)));
DBObject project = new BasicDBObject("$project", new BasicDBObject("end_time", new BasicDBObject("$add", dbList)));
DBObject matchGTE = new BasicDBObject("$match", new BasicDBObject("end_time", new BasicDBObject("$gte",timestamp)));
//GROUP CODE GOES HERE
AggregationOutput output = collection.aggregate(matchLT, project, matchGTE, group);
Here's how you do it in Aggregation Framework, I'm sure you can translate this to Java:
db.collection.aggregate([ {$match: {start_time:{$lt:ISODate("xxxx-xx-xx")}}},
{$project:{end_time:{$add:["$start_time","$duration"]}}},
{$match:{end_time:{$gt:ISODate("yyyy-yy-yy")}}}
] );

$slice mongoDB Java

How can I make this query in Java
db.comments.find({ "hsID.$id" { "$oid" : "4fe71a50e7e9f22ae5fb96bf"}} , {commentList: { $slice : [ 2 , 2]}});
This the code I am doing
BasicDBObject query = new BasicDBObject();
BasicDBObject allFields = new BasicDBObject();
ObjectId objectId = new ObjectId(objId);
query.put("{hsID.$id", objectId);
Integer[] sliceInt = { startIndex, pageSize };
query.put(
"commentList",
new BasicDBObject().append("$slice", sliceInt));
DBCursor resultsCursor = CommentColl.find(allFields,query);
and the output is
And the output is
query = { "{hsID.$id" : { "$oid" : "4fe71a50e7e9f22ae5fb96bf"} , "commentList" : { "$slice" : [ 2 , 2]}}
Thanks for your help
You need to separate the query from the fields you want returned. Try something more like this:
BasicDBObject query = new BasicDBObject("hsID.$id", new ObjectId(objId));
BasicDBObject fields = new BasicDBObject(
"commentList",
new BasicDBObject("$slice", new int[] { startIndex, pageSize }));
DBCursor resultsCursor = CommentColl.find(query, fields);
Notice also that I removed the opening curly brace that you had preceding hsid.$id.

Categories

Resources