Aggregation using MongoDB java driver - java

I'm using MongoDB java driver 3.2.2 to do some aggregation operations, but I'm not sure if something could be achieved through it.
The original query in MongoDB is:
db.getCollection('report').aggregate({
$group: {
_id: "$company_id",
count: {
$sum: {
$cond: [{
$eq: ["$idcard.status", "normal"]
},0,1]
}
}
}
})
I have no idea of how to put the "$cond" as a parameter of "$sum" operator in Java driver in the code section below:
AggregateIterable<Document> res = col.aggregate(Arrays.asList(
group("$company_id",
sum("count", ...)
)));
I've searched the official document about this with no result, anyone has experience of doing this? Thanks.

For 3.x drivers
Using BsonDocument : Type Safe Version
BsonArray cond = new BsonArray();
BsonArray eq = new BsonArray();
eq.add(new BsonString("$idcard.status"));
eq.add(new BsonString("normal"));
cond.add(new BsonDocument("$eq", eq));
cond.add(new BsonInt64(0));
cond.add(new BsonInt64(1));
AggregateIterable<BsonDocument> aggregate = dbCollection.aggregate(Arrays.asList(
group("$company_id",
sum("count", new BsonDocument("$cond", cond))
)));
Using Document - Less Code but Not Type Safe
List cond = new ArrayList();
cond.add(new Document("$eq", Arrays.asList("$idcard.status", "normal")));
cond.add(0);
cond.add(1);
AggregateIterable<Document> aggregate = dbCollection.aggregate(Arrays.asList(
group("$company_id",
sum("count", new Document("$cond", cond))
)));

