Custom field into Aggregation with MONGO - java

Is it possible to add 2 custom fields (that are not exist at my collections), into aggregate group query, using java with mongoDB? My approach is below but it doesn't work
Double custom_calculated_field1 = 0d;
Double custom_calculated_field2 = 0d;
Document customGroup_fields = new Document();
customGroup_fields.append("customer_id", "$customer_id");
//custom_calculated fields
customGroup_fields.append("custom_calculated_field1", custom_calculated_field1);
customGroup_fields.append("custom_calculated_field2", custom_calculated_field2);
AggregateIterable<Document> customers_results = customerID.aggregate(
Arrays.asList(
Aggregates.match(values),
Aggregates.group(customGroup_fields)
));

This is a problem with the way you structure your aggregation. You can read more about it on the Mongo aggregation docs here: https://docs.mongodb.com/manual/aggregation/
Effectively you assume that your pipeline knows what operations you are trying to execute. A pipeline however works very similar to what a unix pipe | does. It executes any command and passes the output to the next. So you need to tell it that your match is a match and the group is a group. I have an example for both Document and DBObject, where DBObject is the more up to date approach I believe. Consider this example:
#Test
public void testAggProjection() {
DBCollection collection = template.getCollection("test-collection");
DBObject ob = new BasicDBObject(ImmutableMap.of("test", "value1", "something", "here"));
DBObject ob2 = new BasicDBObject(ImmutableMap.of("test", "value1", "hello", "world"));
DBObject ob3 = new BasicDBObject(ImmutableMap.of("test", "value2", "other", "fields"));
WriteResult insert = collection.insert(ob, ob2, ob3);
Assert.assertEquals(3, insert.getN());
DBObject match = new BasicDBObject("$match", new BasicDBObject("test", "value1")); // match the test value
Map<String, Object> grouping = new HashMap<>();
grouping.put("_id", "$test");
grouping.put("my_count", new BasicDBObject("$sum", 1));
DBObject group = new BasicDBObject("$group", grouping); // group by
AggregationOutput aggregate = collection.aggregate(match, group);
System.out.println(aggregate.results());
DBObject next = aggregate.results().iterator().next();
Assert.assertTrue(next.containsField("_id"));
Assert.assertTrue(next.containsField("my_count"));
// with documents raw
MongoClient mc = (MongoClient) client;
MongoCollection<Document> collection2 = mc.getDatabase(template.getDb().getName()).getCollection("test-collection");
Document dob = new Document(ImmutableMap.of("test", "value1", "something", "here"));
Document dob2 = new Document(ImmutableMap.of("test", "value1", "hello", "world"));
Document dob3 = new Document(ImmutableMap.of("test", "value2", "other", "fields"));
collection2.insertMany(Arrays.asList(dob, dob2, dob3));
long count = collection2.count();
Assert.assertEquals(3, count);
Document match2 = new Document("$match", new Document("test", "value1"));
Document group2 = new Document("$group", ImmutableMap.of("_id", "$test", "my_count", new Document("$sum", 1)));
AggregateIterable<Document> aggregate2 = collection2.aggregate(Arrays.asList(match2, group2));
Document next2 = aggregate2.iterator().next();
Assert.assertTrue(next2.get("_id") != null);
Assert.assertTrue(next2.get("my_count") != null);
}
This is a working unit test. What this does is (in both cases):
Create a new collection and insert 3 documents.
Aggregate them with a match, matching 2 documents, and a group where we count the objects we found.
The important bits for you are here:
Document match2 = new Document("$match", new Document("test", "value1"));
Document group2 = new Document("$group", ImmutableMap.of("_id", "$test", "my_count", new Document("$sum", 1)));
AggregateIterable<Document> aggregate2 = collection2.aggregate(Arrays.asList(match2, group2));
In my match2 and group2 documents, I define what stage the aggregation is executing as $match and $group.
The resulting Json is then:
Document{{_id=value1, my_count=2}}
I hope that helps!
Please note that the _id field inside the group is mandatory.
Artur

Related

How do i add another value to an array in an existing MongoDB document using Java?

