How to get Count of aggregation query in spring mongo template - mongodb

I'm using spring mongo template to run an agreegation query on mongodb. I'm wondering is there any way to find out the count of aggregation result in spring mongo template?
Here is my Aggregation sample :
Aggregation agg = newAggregation(Class1.class,
match(criteria),
group("prop1", "prop2")
.avg("x").as("averageX")
);
I just need to know how to get count of this aggregation result in spring mongo template.

My response comes very late but it might help others. To get the count for an aggregation you need to add a new group at the end:
Add at the end of the aggregation -> Aggregation.group().count().as("count") to get the count
Aggregation aggregation = newAggregation(
Aggregation.match(Criteria.where("x").is(x).and("y").exists(true)),
Aggregation.group("x", "y"),
Aggregation.group().count().as("count")
);
To get the count:
Long.parseLong(results.getMappedResults().get(0).get("count").toString());

for spring data mongo 2.1.4
if you assume your return class is like this:
class Output{
Object x;
Object y;
/**getter and setter**/
}
you can use this code:
1.Hold grouping data:
AggregationResults<Output> result =
mongoTemplate.aggregate(
agg ,
"YOUR_DOCUMENT",
Output.class
);
int count = result.getMappedResults().size();
2. only get count: (grouping not effect until you use first(...) or last(...) usage after that)
Aggregation agg = Aggregation.newAggregation(
match(criteria),
count().as("x")/*using x only for mapping count*/
);
AggregationResults<Output> result =
mongoTemplate.aggregate(
agg ,
"YOUR_DOCUMENT",
Output.class
);
int count = result.getMappedResults().get(0).getX().get(0);/**o_O**/

Related

Spring data MongoDb aggregation: group and lookup in the same query add where clause

I have a mongoDB query where I use the group operation and lookup operation. I am able to join the 2 collections but I am not sure how I can add a WHERE clause to the below spring data code:
val lookupOperation: LookupOperation = LookupOperation.newLookup()
.from("attachments")
.localField("id")
.foreignField("data.attachmentId").`as`("attachmentJoin")
val aggregation: Aggregation = newAggregation(
Aggregation.match(Criteria("_id").`is`(sId)),
Aggregation.unwind("data"),
Aggregation.match(Criteria("data.name").nin(null, "")),
Aggregation.match(Criteria("data.attachmentName").nin(null, "")),
Aggregation.match(Criteria("data.attachmentId").nin(null, "")),
Aggregation.group(fields()
.and("data.attachmentName")
.and("data.name")
.and("data.attachmentId"))
.push("data").`as`("data"),
// =================================================
lookupOperation
)
Before the line, I have the following aggregation result:
{
"id": {GROUPPED_IDs},
"data": {DATA_OBJECT_ARRAY}
}
After lookup, the aggregation result becomes:
{
"id": {GROUPPED_IDs},
"data": {DATA_OBJECT_ARRAY},
"attachmentJoin": {ATTACHMENT_DATA}
}
the attachmentJoin contains multiple ATTACHMENT objects. I would like to perform a where clause like below, in order to have only the correct attachment
WHERE attachmentJoin.id = data.attachmentId
How could I perform the where condition in order to affect only the attachmentJoin data?
Thanks!

Mongo aggregation based on Dates between on more Collections

I'm using Mongo Aggregation with Spring Data to retrieve data from 4 collections.
My problem is that I want to add a filter based on the createdAt on 3 of these collections.
Here the examples:
Animal
{"_id":123,"name":"Lucky","age":3,createdAt:"2022-02-01 10:00:00.000Z"}
{"_id":456,"name":"Joe","age":5,createdAt:"2022-02-10 20:03:00.000Z"}
Cat
{"idAnimal":123,"toy":6,createdAt:"2022-03-01 10:00:40.000Z"}
{"idAnimal":456,"toy":2,createdAt:"2022-02-10 20:05:00.000Z"}
Pet
{"idAnimal":123,"meal":3,"medicine":false,createdAt:"2022-03-01 10:00:40.000Z"}
{"idAnimal":456,"meal":4,"medicine":true,createdAt:"2022-02-10 20:05:00.000Z"}
What I mean to do is to get all the Animals with a Pet collection created gte(2022-03-01). The expected result would be the first cat, Lucky.
In my code I tried this
final List<Criteria> criteria = new ArrayList<>();
criteria.add(new Criteria().orOperator(
Criteria.where("createdAt").gte("2022-03-01").lte("2022-03-02"),
Criteria.where("Cat.createdAt").gte("2022-03-01").lte("2022-03-02"),
Criteria.where("Pet.createdAt").gte("2022-03-01").lte("2022-03-02")
));
and my Aggregation set up is:
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.lookup("cat", "_id", "idAnimal", "Cat"),
Aggregation.lookup("pet", "_id", "idAnimal", "Pet"),
Aggregation.unwind("$_id"),
Aggregation.match(new Criteria().andOperator(criteria.toArray(new Criteria[0]))
)
);
I tried to swap the criteria, querying the Pet first, but didn't go as expected. When I run this, I get no result.
Do you have any tips? Is it possibile to execute an aggregation on Dates over multiple collections?
Thank you in advance!

Spring WebFlux + MongoDB: Tailable Cursor and Aggregation

