Mongo aggregation in java: group with multiple fields - java

I'm trying to perform an aggregation operation using in Java using the mongo-java-driver. I've performed some other find operations, but I'm unable to do the following aggregation correctly in Java:
db.I1.aggregate([
{ "$match": { "ci": 862222} },
{ "$match": { "gi": { "$ne": null } }},
{ "$group": {
"_id": {
"ci": "$ci",
"gi": "$gi",
"gn": "$gn",
"si": "$si"
}
}},
{ "$group": {
"_id": {
"ci": "$_id.ci",
"gi": "$_id.gi",
"gn": "$_id.gn"
},
"sn": { "$sum": 1 }
}},
{ "$sort" : { "_id.gi" : 1}}
])
I've tried several ways and methods to perform that aggregation in Java, but I'm unable to include the group fields "ci", "gi", "gn","si" correctly in the coll.aggregate(asList()) method. What I got so far, is the following:
MongoCollection<Document> coll = mongo.getCollection("I1");
Document matchCourse = new Document("$match",
new Document("ci", Integer.parseInt(courseid)));
Document matchGroupNotNull = new Document("$match",
new Document("gi", new Document("$ne", null)));
List<Object> list1 = new BasicDBList();
list1.add(new BasicDBObject("ci", "$ci"));
list1.add(new BasicDBObject("gi", "$gi"));
list1.add(new BasicDBObject("gn", "$gn"));
list1.add(new BasicDBObject("si", "$si"));
Document group1 = new Document(
"_id", list1).append("count", new Document("$sum", 1));
List<Object> list2 = new BasicDBList();
list2.add(new BasicDBObject("ci", "$_id.ci"));
list2.add(new BasicDBObject("gi", "$_id.gi"));
list2.add(new BasicDBObject("gn", "$_id.gn"));
Document group2 = new Document(
"_id", list2).append("sn", new Document("$sum", 1));
Document sort = new Document("$sort",
new Document("_id.gi", 1));
AggregateIterable<Document> iterable = coll.aggregate(asList(matchCourse,
matchGroupNotNull, group1, group2, sort));
I know it's not correct, but I included it to give you an idea of what I am doing. I've googled this in many different ways and read several pages, but I didn't find any solution. The available documentation for MongoDB-Java(1, 2) is too short for me and doesn't include this case.
How can I perform that query in Java? Any help would be appreciated.
Thank you very much!!