This is my current code
Document bitcoin = new Document("Currency", "Bitcoin").append("Values", Arrays.asList());
//bitCoinCollection.insertOne(bitcoin);
for(int i = 0; i<100;i++){
BasicDBObject setNewFieldQuery = new BasicDBObject()
.append("$set", new BasicDBObject().append("value", 100));
mongoClient.getDatabase("Binance").getCollection("Binance")
.updateOne(new BasicDBObject().append("_id", "tjena"), setNewFieldQuery);
}
It doesnt add any values to the excisting arraylist... is there a way to add it to an excisting arraylist in a document? Thanks in advance
For this sample collection of one document with an array field using MongoDB Java driver:
{ _id: 1, fruits: [ "orange", "banana" ] }
The following Java code adds one new element to the fruits array:
Bson update = push("fruits", "guava");
Bson filter = eq("_id", new Integer(1));
UpdateResult result = collection.updateOne(filter, update);
To add multiple elements all at once from a List collection:
Bson update = pushEach("fruits", Arrays.asList("grape", "apple", "peach"));
Bson filter = eq("_id", new Integer(1));
UpdateResult result = collection.updateOne(filter, update);

How to use "$or" operator using documents in mongodb using java

Document query = new Document();
query.put("firstName", "abc");
query.put("firstName", "xyz");
Here,"abc" is overrided by the "xyz".So,in the query I'm getting only one value i.e,xyz.I don't know,why the query getting overrided.can u please help me out...
My code:
MongoClient mongo = new MongoClient("localhost",27017);
MongoDatabase db = mongo.getDatabase("sample");
MongoCollection<Document> coll = db.getCollection("users");
Document query = new Document();
query.put("firstName", "abc");
query.put("firstName", "xyz");
Document docStatus =new Document("$or",query );
try (MongoCursor<Document> cursor = coll.find(query).iterator()) {
while (cursor.hasNext()) {
System.out.println(cursor.next());
}
}
The arguments to the $or operator are a List, therefore you are just contructing a List of Document:
Document query = new Document(
"$or", Arrays.asList(
new Document("firstname", "abc"),
new Document("firstname", "xyz")
)
)
But really, when you want an $or condition on the same property, use $in instead. Which is of course another List but just of values:
Document query = new Document(
"firstname",
new Document(
"$in", Arrays.asList("abc","xyz")
)
)
Which is much shorter syntax for the selection of "either" value on the same document property.
You need to construct your query as below :
DBObject clause1 = new BasicDBObject("firstName", "abc");
DBObject clause2 = new BasicDBObject("firstName", "xyz");
BasicDBList or = new BasicDBList();
or.add(clause1);
or.add(clause2);
DBObject query = new BasicDBObject("$or", or);
BasicDBList orList = new BasicDBList();
orList.add(new BasicDBObject("FirstName","xxx"));
orList.add(new BasicDBObject("LastName","yyy"));
BasicDBObject dbObj = new BasicDBObject("$or",orList);

What's the update equivalent of insertMany in Mongo / Java

I am trying to do a bulk upsert, but am having problems to figure out how to do it. This does not work:
BasicDBObject filter = new BasicDBObject();
BasicDBObject update = new BasicDBObject("$set", list);
UpdateOptions options = new UpdateOptions().upsert(true);
collection.updateMany(filter, update, options);
Exception in thread "main" com.mongodb.MongoWriteException: Modifiers operate on fields but we found a Array instead.
Any examples would just be great!
UpsertMany items in one roundtrip
This is what I meant:
public static BulkWriteResult upsertAll(MongoCollection<Document> coll, List<Document> docs, String keyTag) {
List<UpdateOneModel<Document>> requests = new ArrayList<UpdateOneModel<Document>>();
UpdateOptions opt = new UpdateOptions().upsert(true);
for (Document doc : docs ) {
BasicDBObject filter = new BasicDBObject(keyTag, doc.get(keyTag));
BasicDBObject action = new BasicDBObject("$set", doc);
requests.add(new UpdateOneModel<Document>(filter, action, opt));
}
return coll.bulkWrite(requests);
}
You can perform bulk upsert using below code :
MongoClient mongo = new MongoClient("localhost", 27017);
DB db = (DB) mongo.getDB("testDB");
DBCollection collection = db.getCollection("collection");
BasicDBObject obj = new BasicDBObject();
obj.append("$set", new BasicDBObject("my_field", dbList));
BasicDBList dbList = new BasicDBList();
dbList.add(1);
dbList.add(2);
dbList.add(3);
BulkWriteOperation bwo = collection.initializeUnorderedBulkOperation();
bwo.find(new BasicDBObject()).upsert().update(obj);
bwo.execute();
For more details you can check : http://www.journaldev.com/6324/mongodb-upsert-example-using-mongo-shell-and-java-driver
This might work
db.collection.update({},{$set : {"my_field":[1, 2, 3]}},false,true)

