MongoDB Spring Data how can I aggregate count from multiple fields? - java

Let's assume that there are some data like this:
{
"orderId": 1,
"manager" : [
{
"userId" : "UserId1"
}
],
"employee" : [
{
"userId" : "UserId3"
}
]
}
{
"orderId": 2,
"manager" : [
{
"userId" : "UserId1"
}
],
"employee" : [
{
"userId" : "UserId2"
}
]
}
{
"orderId": 3,
"manager" : [
{
"userId" : "UserId1"
}
],
"employee" : [
{
"userId" : "UserId2"
}
]
}
The result should be:
{
"Agg":[
{
"userId":"UserId1",
"total":3
},
{
"userId":"UserId2",
"total":2
},
{
"userId":"UserId3",
"total":1
}
]
}
I want to get all aggregated count of userId engaged in some process. I need to group by userId from "employee" and "manager" objects and sum them. Only what I can dois group by userId from one list:
Aggregation agg = newAggregation(
match(Criteria.where("project").is("project")),
unwind("manager"),
group("manager.userId").count().as("total"),
project("total").and("userId").previousOperation(),
sort(Sort.Direction.DESC, "total")
);
How can I aggregate count from "manager" and "employee" fields?

Use $concatArrays.
Aggregation agg = newAggregation(
match(Criteria.where("project").is(project)),
project("employee", "manager"),
project().and("manager").concatArrays("employee").as("merged"),
unwind("merged"),
group("merged.userId").count().as("total"),
project("total").and("userId").previousOperation(),
sort(Sort.Direction.DESC, "total")
);

Try something like:
"$group' : {
'_id' : '$id',
'totalManager' : { '$sum' : '$manager.total' },
'totalemployee' : { '$sum' : '$employee.total' }
} }
{ "$project" : {
'totalManager' : '$totalManager',
'totalemployee' : '$totalemployee',
'totalSum' : { '$add' : [ '$totalManager', '$totalemployee' ] },
}

Related

I am working with Java and Springboot. Is it possible to retrieve only nested json field from a Json document in mongo

{
"_id" : ObjectId("dde3431134247d401b1cef"),
"_resourceId" : "fwf4-fefre4-ffdfwsc",
"organizationId" : 343203,
"domains" : [
{
"_resourceId" : "da7-cwcwe-2432d",
"name" : "d12.net",
"tenantId" : "A1650",
"application" : "TEST",
"activeInd" : true,
"subdomains" : [
{
"_resourceId" : "fw243-weded3-2eddas",
"name" : "name1",
"clientName" : "Andrew",
"phoneNumber" : "8573458456",
"email" : "modalwindow#gmail.com",
},
{
"_resourceId" : "bce3-cwdd32ede-23ede",
"name" : "name2",
"clientName" : "client2",
"phoneNumber" : "9999999999",
"email" : "test#gmail.com",
}
]
}
]
}
I am using Springboot and MongoTemplate for the find Query. If I want to retrieve a subdomain based on "domains.subdomain.name" field, can I get only the subdomain json from a mongo query, or do I get the entire document and then iterate and filter the subdomain in my java code.
Use $unwind and then $replaceWith
db.collection.aggregate([
{
$match: {
"domains.subdomains.name": "name1"
}
},
{
$unwind: "$domains"
},
{
$unwind: "$domains.subdomains"
},
{
$match: {
"domains.subdomains.name": "name1"
}
},
{
$replaceWith: "$domains.subdomains"
}
])
mongoplayground

Removing default fields from java elasticsearch querybuilder

Elasticsearch Java client's QueryBuider instance is adding properties like
1) order
2) min_doc_count
3) shard_min_doc_count
4) show_term_doc_count_error
5) lang
6) gap_policy
to the final Json query. My query works as expected without those properties. I want to prevent those properties from being added to my final query.
Java:
FilterAggregationBuilder aggregation = AggregationBuilders.filter("id", QueryBuilders.termsQuery("id",
"my-name"));
TermsAggregationBuilder lev1Agg = AggregationBuilders.terms("id").field("id");
lev1Agg.size(1);
lev1Agg.subAggregation(AggregationBuilders.sum("familyMemberCount").field("membersInFamily"));
lev1Agg.subAggregation(AggregationBuilders.sum("totalKidsInFamily").field("kidsInFamily"));
Map<String, String> bucketsPathsMap = new HashMap<>();
bucketsPathsMap.put("familyMemberCount", "familyMemberCount");
bucketsPathsMap.put("totalKidsInFamily", "totalKidsInFamily");
Script script = new Script("params.familyMemberCount / params.totalKidsInFamily");
BucketScriptPipelineAggregationBuilder bs = PipelineAggregatorBuilders
.bucketScript("myScript", bucketsPathsMap, script);
lev1Agg.subAggregation(bs);
aggregation.subAggregation(lev1Agg);
searchSourceBuilder = new SearchSourceBuilder().aggregation(aggregation);
searchSourceBuilder.size(0);
Query built by above code
GET my-alias/_search
{
"size" : 0,
"aggregations" : {
"id" : {
"filter" : {
"terms" : {
"name" : [
"my-name"
],
"boost" : 1.0
}
},
"aggregations" : {
"id" : {
"terms" : {
"field" : "name",
"size" : 1,
"min_doc_count" : 1,
"shard_min_doc_count" : 0,
"show_term_doc_count_error" : false,
"order" : [
{
"_count" : "desc"
},
{
"_term" : "asc"
}
]
},
"aggregations" : {
"familyMemberCount" : {
"sum" : {
"field" : "membersInFamily"
}
},
"totalKidsInFamily" : {
"sum" : {
"field" : "kidsInFamily"
}
},
"myScript" : {
"bucket_script" : {
"buckets_path" : {
"familyMemberCount" : "familyMemberCount",
"totalKidsInFamily" : "totalKidsInFamily"
},
"script" : {
"source" : "params.familyMemberCount / params.totalKidsInFamily",
"lang" : "painless"
},
"gap_policy" : "skip"
}
}
}
}
}
}
}
}