Finally I've come to a solution. There were some errors in the question that I posted, as it was the last attempt after reaching some point of desperation, but finally, here is the final solution:
MongoDatabase mongo = // initialize your connection;
Document matches = new Document("$match",
new Document("gi", new Document("$ne", null))
.append("ci", Integer.parseInt(courseid)));
Document firstGroup = new Document("$group",
new Document("_id",
new Document("ci", "$ci")
.append("gi", "$gi")
.append("gn", "$gn")
.append("si", "$si"))
.append("count", new Document("$sum", 1)));
Document secondGroup = new Document("$group",
new Document("_id",
new Document("ci", "$_id.ci")
.append("gi", "$_id.gi")
.append("gn", "$_id.gn"))
.append("ns", new Document("$sum", 1)));
Document sort = new Document("$sort",
new Document("_id.gi", 1));
List<Document> pipeline = Arrays.asList(matches, firstGroup,
secondGroup, sort);
AggregateIterable<Document> cursor = mongo.getCollection("I1")
.aggregate(pipeline);
for(Document doc : cursor) { // do stuff with doc }
Instead of trying to create lists of key-values, I just appended the elements to the documents. Hope it will be useful for somebody!!

This question is quite old but was the top match on google when I searched so if anyone is looking for a solution to this I managed to do it in the following way
Aggregation.group(Fields.fields()
.and("field1")
.and("field2"))
.first("name")
.`as`("name")
.count().`as`("count")
This will produce the following MDB query:
{ "$group" :
{ "_id" :
{ "field1" : "$field1", "field2" : "$field2"},
"name" : { "$first" : "$name"},
"count" : { "$sum" : 1}
}

Related

Convert this Mongodb command to java (aggregate, remove)

How can i convert this Mongodb command in java?
This command find the duplicates documents and count them, after that start to remove the duplicates with keeping one of them.
I'm using Mongodb 3.
var duplicates = [];
db.sitesToVisit.aggregate([
{ $match: {
Link: { "$ne": '' } // discard selection criteria
}},
{ $group: {
_id: { Link: "$Link"}, // can be grouped on multiple properties
dups: { "$addToSet": "$_id" },
count: { "$sum": 1 }
}},
{ $match: {
count: { "$gt": 1 } // Duplicates considered as count greater than one
}}
]).forEach(function(doc) {
doc.dups.shift(); // First element skipped for deleting
doc.dups.forEach( function(dupId){
duplicates.push(dupId); // Getting all duplicate ids
}
)
})
// If you want to Check all "_id" which you are deleting else print statement not needed
printjson(duplicates);
// Remove all duplicates in one go
db.sitesToVisit.remove({_id:{$in:duplicates}})
And this java code i tried, but i get confused in foreach part and removing.
DBCollection links = db.getCollection("sitesToVisit");
DBObject match = new BasicDBObject("$match", new BasicDBObject("Link", new BasicDBObject("$ne", "")));
DBObject group = new BasicDBObject("$group",
new BasicDBObject("_id", new BasicDBObject("Link", "$Link"))
.append("dups", new BasicDBObject("$addToSet", "$_id"))
.append("count", new BasicDBObject("$sum", 1)));
DBObject sort = new BasicDBObject("$match", new BasicDBObject("count",new BasicDBObject("$gt", 1)));
AggregationOutput output = links.aggregate(match,group,sort);
output.results().forEach((s)->{
System.out.println(s);
});
Thanks in advance.

MongoDB aggregation with Java driver

I need your help for using MongoDB aggregation framework with java driver.
I don't understand how to write my request, even with this documentation.
I want to get the 200 oldest views from all items in my collection. Here is my mongo query (which works like I want in console mode):
db.myCollection.aggregate(
{$unwind : "$views"},
{$match : {"views.isActive" : true}},
{$sort : {"views.date" : 1}},
{$limit : 200},
{$project : {"_id" : 0, "url" : "$views.url", "date" : "$views.date"}}
)
Items in this collection have one or many views.
My question is not about the request result, I want to know the java syntaxe.
Finally found the solution, I get the same result than with the original request.
Mongo Driver 3 :
Aggregate doc
MongoCollection<Document> collection = database.getCollection("myCollection");
AggregateIterable<Document> output = collection.aggregate(Arrays.asList(
new Document("$unwind", "$views"),
new Document("$match", new Document("views.isActive", true)),
new Document("$sort", new Document("views.date", 1)),
new Document("$limit", 200),
new Document("$project", new Document("_id", 0)
.append("url", "$views.url")
.append("date", "$views.date"))
));
// Print for demo
for (Document dbObject : output)
{
System.out.println(dbObject);
}
You can make it more readable with static import :
import static com.mongodb.client.model.Aggregates.*;.
See koulini answer for complet example.
Mongo Driver 2 :
Aggregate doc
Iterable<DBObject> output = collection.aggregate(Arrays.asList(
(DBObject) new BasicDBObject("$unwind", "$views"),
(DBObject) new BasicDBObject("$match", new BasicDBObject("views.isActive", true)),
(DBObject) new BasicDBObject("$sort", new BasicDBObject("views.date", 1)),
(DBObject) new BasicDBObject("$limit", 200),
(DBObject) new BasicDBObject("$project", new BasicDBObject("_id", 0)
.append("url", "$views.url")
.append("date", "$views.date"))
)).results();
// Print for demo
for (DBObject dbObject : output)
{
System.out.println(dbObject);
}
Query conversion logic :
Thank to this link
It is worth pointing out, that you can greatly improve the code shown by the answers here, by using the Java Aggregation methods for MongoDB.
Let's take as a code example, the OP's answer to his own question.
AggregateIterable<Document> output = collection.aggregate(Arrays.asList(
new Document("$unwind", "$views"),
new Document("$match", new Document("views.isActive", true)),
new Document("$sort", new Document("views.date", 1)),
new Document("$limit", 200),
new Document("$project", new Document("_id", 0)
.append("url", "$views.url")
.append("date", "$views.date"))
));
We can rewrite the above code as follows;
import static com.mongodb.client.model.Aggregates.*;
AggregateIterable output = collection.aggregate(Arrays.asList(
unwind("$views"),
match(new Document("views.isActive",true)),
sort(new Document("views.date",1)),
limit(200),
project(new Document("_id",0)
.append("url","$views.url")
.append("date","$views.date"))
));
Obviously, you will need the corresponding static import but beyond that, the code in the second example is cleaner, safer (as you don't have to type the operators yourself every time), more readable and more beautiful IMO.
Using previous example as a guide, here's how to do it using mongo driver 3 and up:
MongoCollection<Document> collection = database.getCollection("myCollection");
AggregateIterable<Document> output = collection.aggregate(Arrays.asList(
new Document("$unwind", "$views"),
new Document("$match", new Document("views.isActive", true))
));
for (Document doc : output) {
...
}
Here is a simple way to count employee by departmentId..
Details at: Aggregation using Java API
Map<Long, Integer> empCountMap = new HashMap<>();
AggregateIterable<Document> iterable = getMongoCollection().aggregate(Arrays.asList(
new Document("$match",
new Document("active", Boolean.TRUE)
.append("region", "India")),
new Document("$group",
new Document("_id", "$" + "deptId").append("count", new Document("$sum", 1)))));
iterable.forEach(new Block<Document>() {
#Override
public void apply(final Document document) {
empCountMap.put((Long) document.get("_id"), (Integer) document.get("count"));
}
});

MongoDb Polygon Search Query returns no result

I have problems regarding a Polygone Search in my MongoDB.
I have a document structure like:
Object:{id,type,...,
data:{
name,
loc:{
lng:xxx
lat:yyy
type:Point}}}
I have an 2d Index on "data.loc".
My query in java code is:
DBCollection coll = database.getCollection(type);
BasicDBList points = new BasicDBList();
points.add(bbox.getNe());
points.add(bbox.getSe());
points.add(bbox.getSw());
points.add(bbox.getNw());
points.add(bbox.getNe());
BasicDBList parentList = new BasicDBList();
parentList.add(points);
DBObject query = new BasicDBObject("data.loc",
new BasicDBObject("$geoWithin",
new BasicDBObject("$geometry", new BasicDBObject("type","Polygon")
.append("coordinates", parentList))));
The Debuger tells me that the query is for example
{ "data.loc" : { "$geoWithin" : { "$geometry" : { "type" : "Polygon" ,
"coordinates" : [ [ [ 48.240553 , 16.451597] , [ 48.162751 ,
16.451597] , [ 48.162751 , 16.303968] , [ 48.240553 , 16.303968] , [ 48.240553 , 16.451597]]]}}}}
But after typing
DBCursor cursor = coll.find(query);
try {
while(cursor.hasNext()) {
data.add(cursor.next().get("data"));
}
} finally {
cursor.close();
}
return data;ยด
data is allways null.
Can anyone find any problems in my approach or does maybe someone have a better approach? In fact I want to do a boundingbox search on my database.
Thank you for your help!
Best regards
Daniel
Okay, so the Java MongoDB driver query for the points in the polygone for a GeoJson strcuture like this is
BasicDBList points = new BasicDBList();
points.add(bbox.getNe());
points.add(bbox.getSe());
points.add(bbox.getSw());
points.add(bbox.getNw());
points.add(bbox.getNe());
BasicDBList parentList = new BasicDBList();
parentList.add(points);
Set<Object> data = new CopyOnWriteArraySet<Object>();
DBObject query = new BasicDBObject("geometry",
new BasicDBObject("$geoWithin",
new BasicDBObject("$geometry", new BasicDBObject("type","Polygon")
.append("coordinates", parentList))));
System.err.println(query);

How to use aggregation in Mongo db Java driver with $match and $in?

How to convert below query into Java code for Mongo Java driver?
db.post.aggregate(
[
{ $match : {"name" :{'$in': ["michael", "jordan"] } }},
{ $group : { _id : "$game.id" , count : { $sum : 1 } } }
]
)
My function is not working:
DBObject match = new BasicDBObject('$match', new BasicDBObject("name", names));
The $in operator takes and array or list of arguments, so any list will basically do. But you need to form the corresponding BSON. Indenting your code helps to visualize:
BasicDBList inArgs = new BasicDBList();
inArgs.add("michael");
inArgs.add("jordan");
DBObject match = new BasicDBObject("$match",
new BasicDBObject("name",
new BasicDBObject("$in", inArgs )
)
);
DBObject group = new BasicDBObject("$group",
new BasicDBObject("_id","$game.id").append(
"count", new BasicDBObject("$sum",1)
)
);
According to the Aggregation Documentation, your query should look like:
DBObject match = new BasicDBObject('$match', new BasicDBObject('name', new BasicDBObject('$in', names)));

(MongoDB Java) $push into array

I'm using mongo 2.2.3 and the java driver.
My dilemma, I have to $push a field and value into an array, but I cant seem to figure out how to do this. A sample of my data:
"_id" : 1,
"scores" : [
{
"type" : "homework",
"score" : 78.97979
},
{
"type" : "homework",
"score" : 6.99
},
{
"type" : "quiz",
"score" : 99
}
]
I can $push in the shell:
db.collection.update({_id:1},{$push:{scores:{type:"quiz", score:99}}})
but it's when I translate this into java I confuse my self and chuck my keyboard at a wall.
my java code (incomplete and wrong) so far:
DBObject find = new BasicDBObject("_id", 1);
DBObject push = new BasicDBObject("$push", new BasicDBObject(
"scores", new BasicDBObject()));
DBObject listItem = new BasicDBObject("scores", new BasicDBObject("type","quiz").append("score",99));
DBObject updateQuery = new BasicDBObject("$push", listItem);
myCol.update(findQuery, updateQuery);
Since mongodb-driver 3.1. there is a builder class com.mongodb.client.model.Updates with appropriate methods for each update case. In this case this would be:
Document score = new Document().append("type", "quiz")
.append("score",99);
collection.updateOne(eq("_id", "1"),Updates.addToSet("scores", score));
If you're more comforable with the query format of the shell, you may find it's easier to use JSON.parse to contstruct your DBObject for the $push:
import com.mongodb.util.JSON;
String json = "{$push:{scores:{type:'quiz', score:99}}}";
DBObject push = (DBObject) JSON.parse(json);
Using Jongo, you can do as in the shell:
db.collection.update({_id:1},{$push:{scores:{type:"quiz", score:99}}})
Becomes in Java:
collection.update("{_id:1}").with("{$push:{scores:{type:#, score:#}}}", "quiz", 99);
No fancy DBObject needed ;-)
MongoDB Java driver can simplify this. Use $each instead of $push.
$each mongodb reference document
Java sample -
BasicDBObject addressSpec = new BasicDBObject();
addressSpec.put("id", new ObjectId().toString());
addressSpec.put("name", "one");
BasicDBObject addressSpec2 = new BasicDBObject();
addressSpec2.put("id", new ObjectId().toString());
addressSpec2.put("name", "two");
List<BasicDBObject> list = new ArrayList<>();
list.add(addressSpec); list.add(addressSpec2);
UpdateResult updateOne = individualCollection.updateOne(Filters.eq("_id", "5b7c6b612612242a6d34ebb6"),
Updates.pushEach("subCategories", list));

Categories

Resources