To use $cond in Java use ArrayList.
{ $cond: [ { $eq: ["$idcard.status", "normal"] },0,1]
// To Acheive this - [ "$idcard.status", "normal" ]
ArrayList eqArrayList = new ArrayList();
eqArrayList.add("$idcard.status");
eqArrayList.add("normal");
// To Acheive this - [ { $eq: [ "$idcard.status", "normal" ] } , 1, 0 ]
ArrayList condArray = new ArrayList();
condArray.add(new BasicDBObject("$eq", eqArrayList));
condArray.add(1);
condArray.add(0);
// Finally - { $cond: [ { $eq: ["$idcard.status", "normal" ] } , 1, 0 ] }
BasicDBObject fullCond = new BasicDBObject("$cond", condArray);
Also see: MongoDB aggregation condition translated to JAVA driver

Related

Spring MongoDB - write a query with multiple $or conditions

For an assignment I have to write a query with multiple OR conditions.
If I had to write it using MongoDB Query Language it would be trivial
{ $and : [
{ $or : [ { "field1" : "value1" }, { "field2" : "value2" } ] },
{ $or : [ { "field3" : "value3" }, { "field3" : null }, { "field3" : { $exists : true }} ] }
] }
I there a way to achieve this using Spring MongoDB ?
I tried
Query query = new Query();
query.addCriteria(new Criteria().orOperator(
Criteria.where("field1").is("value1"),
Criteria.where("field2").is("value2"),
));
query.addCriteria(new Criteria().orOperator(
Criteria.where("field3").is("value3"),
Criteria.where("field3").is(null),
Criteria.where("field3").exists(false),
));
and also tried
Query query = new Query();
query.addCriteria(
Criteria.where("field3").is("value3")
.orOperator(Criteria.where("field3").is(null))
.orOperator(Criteria.where("field3").exists(false))
.andOperator(
Criteria.where("field1").is("value1")
.orOperator(Criteria.where("field2").is("value2"))
);
I get the following message when trying to execute either queries.
Due to limitations of the com.mongodb.BasicDocument, you can't add a
second '$or' expression specified as [...]
Any help would be greatly appreciated.
You can try with this piece of code:
Criteria firstOrCriteria = new Criteria().orOperator(
Criteria.where("field1").is("value1"),
Criteria.where("field2").is("value2"));
Criteria secondOrCriteria = new Criteria().orOperator(
Criteria.where("field3").is("value3"),
Criteria.where("field3").is(null),
Criteria.where("field3").exists(true));
Criteria andCriteria = new Criteria().andOperator(firstOrCriteria, secondOrCriteria);
Query query = new Query(andCriteria);

How to search and count paths values in MongoDb using java driver

i want to see how many documents I have in a collection with the same base path: my mongoDb is structured like this:
{
{_id:1,
tag1: a,
path: C:\Users\A\Downloads\1\qwerty
},
{
_id: 2,
tag1: b,
path: C:\Users\A\Downloads\2\abcd
},
{
_id: 3,
tag1: alfa,
path: C:\Users\A\Documents\3\fsdf
},
{
_id: 4,
tag1: beta,
path: C:\Users\A\Documents\4\aaa
}
}
I want to search, for example, how many elements there are in C:\Users\A\Downloads and how many elements there are in C:\Users\A\Documents. How can I do it?
i want to see how many documents I have in a collection with the same
base path:
Assuming you are supplying the base path to find the number of documents with that base path - the following regex query will count all the documents with the path field value staring with "C:\Users\A\Downloads\".
db.paths.find( { path: /^C:\\Users\\A\\Downloads\\/ } ).count()
Code Using MongoDB Java Driver:
Pattern p = Pattern.compile("^C:\\\\Users\\\\A\\\\Documents\\\\");
Bson queryFilter = regex("path", p);
long count = collection.countDocuments(filter);
The following is the data I am using; there are 4 documents. When I run the code I get a count of 2 (which is correct and expected as there are two paths which match the pattern "^C:\\Users\\A\\Documents\\").
Using the same data as shown in the Compass screenshot, the following aggregation
db.paths.aggregate( [
{
$group : {
_id : "Counts",
Documents: {
$sum: {
$cond: [ { $regexMatch: { input: "$path" , regex: /^C:\\Users\\A\\Documents\\/ } }, 1, 0 ]
}
},
Downloads: {
$sum: {
$cond: [ { $regexMatch: { input: "$path" , regex: /^C:\\Users\\A\\Downloads\\/ } }, 1, 0 ]
}
}
}
},
] )
prints:
{ "_id" : "Counts", "Documents" : 2, "Downloads" : 1 }
The Java code for the above aggregation:
Pattern docPattern = Pattern.compile("^C:\\\\Users\\\\A\\\\Documents\\\\");
Pattern downloadPattern = Pattern.compile("^C:\\\\Users\\\\A\\\\Downloads\\\\");
List<Bson> pipeline =
Arrays.asList(new Document("$group",
new Document("_id", "Counts")
.append("document_counts",
new Document("$sum",
new Document("$cond",
Arrays.asList(
new Document("$regexMatch",
new Document("input", "$path")
.append("regex", docPattern)),
1L, 0L
)
)
)
)
.append("download_counts",
new Document("$sum",
new Document("$cond",
Arrays.asList(
new Document("$regexMatch",
new Document("input", "$path")
.append("regex", downloadPattern)),
1L, 0L
)
)
)
)
),
project(excludeId())
);
List<Document> results = new ArrayList<>();
collection.aggregate(pipeline).into(results);
results.forEach(System.out::println);
The result document:
Document{ { document_counts=2, download_counts=1 } }

How to filter data without repeat values in mongodb with java

I'm trying to get data from mongoDB without repeat values. I want to filter following data
{"page":"www.abc.com","impressions":1,"position":144}
{"page":"www.abc.com","impressions":1,"position":8}
{"page":"www.xyz.com","impressions":7,"position":4}
{"page":"www.pqr.com","impressions":1,"position":7}
{"page":"www.abc.com","impressions":1,"position":19}
to filter as following. any idea how should I do that ?
{"page":"www.xyz.com","impressions":7,"position":4}
{"page":"www.pqr.com","impressions":1,"position":7}
In java for mongodb java driver 3.0+ it could be:
public static void main(String[] args) {
try (MongoClient client = new MongoClient("127.0.0.1")) {
MongoCollection<Document> col = client.getDatabase("test").getCollection("test");
Document groupFields = new Document("_id", "$page");
groupFields.put("count", new Document("$sum", 1));
groupFields.put("impressions", new Document("$first", "$impressions"));
groupFields.put("position", new Document("$first", "$position"));
Document matchFields = new Document("count", 1);
Document projectFields = new Document("_id", 0);
projectFields.put("page", "$_id");
projectFields.put("impressions", 1);
projectFields.put("position", 1);
AggregateIterable<Document> output = col.aggregate(Arrays.asList(
new Document("$group", groupFields),
new Document("$match", matchFields),
new Document("$project", projectFields)
));
for (Document doc : output) {
System.out.println(doc);
}
}
}
Output for your db is:
Document{{impressions=1.0, position=7.0, page=www.pqr.com}}
Document{{impressions=7.0, position=4.0, page=www.xyz.com}}
You should be able to run an aggregation pipeline that groups the documents by the page field using the $group pipeline operator, get a count of the documents using the $sum operator and retain the other two fields using the $first (or $last) operator.
The preceding pipeline after the $group should be able to filter the grouped documents on the count field, i.e. filter out the duplicates from the result. Use the $match pipeline operator for such query.
A final cosmetic pipeline would involve the $project stage which reshapes each document in the stream, include, exclude or rename fields, inject computed fields, create sub-document fields, using mathematical expressions, dates, strings and/or logical (comparison, boolean, control) expressions.
Run this aggregation pipeline to get the desired result:
db.collection.aggregate([
{
"$group": {
"_id": "$page",
"count": { "$sum": 1 },
"impressions": { "$first": "$impressions" },
"position": { "$first": "$position" }
}
},
{ "$match": { "count": 1 } },
{
"$project": {
"_id": 0,
"page": "$_id",
"impressions": 1,
"position": 1
}
}
])

MongoDB/Java SDK: Query elements with a value in array

I am very new to MongoDB and its Java... SDK? Api? I have a very simple question, but I haven't been able to find a satisfactory answer.
Let's say I have a collection of instances that are like:
{
"_id": {
"$oid": "5156171e5d451c136236e738"
},
"_types": [
"Sample"
],
"last_z": {
"$date": "2012-12-30T09:12:12.250Z"
},
"last": {
"$date": "2012-12-30T04:12:12.250Z"
},
"section": "5156171e5d451c136236e70f",
"s_mac": "AA:AA:AA:AA:AA:AA",
"_cls": "Sample",
}
And I have a hard-coded Java list:
static List<String> MAC_LIST = Arrays.asList("90:27:E4:0E:3D:D2", "A8:26:D9:E6:1D:8B");
What I would like to know is how to query the MongoDB so it will give me all the objects whose s_mac field has a value that appears in the MAC_LIST List.
I'm guessing I should use the $in operator, but I don't know how to translate it to Java code.
Any hint or link to pages with explanations of the use of the $in operator through the Java SDK would be appreciated!
Here is a contrived example that works for me (driver version 2.10.1) - you can adjust the IP address and run it as is to check if you get the same outcome:
public void gss() throws Exception{
MongoClient mongo = new MongoClient("192.168.1.1");
DB db = mongo.getDB("test");
DBCollection collection = db.getCollection("stackoverflow");
DBObject o1 = new BasicDBObject();
o1.put("s_mac", "AA:AA:AA:AA:AA:AA");
o1.put("_cls", "Sample1");
DBObject o2 = new BasicDBObject();
o2.put("s_mac", "90:27:E4:0E:3D:D2");
o2.put("_cls", "Sample2");
DBObject o3 = new BasicDBObject();
o3.put("s_mac", "A8:26:D9:E6:1D:8B");
o3.put("_cls", "Sample3");
collection.insert(o1, o2, o3);
System.out.println(collection.find().count());
List<String> MAC_LIST = Arrays.asList("90:27:E4:0E:3D:D2", "A8:26:D9:E6:1D:8B");
System.out.println(collection.find(new BasicDBObject("s_mac", new BasicDBObject("$in", MAC_LIST))).count());
}
It inserts the following documents:
{ "_id" : ObjectId("5159ff98567e143bff0668e9"),
"s_mac" : "AA:AA:AA:AA:AA:AA",
"_cls" : "Sample1"
}
{ "_id" : ObjectId("5159ff98567e143bff0668ea"),
"s_mac" : "90:27:E4:0E:3D:D2",
"_cls" : "Sample2"
}
{ "_id" : ObjectId("5159ff98567e143bff0668eb"),
"s_mac" : "A8:26:D9:E6:1D:8B",
"_cls" : "Sample3"
}
A call to collection.find().count() returns 3 and a call to collection.find(new BasicDBObject("s_mac", new BasicDBObject("$in", MAC_LIST))).count() returns 2 which I think is what you expected.

Insert Array inside an object in MongoDB

I am new at MongoDB and I want to insert to mongodb data like this but I couldn't figure out how
{
image = "cab"
tags = [
[ "NNP", 0 ],
[ "NN", 1 ]
]
},
{
image = "castle"
tags = [
[ "NNP", 2 ],
[ "NN", 1 ],
]
}
my code is
BasicDBObject obj = new BasicDBObject();
obj.put("images", ....);
for(Tag tag:tags){
BasicDBObject tagsObj = new BasicDBObject();
tagsObj.put("NNP",tag.getNNP());
tagsObj.put("NN",tag.getNN());
obj.put("tags",tagsObj);
}
UPDATE:
using this code
Mongo m = new Mongo();
DB db = m.getDB("test");
DBCollection coll = db.getCollection("tags");
for(Tag tag:tags){
BasicDBList dbl = new BasicDBList();
dbl.add(new BasicDBObject("NNP",tag.getNNP()));
dbl.add(new BasicDBObject("NN", tag.getNNP()));
BasicDBObject outer=new BasicDBObject("images", currentImageName).append("tags", dbl);
coll.insert(outer);
}
I store every image alone cause the tags might be like this for the same image
{
image = "cab",
tags = [
{ "NNP", 0 },
{ "NN", 1 }
],
[ {"NNP", 4 },
{ "NN", 5 }
],
[
{"NNP", 0 },
{ "NN", 4 }
]
},
Thanks
Basically you use BasicDBObject for key-value mappings and BasicDBList for array objects. For the object in your question, you'd do this:
BasicDBList dbl = new BasicDBList();
dbl.add(new BasicDBObject("NNP",0));
dbl.add(new BasicDBObject("NN", 1));
BasicDBOBject outer=new BasicDBObject("image", "cab").append("tags", dbl);
There's some convenience methods in the api to make this a bit less verbose.
The mapping works like this:
for: {"A":1} use: new BasicDBObject("A",1)
for: {"A":1, "B":2} use: new BasicDBObject("A",1).append("B",2)
for: {"A":{"B":2}} use: new BasicDBObject("A",new BasicDBObject("B",2))
for: {"A":["B","C"]} use:
BasicDBList dbl = new BasicDBList();
dbl.add("B");
dbl.add("C");
-> new BasicDBObject("A",dbl);
Did you mean like this?
BasicDBObject obj = new BasicDBObject();
obj.put("image", ....);
for(Tag tag:tags){
BasicDBObject tagsObj = new BasicDBObject();
tagsObj.put("NNP",tag.getNNP());
tagsObj.put("NN",tag.getNN());
obj.put("tags",tagsObj);
}
Here is how i use it when using mongo3.x:
suppose you want the result to be like this: {"data": [{"key":"v1"}, {"key":"v1"}, {"key":"v1"}] }
[step1]: use Java Map to create json object which maps to the elements inside the array; that is, the {} inside []
[step1 Ans]: Map m1,m2,m3 = new HashMap(); m1.put("key", "v1"); m2.put("key", "v1"); m3.put("key", "v1");
[step2]: use Java List to add all Java Map into one element.
[step2 Ans]: List list = new ArrayList(); list.add(m1); list.add(m2); list.add(m3);
[step3]: add Java list into mongo
[step3 Ans]: Document dc = new Document("key", list);

Categories