I have a typical web application in which I am trying to generate facets from the mongodb collection. This is currently being done using the aggregation framework using the Java driver (v2.10.1). The facets are generated correctly, except for the documents containing sub-arrays, for instance I have the following json documents:
{name: polo, fueltypes:[benzin, lpg], color: black}
{name: golf, fueltypes:[benzin, cng], color: blue}
{name: a4, fueltypes:[diesel], color: blue}
The returned result set is:
name:
{_id: polo, count: 1}
{_id: golf, count: 1}
{_id: a4, count: 1}
color:
{_id: black, count: 1}
{_id: blue, count: 2}
fueltypes:
{_id: [benzin,lpg,cng,diesel], count: 3}
The aggregated result of the fueltypes field contains all the array fields.
However the desired result should be:
fueltypes:
{_id: benzin, count: 2}
{_id: lpg, count: 1}
{_id: diesel, count: 1}
{_id: cng, count: 1}
and the corresponding java code:
String str = "name" ; //or fueltypes, color
// create match
BasicDBObject match = new BasicDBObject();
match.put("$match", new BasicDBObject());
// build the $projection operation
DBObject fields = new BasicDBObject();
// fields.put("count", 1);
DBObject project = new BasicDBObject();
// Now the $group operation
DBObject groupFields = new BasicDBObject();
DBObject unwindFields = new BasicDBObject();
// build the $projection operation
fields.put(str, 1);
project.put("$project", fields);
// Now the $group operation
groupFields.put("_id", "$" + str);
// performing sum and storing it in the count attribute
groupFields.put("count", new BasicDBObject("$sum", 1));
DBObject group = new BasicDBObject("$group", groupFields);
AggregationOutput output = serviceCollection.aggregate(match, project, group);
Grouping by the array "fueltypes" gives you the number of occurrences of the array as such.
To count it's elements individually, you'll have to use the $unwind operator, like so:
// create unwind
BasicDBObject unwind = new BasicDBObject();
unwind.put("$unwind", "$" + str);
and include this before the $group operator. Alternatively, you could call the $unwind only if str is "fueltypes".
For more information about unwind, see http://docs.mongodb.org/manual/reference/aggregation/
Related
I have documents similar to below in my People collection:
{
"_id":{"$oid": XYZ},
"id": {"$numberLong":"1"},
"name":"XYZ",
"friends": [...],
"likes": [...]
}
I want to count sum of sizes of friends and likes array for each of document. In MongoDB I created an aggregation query:
{"$project":
{
id: "$id",
neighbour_count: {$sum: [{$size: "$likes"}, {$size: "$friends"}]}
}
}
and got results:
{
"_id": XYZ,
"id":2,
"neighbour_count":1601
}
Now I want simmilar results in my Java MongoDB driver. I tried to do something with Aggregates.count and Projections.fields, but didn't get proper results.
My current code:
DBCollection peopleCollection = database.getCollection("People");
BasicDBList sum = new BasicDBList();
sum.add(new BasicDBObject("$size", "$likes"));
sum.add(new BasicDBObject("$size", "$friends"));
Iterable<DBObject> output = peopleCollection.aggregate(Arrays.asList(
new BasicDBObject("$project", new BasicDBObject("id","$id").append("$sum", sum))))
.results();
throws error:
Invalid $project :: caused by :: FieldPath field names may not start with '$'.
How to do it in proper way?
The table I am having is of the following form
{
"element_1": 1,
"element_2": 1,
"elements":[
"ele_1", "ele_2", "ele_3", "ele_4"
]
},
{
"element_1":2,
"element_2":2,
"elements":[
"ele_5", "ele_6", "ele_7", "ele_8"
]
},
{
"element_1": 3,
"element_2": 3,
"elements": [
"ele_9", "ele_10", "ele_11", "ele_12"
]
}
Over here I wanted to query out the document having the element ele_1 in the elements field so that on using the java command
Query query = new Query("Required Criteria");
the document which should get returned should be
{
"element_1": 1,
"element_2": 1,
"elements":[
"ele_1", "ele_2", "ele_3", "ele_4"
]
}
I would like to mention again that the arrays in the field "elements" have no field name hence providing a key parameter while building the Criteria object is not possible. How to get the required result?
you can simply write :
Query query = new Query("{'elements' : 'ele_1'}");
You dont need $elemMatch
I'am trying to fetch all documents in a collections, where any of the document field can match to any of the listed regular expressions.
Considering below scenarios.
User can create documents with different fields names as they wish in a collection.
such as
document1 = >{ "_id":1, "card" : 1234 , "status": 4}
document2 => {"_id": ***, "Housenumber" : 356/78 , "value" : null}
------
documentn =>{ "_id" : ObjectId("4ecd2e33dd68c9021e453d12"), "searchword" : "win" }
------
Field names are not same for all the documents in a collection.
regular expressions can be:"/^(^456$|^win$............etc)/"
I tried to get key dynamically and do find query as mentioned below:
----------
table = db.getCollection(coll);
DBObject dataKeys = table.findOne();
Set<String> keys = dataKeys.keySet();
Iterator<String> iterator = keys.iterator();
while(iterator.hasNext()){
String key = iterator.next();
regexQuery.put(**key**, new BasicDBObject("$regex", "^((^(([0-9]{4}[-. _]?)$)|"
+ "(^[a-zA-Z0-9._%+-]...........................0-9]$$").append("$options", "i"));
DBCursor cursor = table.find(regexQuery);
while (cursor.hasNext()) {
System.out.println(cursor.next());
I can see key value is coming properly but it is not fetching the matching documents.
I am new to MongoDB and I followed above approach after googling it.
If you are looking to regex match on the field names (not the values), then use $objectToArray to turn the field names (LHS) into expression-worthy values (RHS):
var r = [
{ _id: 1, name: "buzz", addr: "here"}
,{ _id: 2, searchword: "win", value: 6}
,{ _id: 3, game:0, word: "foo", fruit: "apple", fame: 7}
,{ _id: 4, qval:23}
];
db.foo.insert(r);
var rin = [ /ame/, /^val/ ]; // list of regex
db.foo.aggregate([
{$project: {x: {$objectToArray: "$$CURRENT"}}}
,{$unwind: "$x"}
,{$match: {"x.k": {$in: rin}}}
]);
{ "_id" : 1, "x" : { "k" : "name", "v" : "buzz" } }
{ "_id" : 2, "x" : { "k" : "value", "v" : 6 } }
{ "_id" : 3, "x" : { "k" : "game", "v" : 0 } }
{ "_id" : 3, "x" : { "k" : "fame", "v" : 7 } }
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
}
}
])
I want to count number of sub document where type="User.Notice". My database is as below.
i wrote following query but it's returning 1 or 0 always. What's possibly wrong with it.
long countss = eventlist.count(new BasicDBObject("192_168_10_17.type", new BasicDBObject("$eq", "User.Notice")));
System.out.println(countss);
Update:
How can i get all records under particular array. I want all the documents under array `192_168_10_17. can you suggest a way?
First you should unwind 192_168_10_17 and used mongo aggregation as below
db.collectionName.aggregate({
"$unwind": "$192_168_10_17"
}, {
"$match": {
"192_168_10_17.type": "User.Notice"
}
}, {
"$group": {
"_id": "$192_168_10_17.type",
"count": {
"$sum": 1
}
}
}, {
"$project": {
"_id": 0,
"count": "$count"
}
})
Above query return all matching User.Notice count. Now convert this query in java using mongo java aggregation . I tried following java code as below
// unwind 192_168_10_17
DBObject unwind = new BasicDBObject("$unwind", "$192_168_10_17");
// create pipeline operations, with the $match
DBObject match = new BasicDBObject("$match",new BasicDBObject("192_168_10_17.type", "User.Notice"));
// Now the $group operation
DBObject groupFields = new BasicDBObject("_id", "$192_168_10_17.type");
groupFields.put("count", new BasicDBObject("$sum", 1));
DBObject group = new BasicDBObject("$group", groupFields);
// build the $projection operation
DBObject fields = new BasicDBObject("_id", 0);
fields.put("count", "$count");
DBObject project = new BasicDBObject("$project", fields);
// run aggregation
List < DBObject > pipeline = Arrays.asList(match, group, project);
AggregationOutput output = collectionName.aggregate(pipeline);
for (DBObject result: output.results()) {
System.out.println(result);
}