count array object with condition in mongodb and spring-boot - java

I want to count the number of videos in each mediaList and then return the total number of videos in the document.
{
_id: ObjectId("5f9d8d02d7bb2c56b678614c"),
mainTopicId: "5f8066dad0b65b3b9ca67e4b",
subTopicId: "5f9d89f5d7bb2c56b678614b",
userIdLiked: [
"5fe4560f19050e372a2a17f8",
"5f8061e0d0b65b3b9ca67e49"
],
likeCount: NumberInt(2),
mediaList: [
{
url: "ad491eb8-7f28-47da-9d35-fe83bb4f8eb0.mp4",
mediaType: "VIDEO"
}
],
text: "this sample text",
title: "sample title",
created: ISODate("2020-10-31T16:12:50.615Z"),
updated: ISODate("2020-10-31T16:12:50.615Z"),
},
{
_id: ObjectId("5f9d8f95d7bb2c56b678614e"),
mainTopicId: "5f8066dad0b65b3b9ca67e4b",
subTopicId: "5f67816fb2ae8c3e1f203946",
userIdLiked: [
"5f8061e0d0b65b3b9ca67e49"
],
mediaList: [
{
url: "5633c4b2-d3aa-4d7c-8419-1403e43674b1.mp4",
mediaType: "VIDEO"
}
],
text: "sample text 2",
title: "sample title 2",
created: ISODate("2020-10-31T16:23:49.488Z"),
updated: ISODate("2020-10-31T16:23:49.488Z"),
_class: "com.example.api.pregnancy.models.Content"
}
I use this Spring-boot code for sum likeCount in document.
now i want count total mediaType when is VIDEO type
Aggregation agg = Aggregation.newAggregation(
project("subTopicId").and("likeCount").as("sumLikeCount"),
group("subTopicId").sum("sumLikeCount").as("totalCount")
);

The code
Aggregation aggregation = Aggregation.newAggregation(
p-> new Document("$project",
new Document("totalVideos",
new Document("$size",
new Document("$filter",
new Document("input","$mediaList").append("cond",
new Document("$eq",Arrays.asList("$$this.mediaType","VIDEO"))
)
)
)
)
)
).withOptions(AggregationOptions.builder().allowDiskUse(Boolean.TRUE).build());
Spring-data doesn't provide few operation like $addFields, $filter. So what we can do is to go with a trick using BSON
Note : This code is not tested, Hope, it would work. It was written based on working Mongo playground

Related

mongodb: updateOne set array of string if elements non present, leave unaltered if present

I have the following document in mongodb:
{
"_id":"43434",
"mail": "test#gmail.com"
"category": ["Alimentari","Eventi","Ristorante","Servizi"]
}
I would like to write java code so that if:
I have the following Array of string in input ["Alimentari","Eventi","Ristorante"], the document remain unchanghed
with the following array string ["Alimentari","Bar"] the document will be:
{
"_id":"43434",
"mail": "test#gmail.com"
"category": ["Alimentari","Eventi","Ristorante","Servizi","Bar"]
}
if I pass an array of just one string ["Alimentari"], the document remain unchanghed
if I pass the following ["Grande Distribuzione"], the document will be
{
"_id":"43434",
"mail": "test#gmail.com"
"category": ["Alimentari","Eventi","Ristorante","Servizi","Grande Distribuzione"]
}
I tried with this code
String[] category= {"Alimentari","Eventi","Ristorante"};
collection.updateOne(
new BasicDBObject("_id", new ObjectId(_id)),
new BasicDBObject("$set", new BasicDBObject("category", category));
but the resulting document is:
{
"_id":"43434",
"mail": "test#gmail.com"
"category": ["Alimentari","Eventi","Ristorante"]
}
Could you please help me ?
Thank you
For MongoDB query answer, you need $addToSet with $each to add multiple values into the category field if the value doesn't exist.
db.collection.update({
_id: "43434"
},
{
"$addToSet": {
"category": {
"$each": [
"Alimentari",
"Bar"
]
}
}
})
Sample Mongo Playground

How to project array element field in Spring Data Mongo DB Aggregation

How to project embedded array element field in Spring Data MongoDB Aggregation with the document sample below, I tried:
project("customers.id")
project("customers.[].id")
project("customers.?.id")
project("$customers.id")
but didn't work.
Result document without projection:
{
"id": "group1",
"name": "Default Identity Management",
"warningThreshold": 900000,
"tariffId": "TR_0001",
"active": false,
"customers": [
{
"id": "1",
"name": "David",
"properties": [
{
"name": "phone",
"value": "678"
}
],
"roles": [
"dev"
]
},
{
"id": "2",
"name": "Peter",
"properties": [
{
"name": "phone",
"value": "770"
}
],
"roles": [
"techsales",
"dev"
]
}
]
}
Expected document like this:
{
"id" : "group1",
"name" : "Group1",
"tariffId" : "TR_0001",
"warningThreshold" : 900000,
"customers" : [
{
"id" : "1",
"name" : "David",
"properties" : [
{
"name" : "phone",
"value" : "678"
}
]
},
{
"id" : "2",
"name" : "Peter",
"properties" : [
{
"name" : "phone",
"value" : "770"
}
]
}
]
}
I would like to include customers[].id, customers[].name, customers[].properties.
I'd been trying to figure this out for a while now, but couldn't. And the other posts here on stackoverflow, and other places on the internet, didn't provide the solution I was looking for.
My problem was similar to the original author's: There's a document, which has a field which is an array of documents. I wanted to query all the top level fields in the document, and exclude a single field from the documents within the array.
s7vr's answer in the comments for the question did the job for me! Just re-posting that here since most people don't go through all the comments, and it is a really useful answer, that saved me from writing a lot of crappy code! :D
AggregationOperation project = new AggregationOperation() {
#Override
public Document toDocument(AggregationOperationContext aggregationOperationContext) {
return new Document("$project", new Document("arrayField.nestedFieldToExclude", 0));
}
};
With Lambda:
AggregationOperation project = aggregationOperationContext -> new Document("$project", new Document("arrayField.nestedFieldToExclude", 0));
Overall pipeline:
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(criteria),
Aggregation.sort(Sort.Direction.DESC, "createdOn"),
project);
I just wish there was a cleaner way to do this with the Spring MongoDB Data API directly, rather than using it this way with lambda functions.
Also, please note that the method AggregationOperation.toDocument(AggregationOperationContext aggregationOperationContext) has been deprecated as of spring-data-mongodb version 2.2.