How to write group by with add operation in MongoDB Java driver

In the following query , I'm doing addition to a field (This field having ISO date as value) then extracting hour from that field, then group by on hour
db.campaigns.aggregate([
{$group : { _id: {$hour:{$add:['$time', 19800000]}}}}
])
Sample record of the collection
db.campaigns.findOne()
{
"_id" : ObjectId("53c7afdab92be74745af9068"),
"time" : ISODate("2013-03-08T12:25:24.973Z"),
"type" : "annoying",
"PINGS" : 143
}
The above one is working fine in Mongo shell,
I'm trying write this query in Java
Here is my partial Java code
DBObject group2Fields = new BasicDBObject();
group2Fields.put("hour", new BasicDBObject("$hour", new BasicDBObject("$add",new BasicDBObject("time",19800000))));
DBObject group2 = new BasicDBObject("_id", group2Fields);
DBObject secondGroup = new BasicDBObject("$group", group2);
I'm getting "errmsg" : "exception: field inclusion is not allowed inside of $expressions"
try this:
DBObject group2Fields = new BasicDBObject();
BasicDBList addObjects = new BasicDBList();
addObjects.add("$time");
addObjects.add(19800000);
group2Fields.put("$hour", new BasicDBObject("$add", addObjects));
DBObject group2 = new BasicDBObject("_id", group2Fields);
DBObject secondGroup = new BasicDBObject("$group", group2);
Nesting calls in your code generally helps you to "visualise" the structure that you want:
BasicDBList addArgs = new BasicDBList();
addArgs.add("$time");
addArgs.add(19800000);
DBObject group = new BasicDBObject("$group",
new BasicDBObject("_id",
new BasicDBObject("$hour",
new BasicDBObject("$add", addArgs)
)
)
);
Which of course produces a group variable that serializes like so:
{ "$group" : { "_id" : { "$hour" : { "$add" : [ "$time" , 19800000]}}}}

Complex Mongo DB query - Sub documents

I have the document structured as shown
I would like to get a collection of moduleDataItems which has version say more than 0.
This is my attempt :
Query qu = Query.query(Criteria.where("appKey")
.is("MOCK_APP").and("modules._id")
.is("APP_1_MOD_1")
.and("modules.moduleDataItems.version")
.gt(0));
List<DataItem> dList = mongoTemplate.find(qu,
DataItem.class,
ApplicationConstants.MONGO_APPLICATION_COLLECTION_NAME);
I m pretty sure I'm not doing the right thing. I do not get any DataItem in the result.
The classes represent the json structure.
Any help is appreciated.
Thanks
Finally solved it using this -
DBObject unwindParam = new BasicDBObject("$unwind","$dataItems");
DBObject matchParam = new BasicDBObject("$match",
new BasicDBObject("dataItems.version",
new BasicDBObject("$gt",requestedModule.getVersion())));
DBObject fields = new BasicDBObject("dataItems", 1);
DBObject projectParam = new BasicDBObject("$project", fields);
AggregationOutput output = mongoTemplate.getCollection(
"appModules").aggregate(
unwindParam, matchParam,projectParam);
CommandResult updatedData = output.getCommandResult();
BasicDBList resList = (BasicDBList) updatedData.get("result");

Categories