MongoDB Java - Translate query to mongodb Java driver - java

I have a wierd problem. I have a query which I am sure it works, but when I translated it in Java it does not return anything.
The MongoDB query is:
db.births.aggregate([
{
"$match": {
"day" : "March_13",
"events.year": "1929",
"events.info": /American/
}
},
{ "$unwind": "$events" },
{
"$match": {
"day" : "March_13",
"events.year": "1929",
"events.info": /American/
}
},
{
"$group": {
"_id": "$_id",
"day": { "$first": "$day" },
"events": { "$push": "$events" }
}
}
])
The Java code is:
AggregateIterable<Document> result = mongoDatabase.getCollection(category).
aggregate(
Arrays.asList(
new Document("$match",
new Document("day", day).
append("events.year", year).
append("events.info", "/" + word + "/")),
new Document("$unwind", "$events"),
new Document("$match",
new Document("day", day).
append("events.year", year).
append("events.info", "/" + word + "/")),
new Document("$group",
new Document("_id", "$_id").
append("day", new Document("$first", "$day")).
append("events", new Document("$push", "$events")))));
Please have a look and tell me if the way I translated it into Java is wrong or there is something else.
Thank you!

In Java you can try this:
String m = "American";
DBObject eventInfoObject = new BasicDBObject(
"events.info",
java.util.regex.Pattern.compile(m)
);
This send to the MongoDB server the query { "events.info" : { "$regex" : "American"}}
In your code replace:
"/" + word + "/"
with:
java.util.regex.Pattern.compile(word)

Related

creating dynamically group query in java