Spring Data MongoDB addToSet() with complex object

I want to get data from MongoDB through Java application using Spring Data.
I did following MongoDB query and converted successfully to Java code
db.getCollection('financialMessage').aggregate([{
$match:{ createdDate: {
$gte: ISODate("2017-11-03 00:00:00.000Z"),
$lt: ISODate("2017-11-04 00:00:00") }}}, {
$group: { _id: {
consolidatedBatchId: "$consolidatedBatchId",
version: "$version"},
messages: { $addToSet: "$message" }}}, {
$sort: {
"_id.consolidatedBatchId": 1,
"_id.version": 1}
}])
The results looks like :
{
"_id" : {
"consolidatedBatchId" : "5f4e1d16-2070-48ef-8369-00004ec3e8ee",
"version" : 4
},
"messages" : [
"message1",
"message2",
"message3"
]
}
Java code for above query looks like :
Criteria filterCriteria = Criteria.where(CREATED_DATE)
.gte(startDate)
.lt(endDate);
Sort sort = new Sort(Sort.Direction.DESC, "consolidatedBatchId" ,"version");
Aggregation agg = Aggregation.newAggregation(
Aggregation.match(filterCriteria),
Aggregation.group("consolidatedBatchId", "version")
.addToSet("message").as("messages"),
Aggregation.sort(sort)
);
AggregationResults<FinancialMessageKey> aggregationResults =
mongoTemplate.aggregate(agg, FinancialMessage.class, FinancialMessageKey.class);
return aggregationResults.getMappedResults();
Now I do not find how to convert following MongoDB query code to Java code :
db.getCollection('financialMessage').aggregate([{
$match:{ createdDate: {
$gte: ISODate("2017-11-03 00:00:00.000Z"),
$lt: ISODate("2017-11-04 00:00:00")
}}}, {
$group: { _id: {
consolidatedBatchId: "$consolidatedBatchId",
version: "$version"},
messages: { $addToSet: {message: "$message",
createdDate: "$createdDate",
sender: "$sender",
receiver: "$receiver" }}}}, {
$sort: {
"_id.consolidatedBatchId": 1,
"_id.version": 1}
}])
With the following output :
{
"_id" : {
"consolidatedBatchId" : "5f4e1d16-2070-48ef-8369-00004ec3e8ee",
"version" : 4
},
"messages" : [
{
"message" : "message1",
"createdDate" : ISODate("2017-11-03T07:13:08.074Z"),
"sender" : "sender",
"receiver" : "receiver"
},
{
"message" : "message2",
"createdDate" : ISODate("2017-11-03T07:13:08.111Z"),
"sender" : "sender",
"receiver" : "receiver"
},
{
"message" : "message3",
"createdDate" : ISODate("2017-11-03T07:13:07.986Z"),
"sender" : "sender",
"receiver" : "receiver"
}
]
}
How this addToSet() could be write in Java in order to get List<'complex object'> instead of simple List ?
After few minutes of research on Google, I finally found how to do it.
public List<FinancialMessageKey> findFinancialMessageKeys(FinancialMessageQueryDTO financialMessageQueryDTO) {
Criteria filterCriteria = Criteria.where(CREATED_DATE)
.gte(startDate)
.lt(endDate);
Sort sort = new Sort(Sort.Direction.DESC, "consolidatedBatchId", "version");
Aggregation agg = Aggregation.newAggregation(
Aggregation.match(filterCriteria),
Aggregation.group(CONSOLIDATED_BATCH_ID, VERSION)
.addToSet(new BasicDBObject() {
{
put(CREATED_DATE, "$" + CREATED_DATE);
put(SENDER, "$" + SENDER);
put(RECEIVER, "$" + RECEIVER);
put(MESSAGE, "$" + MESSAGE);
}
}
).as("messages"),
Aggregation.sort(sort));
AggregationResults<FinancialMessageKey> aggregationResults =
mongoTemplate.aggregate(agg, FinancialMessage.class, FinancialMessageKey.class);
return aggregationResults.getMappedResults();
}