I need to rename one field in document

Here is the code :
AggregateIterable<Document> result = chatLogCollection.aggregate(Arrays.asList(
new Document("$match", new Document("displayName", "user")),
new Document("$group", new Document("_id","$sessionGUID")
.append("time", new Document("$first","$ts"))
.append("makerID", new Document("$first","$makerID"))),
new Document("$sort", new Document("time", -1)),
new Document("$skip", skip),
new Document("$limit", limit)
));
This will generate the below type out put.
{
"displayName": "test test",
"_id": "123a54be-4b69-cd49-edb3-9b264fea077b",
"time": {
"$date": 1499759619016
},
"makerID": "xxxxx"
}
I need to format this to look like this:
{
"displayName": "test test",
"sessionID": "123a54be-4b69-cd49-edb3-9b264fea077b",
"time": {
"$date": 1499759619016
},
"makerID": "xxxxx"
}
That means i need to appear _id as sessionId. Please help me to do that.I am using mongoDB, java and windows 7.

Data Difference in GA Core Reporting API and GA dashboard

I am using Core reporting API (V4) through java to extract data with custom dimension.
There are two Scenarios. One in which i use two dimensions (pagePath and hostname) and in another i use just one dimension (pagePath only).
Issue is that i am observing difference in both requests. On using one dimension, data is exact copy of what I See on GA Dashboard. But on using two dimensions, This data i receive is not conforming with DA Dashboard.
Refer Code below -
DateRange dateRange = new DateRange();
dateRange.setStartDate("2016-08-01");
dateRange.setEndDate("2016-08-01");
Metric sessions = new Metric().setExpression("gaSmiley Tongueageviews").setAlias("PageViews");
Metric avgtime = new Metric().setExpression("ga:timeOnPage").setAlias("Time On Page");
List<Metric> list = new ArrayList<Metric>();
list.add(sessions);
list.add(avgtime);
Dimension dimension1 = new Dimension().setName("gaSmiley TongueagePath");
Dimension dimension2 = new Dimension().setName("ga:hostname");
List<Dimension> listd = new ArrayList<Dimension>();
listd.add(dimension1);
listd.add(dimension2); //---Error Occurs on Addition of this
List<DimensionFilter> filterList = new ArrayList<DimensionFilter>();
DimensionFilter dimFilter = new DimensionFilter().setDimensionName("gaSmiley TongueagePath").setOperator("REGEXP")
.setExpressions(Arrays.asList("53474130"));
filterList.add(dimFilter);
DimensionFilterClause clause = new DimensionFilterClause().setFilters(filterList);
clause.setFilters(filterList);
List<GetReportsResponse> responseList = new ArrayList<GetReportsResponse>();
String pageToken = "";
while(pageToken != null){
ReportRequest request = new ReportRequest().setViewId(VIEW_ID).setDateRanges(Arrays.asList(dateRange))
.setDimensions(listd).setMetrics(list).setDimensionFilterClauses(Arrays.asList(clause));
System.out.println(request);
if(pageToken != null && !pageToken.isEmpty()){
request.setPageToken(pageToken); }
ArrayList<ReportRequest> requests = new ArrayList<ReportRequest>();
requests.add(request);
GetReportsRequest getReport = new GetReportsRequest().setReportRequests(requests);
GetReportsResponse response = service.reports().batchGet(getReport).execute();
System.out.println(response);
responseList.add(response);
pageToken = response.getReports().get(0).getNextPageToken();
break;
}
When ever I use "dimension2" in query Data comes out to be (3 records, whose data is different from what I see on dashboard)-
{
"dimensions": ["m.photos.timesofindia.com/celebs/celeb-themes/celebs-at-airport/articleshow/53474130.cms",
"m.photos.timesofindia.com"],
"metrics": [{
"values": ["12",
"19.0"]
}]
}{
"dimensions": ["m.photos.timesofindia.com/celebs/celeb-themes/celebs/celeb-themes/celebs-at-airport/articleshow/53474130.cms",
"m.photos.timesofindia.com"],
"metrics": [{
"values": ["231",
"698.0"]
}]
}{
"dimensions": ["photogallery.indiatimes.com/celebs/celeb-themes/celebs-at-airport/articleshow/53474130.cms",
"photogallery.indiatimes.com"],
"metrics": [{
"values": ["168",
"648.0"]
}]
}
But When i re-run my code without the "hostname" dimension, Data i see is conforming with what is visible on dashboard. Response in this case is -
{
"dimensions": ["m.photos.timesofindia.com/celebs/celeb-themes/celebs/celeb-themes/celebs-at-airport/articleshow/53474130.cms"],
"metrics": [{
"values": ["217",
"746.0"]
}]
}{
"dimensions": ["photogallery.indiatimes.com/celebs/celeb-themes/celebs-at-airport/articleshow/53474130.cms"],
"metrics": [{
"values": ["181",
"2031.0"]
}]
}
Current Core Reporting API version used is V4.
I have tried sampling also. But there was no difference is response.
I am seeing difference in data once i add "hostname" dimension.
Please help me out here.
Warm Regards,
Vibhav