I have the following database:
{ stream :{ "name": "name1",
"error1": 1,
"error2": 1,
"error3": 1 }
}
,
{ stream : {"name": "name1",
"error1": 2,
"error2": 1,
"error3": 1 }
}
,
{ stream : {"name": "name2",
"error1": 1,
"error2": 1,
"error3": 1 }
}
I would like to group it by name and sum every time some different combination of errors.
this is what I did in mongo, I need to create the following query dynamically in java
db.collection.aggregate([{$group: {_id: "$stream.name",error1: {$sum:"$stream.error1" },error2: {$sum: "$stream.error2" }} ])
the thing is that every time I need different combinations of the errors:error1 with error2, only error 1 etc..
this is what I did: (the arguments in the "if" are some boolean values that I am getting)
List<String> totalError = new ArrayList<String>();
BasicDBObject group = new BasicDBObject( "$group", new BasicDBObject("_id","$stream.name" ));
if (error1)
{
group.append("error1",new BasicDBObject ("$sum", "$stream.error1" ));
}
if (error2)
{
group.append("error2",new BasicDBObject ("$sum", "$stream.error2" ));
}
if (error3)
{
group.append("error3",new BasicDBObject ("$sum", "$stream.error3" ));
}
the problem is that I am getting:
{ "$group" : { "_id" : "$stream.name"} , "error1" : { "$sum: "$stream.error1"} , "error2" : { "$sum" : "$stream.error2"}
},
instead of:
{ "$group" : { "_id" : "$stream.name", "error1" : { "$sum: "$stream.error1"} , "error2" : { "$sum" : "$stream.error2"}}
if I knew what error combination I need I could use append in the constructor of group dbobject.. but I don't know the combination and I need to use the "ifs"
Try
BasicDBObject fields = new BasicDBObject("_id","$stream.name" );
if (error1)
fields.append("error1",new BasicDBObject ("$sum","$stream.error1"));
if (error2)
fields.append("error2",new BasicDBObject ("$sum","$stream.error2"));
if (error3)
fields.append("error3",new BasicDBObject ("$sum","$stream.error3"));
BasicDBObject group = new BasicDBObject( "$group", fields);
You should use helper functions when possible.
List<BsonField> fieldAccumulators = new ArrayList<>();
if (error1)
fieldAccumulators.add(Accumulators.sum("error1","$stream.error1"));
if (error2)
fieldAccumulators.add(Accumulators.sum("error2","$stream.error2"));
if (error3)
fieldAccumulators.add(Accumulators.sum("error3","$stream.error3"));
collection.aggregate(Arrays.asList(Aggregates.group("$stream.name", fieldAccumulators)));

Converting Mongo aggregate query into java objects

I am trying to convert a mongo aggregate query into java objects. When I am running the query in RoboMongo (tool), I get the result but converting into java objects gives empty results.
Mongo Query:
db.getCollection('wb_physicians').aggregate([
{
$match: {
$and: [
{ "product.mpoCode": "VA001"},
{ "product.npoCode": { $exists: true } }
]
}
},
{
"$project" : {
"product.specialties.code": 1,
"providerId": 1,
"product.code": 1,
"_id" : 0
}
},
{ "$unwind" : "$product.specialties" },
{
"$group" : {
"_id" : {
"providerId": "$providerId" ,
"productCode": "$product.code"
},
"specialityCodeList": { "$addToSet": "$product.specialties.code" }
}
}
])
Java Code:
private static AggregationOutput findProviderandSpecialty(DBCollection collection) {
DBObject match = new BasicDBObject("$match" ,
new BasicDBObject("$and", Arrays.asList(
new BasicDBObject("product.mpoCode" , "VA001").append("product.npoCode", "$exists: true")
))
);
DBObject project = new BasicDBObject("$project" ,
new BasicDBObject("product.specialties.code" , 1)
.append("providerId" , 1)
.append("product.code", 1)
.append("_id", 0)
);
DBObject unwind = new BasicDBObject("$unwind" , "$product.specialties");
DBObject group = new BasicDBObject("$group",
new BasicDBObject("_id", new BasicDBObject("providerId" , "$providerId"))
.append("specialityCodeList",
new BasicDBObject("$addToSet", "$product.specialties.code")
)
);
AggregationOutput output = collection.aggregate(match,project,unwind,group);
return output;
}
Could you please help me where I made the wrong mapping?
The problem is on the $match pipeline:
DBObject match = new BasicDBObject("$match" ,
new BasicDBObject("$and", Arrays.asList(
new BasicDBObject("product.mpoCode" , "VA001")
.append("product.npoCode", "$exists: true")
))
);
should be
DBObject match = new BasicDBObject("$match" ,
new BasicDBObject("$and", Arrays.asList(
new BasicDBObject("product.mpoCode" , "VA001"),
new BasicDBObject("product.npoCode",
new BasicDBObject("$exists", "true")
)
))
);
Nonetheless, you can do without the explicit $and logic by specifying a comma-separated expression of the documents as well as removing the $project pipeline before the $group
as it's rather unnecessary, so your revised pipeline could be run as:
db.getCollection('wb_physicians').aggregate([
{
"$match": {
"product.mpoCode": "VA001",
"product.npoCode": { "$exists": true }
}
},
{ "$unwind" : "$product.specialties" },
{
"$group" : {
"_id" : {
"providerId": "$providerId" ,
"productCode": "$product.code"
},
"specialityCodeList": { "$addToSet": "$product.specialties.code" }
}
}
])
And the final Java code:
private static AggregationOutput findProviderandSpecialty(DBCollection collection) {
DBObject match = new BasicDBObject("$match" ,
new BasicDBObject("product.mpoCode" , "VA001").append("product.npoCode",
new BasicDBObject("$exists", "true")
)
);
DBObject unwind = new BasicDBObject("$unwind" , "$product.specialties");
DBObject group = new BasicDBObject("$group",
new BasicDBObject("_id", new BasicDBObject("providerId" , "$providerId"))
.append("specialityCodeList",
new BasicDBObject("$addToSet", "$product.specialties.code")
)
);
List<DBObject> pipeline = Arrays.<DBObject>asList(match, unwind, group);
AggregationOutput output = collection.aggregate(pipeline);
return output;
}

How we can write this MongoDB query in Java?

([{
$match: {
"Publication_Date": {
$gte: ISODate("2003-01-01T00:00:00.0Z")
},
}
}, {
$group: {
_id: {
$year: "$Publication_Date"
},
total: {
$sum: 1
},
}
}])
Just "translate" this into org.bson.Document structure (similar to map) and call proper action (it is not a query, in that case it is an aggregate):
Date date = new SimpleDateFormat("yyyy-MM-dd").parse("2003-01-01");
Document group = new Document();
group.put("_id", new Document("$year", "$Publication_Date"));
group.put("total", new Document("$sum", 1));
AggregateIterable<Document> aggregate = collection.aggregate(Arrays.asList(
new Document("$match", new Document("Publication_Date",
new Document("$gte", date))),
new Document("$group", group)
));
or use com.mongodb.client.model package static methods (less verbose):
AggregateIterable<Document> aggregate = collection.aggregate(Arrays.asList(
Aggregates.match(Filters.gte("Publication_Date", date)),
Aggregates.group(
new Document("$year", "$Publication_Date"),
Accumulators.sum("total", 1))));

New aggregation feature with Mongo 3.2 driver, using Java

I want to perform an aggregation in Mongo 3.2 as explained here, but in Java:
https://docs.mongodb.org/master/reference/operator/aggregation/lookup/#pipe._S_lookup
At the moment my query object in java is very simple:
Document query = new Document();
query.append("employeId", employeId);
In addition to filtering by employeId, I would like to join this collection with company (where employee.company_id = company.id)
How can I do that in Java? It seems like I cannot find documentation of this new Mongo feature.
EDIT
Example of Employee collection:
{
"id" : 1,
"name" : "John",
"lastName" : "Moore",
"age" : 44,
"companyId": 10
}
Example of Company collection:
{
"id" : 10,
"companyName" : "Microsoft",
"numEmployee" : 100
}
Example of output expected
{
"id" : 1,
"name" : "John",
"lastName" : "Moore",
"companyId" : 10,
"companyName" : "Microsoft"
}
Running the following aggregation pipeline should give you the needed result
pipeline = [
{
"$match": {
"_id": employeeId
}
},
{
"$lookup": {
"from": "company",
"localField": "companyId",
"foreignField": "_id",
"as": "company"
}
},
{
"$project": {
"name": 1,
"lastName": 1,
"companyId": 1,
"companyName": "$company.companyName"
}
}
];
db.employee.aggregate(pipeline);
Java test implementation
public class JavaAggregation {
public static void main(String args[]) throws UnknownHostException {
MongoClient mongo = new MongoClient();
DB db = mongo.getDB("test");
DBCollection coll = db.getCollection("employee");
// create the pipeline operations, first with the $match
DBObject match = new BasicDBObject("$match",
new BasicDBObject("_id", employeeId)
);
// build the $lookup operations
DBObject lookupFields = new BasicDBObject("from", "company");
lookupFields.put("localField", "companyId");
lookupFields.put("foreignField", "_id");
lookupFields.put("as", "company");
DBObject lookup = new BasicDBObject("$lookup", lookupFields);
// build the $project operations
DBObject projectFields = new BasicDBObject("name", 1);
projectFields.put("lastName", 1);
projectFields.put("companyId", 1);
projectFields.put("companyName", "$company.companyName");
DBObject project = new BasicDBObject("$project", projectFields);
List<DBObject> pipeline = Arrays.asList(match, lookup, project);
AggregationOutput output = coll.aggregate(pipeline);
for (DBObject result : output.results()) {
System.out.println(result);
}
}
}

mongotemplate aggregation with condition

I have a collection where the documents look like this:
{
_id: "545b9fa0dd5318a4285f7ce7",
owner: "admin",
messages: [
{
id: "100",
status: "sent",
note: ""
},
{
id: "100",
status: "pending",
note: ""
},
{
id: "101",
status: "sent",
note: ""
},
{
id: "102",
status: "sent",
note: ""
},
{
id: "101",
status: "done",
note: ""
}
]
}
(This is just a short example, in my case the sub array is very large)
I need to query the collection and get some statistics for a specific document.
So in this example if I query the doucment that has id: "545b9fa0dd5318a4285f7ce7"
I should get this result:
{
sent: 3,
pending: 1,
done: 1
}
How can I do such aggregation with spring mongotemplate ?
To do this sort of thing you need the $cond operator in the aggregation framework. Spring Data MongoDB does not do that yet, and there are many things lacking from common $group operations that are even only implemented under $project.
The issue for tracking implementation of any $cond support is here:
https://jira.spring.io/browse/DATAMONGO-861
For the rest of the world it looks like this:
db.collection.aggregate([
{ "$match": { "_id": ObjectId("545b9fa0dd5318a4285f7ce7") } },
{ "$unwind": "$messages" },
{ "$group": {
"_id": "$_id",
"sent": {
"$sum": {
"$cond": [
{ "$eq": [ "$mesages.status", "sent" ] },
1,
0
]
}
},
"pending": {
"$sum": {
"$cond": [
{ "$eq": [ "$messages.status", "pending" ] },
1,
0
]
}
},
"done": {
"$sum": {
"$cond": [
{ "$eq": [ "$messages.status", "done" ] },
1,
0
]
}
}
}}
])
To get that sort of thing to work under a mongotemplate aggregation you need a class extending aggregation operations that can be built from a DBObject:
public class CustomGroupOperation implements AggregationOperation {
private DBObject operation;
public CustomGroupOperation (DBObject operation) {
this.operation = operation;
}
#Override
public DBObject toDBObject(AggregationOperationContext context) {
return context.getMappedObject(operation);
}
}
Then you can define the "$group" as a DBObject and implement in the aggregation pipeline:
DBObject myGroup = (DBObject)new BasicDBObject(
"$group", new BasicDBObject(
"_id","$_id"
).append(
"sent", new BasicDBObject(
"$sum", new BasicDBObject(
"$cond", new Object[]{
new BasicDBObject(
"$eq", new Object[]{ "$messages.status", "sent"}
),
1,
0
}
)
)
).append(
"pending", new BasicDBObject(
"$sum", new BasicDBObject(
"$cond", new Object[]{
new BasicDBObject(
"$eq", new Object[]{ "$messages.status", "pending"}
),
1,
0
}
)
)
).append(
"done", new BasicDBObject(
"$sum", new BasicDBObject(
"$cond", new Object[]{
new BasicDBObject(
"$eq", new Object[]{ "$messages.status", "done"}
),
1,
0
}
)
)
)
);
ObjectId myId = new ObjectId("545b9fa0dd5318a4285f7ce7");
Aggregation aggregation = newAggregation(
match(Criteria.where("_id").is(myId)),
unwind("messges"),
new CustomGroupOperation(myGroup)
);
That allows you to come up with a pipeline that is basically the same as the shell representation above.
So it would seem that for now, where certain operations and sequences are not supported the best case is to implement a class on the AgggregationOperation interface that can be fed a DBObject or otherwise internally construct one from your own custom methods.
You can use following aggregation :
db.collection.aggregate(
{ $match : { "_id" : ObjectId("545b9fa0dd5318a4285f7ce7") } },
{ $unwind : "$messages" },
{ $group : { "_id" : "$messages.status", "count" : { $sum : 1} } }
)
it will give you the count of status for which message are available, all other status count should be consider 0.

Categories

Resources