I´m new with WebFlux and MongoDB. I´m trying to use aggregation in a capped collection with tailable cursor, but I´m nothing getting sucessful.
I´d like to execute this mongoDB query:
db.structures.aggregate(
[
{
$match: {
id: { $in: [8244, 8052]}
}
},
{ $sort: { id: 1, lastUpdate: 1} },
{
$group:
{
_id: {id: "$id"},
lastUpdate: { $last: "$lastUpdate" }
}
}
]
)
ReactiveMongoOperations gives me option to "tail" or "aggregation".
I´m able to execute aggregation:
MatchOperation match = new MatchOperation(Criteria.where("id").in(8244, 8052));
GroupOperation group = Aggregation.group("id", "$id").last("$lastUpdate").as("lastUpdate");
Aggregation aggregate = Aggregation.newAggregation(match, group);
Flux<Structure> result = mongoOperation.aggregate(aggregate,
"structures", Structure.class);
Or tail cursor
Query query = new Query();
query.addCriteria(Criteria.where("id").in(8244, 8052));
Flux<Structure> result = mongoOperation.tail(query, Structure.class);
Is it possible? Tail and Aggregation together?
Using aggregation was the way that I found to get only the last inserted document for each id.
Without aggregation I get:
query without aggregation
With aggregation:
query with aggregation
Tks in advance
The tailable cursor query creates a Flux that never completes (never emits onComplete event) and that Flux emits records as they are inserted in the database. Because of that fact I would think aggregations are not allowed by the database engine with the tailable cursor.
So the aggregation doesn't make sense in a way because on every newly inserted record the aggregation would need to be recomputed. Technically you can do a running aggregation where for every returned record you compute the wanted aggregate record and send it downstream.
One possible solution would be to do the aggregations programmatically on the returned "infinite" Flux:
mongoOperation.tail(query, Structure.class)
.groupBy(Structure::id) // create independent Fluxes based on id
.flatMap(groupedFlux ->
groupedFlux.scan((result, nextStructure) -> { // scan is like reduce but emits intermediate results
log.info("intermediate result is: {}", result);
if (result.getLastUpdate() > nextStructure.getLastUpdate()) {
return result;
} else {
result.setLastUpdate(nextStructure.getLastUpdate());
return result;
}
}));
On the other hand you should probably revisit your use case and what you need to accomplish here and see if something other than capped collection should be used or maybe the aggregation part is redundant (i.e. if newly inserted records always have the lastUpdate property larger then the previous record).

Different output for native and SpringData Query

I have a user collection with documents having org array with multiples values. The problem here is that the Spring API returns 3 documents. But the Org data is null instead of individual array data. The native query is working perfectly returning org data with values.
I am using Spring Data version 1.10.1.RELEASE
Following is my native query
db.users.aggregate([
{ $unwind: "$Org" }
])
The above query returns 3 documents because org array contains 3 data.
Following is my equivalent SpringData API
Aggregation aggregation = newAggregation(
unwind("Org"),
);
AggregationResults<UserDTO> groupResults =
mongoTemplate.aggregate(aggregation, Users.class, Users.class);
return groupResults.getMappedResults();

Spring data mongodb - aggregation framework integration

I started to use MongoDB database in my application and for data access I have chosen Spring Data for MongoDB.
I skimmed API reference and documentation and I can see that there is map-reduce integration but what about aggregation framework? I can see that it supports group by operation, which would indicate that it supports $group operator judging from this: http://docs.mongodb.org/manual/reference/sql-aggregation-comparison/, but what about other operators, are that not supported for now?
I am asking this question because I wanted to know what kind of integration with MongoDB Sping Data provides so I know what to expect, so to speak.
Spring Data 1.3.0.RC1 is available and it does support the aggregation framework.
For example:
The shell aggregation comand:
db.eft_transactions.aggregate(
{$match:
{
service:"EFT",
source:"MARKUP",
}
},
{$group:
{
_id:"$card_acceptor_id",
tran_count:{$sum:1},
amount_sum:{$sum:"$amount"}
}
}
)
is run like this from java:
AggregationOperation match = Aggregation.match(Criteria.where("service").is("EFT").and("source").is("MARKUP"));
AggregationOperation group = Aggregation.group("card_acceptor").and("amount_sum").sum("amount").and("tran_count").count();
Aggregation aggregation = newAggregation(match, group);
AggregationResults<StoreSummary> result = this.mongoTemplate.aggregate(aggregation, "eft_transactions", StoreSummary.class);
The documentation is here
NOTE: We recently had to switch to using the BUILD-SNAPSHOT build of version 1.3.0. This change necessitated the change to 2 of the above lines which have changed to:
AggregationOperation group = Aggregation.group("card_acceptor").sum("amount").as("amount_sum").count().as("tran_count");
Aggregation aggregation = Aggregation.newAggregation(match, group);
The Spring Data MongoOperations.group() method is mapped to db.collection.group() MongoDB command and not the $group aggregation function. Currently there is no support in Spring Data MongoDB for aggregation framework. Map reduce, as you have mentioned, is supported though
Aggregation aggregation = newAggregation(
match(Criteria.where("salesyear").is(year)),
group("brand","salesyear").sum("numberOfCars").as("total"),
sort(Sort.Direction.ASC, previousOperation(), "brand")
);
Here is how to get the sum of a particular field.
private Map<String, Long> getTotalMap(){
/*
db.pDSSummaryModel.aggregate([{
$group: {
_id: null,
total: {
$sum: '$totalUniqueCustomerCount'
}
}
}])
*/
Aggregation aggregations = newAggregation(
group("null").sum("totalUniqueUserCount").as("userTotal")
.sum("totalUniqueCustomerCount").as("customerTotal"),
project("customerTotal", "userTotal")
);
AggregationResults<DBObject> results = mongoTemplate.aggregate(aggregations, "pDSSummaryModel", DBObject.class);
List<DBObject> fieldList = results.getMappedResults();
Map<String, Long> map = new HashMap<>();
if(fieldList != null && !fieldList.isEmpty()) {
for(DBObject db: fieldList){
map.put("userTotal", parseLong(db.get("userTotal").toString()));
map.put("customerTotal", parseLong(db.get("customerTotal").toString()));
}
}
return map;
}