Java mongodb - find then average

Okey, let's start. Imagine that we have the next mongo collection:
{
"city": "TWENTYNINE PALMS",
"loc": [-116.06041, 34.237969],
"pop": 11412,
"state": "CA",
"_id": "92278"
}
{
"city": "NEW CUYAMA",
"loc": [-74.823806, 34.996709],
"pop": 80,
"state": "CA",
"_id": "93254"
}
{
"city": "WATERBURY",
"loc": [-72.996268, 41.550328],
"pop": 25128,
"state": "CT",
"_id": "06705"
}
Notice that loc array is [latitude,longitude]
I would like to obtain using java mongo driver the "pop" average of the cities that have the altitude beetwen -75,-70.
So, using SQL I know that the query is:
SELECT avg(pop)
WHERE loc.altitude > -75 AND lloc.altitude < -70
I am very noob in mongodb, this is my current code:
BasicDBObject doc = new BasicDBObject("loc.0", new BasicDBObject("$gte",
-75).append("$lte", -70));
DBCursor cursor = collection.find(doc);
The previous code returns me all the documents that altitude are beetwen (-75,-70), but I do not know how to obtain the average,using mongo driver, I know that I can iterate over results using java..
Thank you
Use the aggregation framework with following aggregation pipeline (Mongo shell implementation):
db.collection.aggregate([
{
"$match": {
"loc.0": { "$gte": -75 },
"loc.1": { "$lte": 70 }
}
},
{
"$group": {
"_id": 0,
"average": {
"$avg": "$pop"
}
}
}
])
With the example above, this outputs to console:
/* 1 */
{
"result" : [
{
"_id" : 0,
"average" : 12604
}
],
"ok" : 1
}
With Java, this can be implemented as follows:
DBObject match = new BasicDBObject();
match.put("loc.0", new BasicDBObject("$gte", -75));
match.put("loc.1", new BasicDBObject("$lte", 70));
DBObject groupFields = new BasicDBObject( "_id", 0);
groupFields.put("average", new BasicDBObject( "$avg", "$pop"));
DBObject group = new BasicDBObject("$group", groupFields);
AggregationOutput output = collection.aggregate( match, group );

Categories