How to count number of occurence of specific field in a collection in Mongodb

I have my collection as shown below:
{
"_id" : NumberLong(366),
"file" : "xyz",
"clist" : {
"account" : "BFS",
"subAccount":"a"
},
},
{
"_id" : NumberLong(366),
"file" : "xyz",
"clist" : {
"account" : "BFS",
"subAccount":"b"
},
},
{
"_id" : NumberLong(366),
"file" : "xyz",
"clist" : {
"account" : "HC",
"subAccount":"c"
},
}
In that I have to group by account and count number of subAccount; for example:
{
account : "BFS",
subAccount : "b",
count : 1,
subAccount :"a",
count : 1
}
If for account BFS, subAccount b exists two times, then I should get output like this:
{
account : "BFS",
subAccount : "b",
count : 2,
subAccount : "a",
count : 1
}
May this help you...
db.coll.aggregate([ {
"$group" : {
"_id" : {
"account" : "$clist.account",
"subAccount" : "$clist.subAccount"
},
"count" : {
"$sum" : 1
}
}
}, {
$project : {
_id : 0,
"account" : "$_id.account",
"subAccount" : "$_id.subAccount",
"count" : "$count"
}
}, {
$group : {
_id : "$account",
"subaccounts" : {
"$push" : {
"subAccount" : "$subAccount",
"count" : "$count"
}
}
}
} ])

How to use MongoDB $let with Spring-Data?

I have a MongoDB collection of places. A typical place has most of the following fields:
{
"_id" : ObjectId("575014dc6b028f07bef53681"),
"_class" : "domain.model.PlaceV1",
"name" : "Γιασεμί",
"primary_photo_url" : "https://irs0.4sqi.net/img/general/original/34666238_STHSh6CHiC7hpAuB4rztRVg6cFc5ylfi15aRaR7zUuQ.jpg",
"seenDetails" : NumberLong(0),
"foursquare_checkins" : 646,
"foursquare_tips" : 28,
"keywords" : [
""
],
"verified" : 1,
"location" : {
"loc" : {
"type" : "Point",
"coordinates" : [
25.898318,
36.831486
]
},
"formattedAddress" : "Χώρα",
"locality" : "Amorgos",
"first_neighbourhood" : "Katapola",
"greek_locality" : "Αμοργός",
"greek_first_neighbourhood" : "Κατάπολα"
},
"contact" : {
"phone_numbers" : [
"+30 2285 074017"
]
},
"price" : {
"priceVotes" : NumberLong(0),
"price" : 0,
"priceVotesSum" : NumberLong(0)
},
"rating" : {
"rating" : 8,
"ratingVotes" : NumberLong(0),
"ratingVotesSum" : NumberLong(0)
},
"categories" : [
{
"cat_id" : NumberLong(10310061000),
"category" : "Café",
"greek_category" : "Καφετέρια",
"weight" : 4
},
{
"cat_id" : NumberLong(11610021000),
"category" : "Bar",
"greek_category" : "Μπαρ",
"weight" : 4
}
]
}
I want to make queries where the sorting will be based on a score that is a result of some expressions and conditions. From the mongo shell I have tried this:
db.place.aggregate([
{$match:{"location.locality":"Athens"}},
{$project:
{name:1, location:1, score:{
$let: {
vars:{ foursquare: {
$cond: { if: { $gte: [ "$foursquare_checkins", 500 ] }, then: 500, else: "$foursquare_checkins" }
},
rating: {$multiply:["$rating.rating", 100]},
},
in:{$add:["$$foursquare", "$$rating", "$seenDetails"]}
}
}
}
},
{$sort: {score: -1}}]).pretty();
This is a simple example of my queries. The score will contain more complex expressions like the distance from a location. The problem is that I cannot find a way to use the $let and the $cond operator in my Java code with Spring. Could anybody help?
You should be able to do this using nested DBObject and a Custom Aggregation Operation.
For Example:
Map operations = new HashMap();
operations.put("name", 1);
operations.put("location", 1);
operations.put("score", new BasicDBObject("$let", new BasicDBObject("vars", new BasicDBObject())));
Then you can create a CustomAggregationOperation to add this to your project
CustomAggregationOperation project = new CustomAggregationOperation(new BasicDBObject("$project", operation));
This will give you the following pipeline:
{ "$project" : { "score" : { "$let" : { "vars" : { }}} , "name" : 1 , "location" : 1}}
Then you can add your other stages:
Aggregation aggregate = Aggregation.newAggregation(match, project, sort);
public class CustomAggregationOperation implements AggregationOperation {
private DBObject operation;
public CustomAggregationOperation (DBObject operation) {
this.operation = operation;
}
#Override
public DBObject toDBObject(AggregationOperationContext context) {
return context.getMappedObject(operation);
}
}

Categories

Resources