$elemMatch in an MongoDB aggregation - mongodb

I am attempting to write a query builder for aggrigation's ( Our app has one for the find api, and we have some use cases where aggrigation's are needed )
in find one example we use it for is for pre-filtering document's to only one's they should have access to ( Reduces the load on our auth system ), But I can not seem to find a way to do that with aggregation
How would I re-write this in an aggrigation pipeline?
db.collection.find({
participants: {
"$elemMatch": {
scopes: {
"$in": [
"READWRITE",
"READ",
"OWNER"
],
"$nin": [
"EXCLUDE"
]
},
module_id: {
"$in": [
"bdc1ab4d-8c58-48cb-b811-e42a0d778df3",
"e0f26978-37ea-4415-9213-fb48dbfc3630"
]
}
}
}
})
Example Doc's
{
"title": "One Document",
"participants": [
{
"id": "2464b4a6-96c1-4dca-b764-34e424499e9f",
"module_name": "USER",
"module_id": "bdc1ab4d-8c58-48cb-b811-e42a0d778df3",
"roles": [
"MEMBER"
],
"scopes": [
"OWNER"
],
},
{
"id": "e0e58850-a6ce-4b89-a527-71bcdd57014a",
"module_name": "ORGANIZATION",
"module_id": "e0f26978-37ea-4415-9213-fb48dbfc3630",
"roles": [
"MEMBER"
],
"scopes": [
"READWRITE"
],
}
]
},
{
"title": "another document",
"participants": [
{
"id": "9edae792-6fdd-47f4-900d-e6bc11fa8e7a",
"module_name": "USER",
"module_id": "579b4b72-5dba-4d0c-bf21-2982e0a2ff94",
"roles": [
"MEMBER"
],
"scopes": [
"OWNER"
],
},
{
"id": "b72eea73-837d-492d-aa11-d0edb994b6ee",
"module_name": "USER",
"module_id": "bdc1ab4d-8c58-48cb-b811-e42a0d778df3",
"roles": [
"MEMBER"
],
"scopes": [
"READWRITE"
],
},
{
"id": "cae84997-079f-4a2b-9b4d-f1b4f152f697",
"module_name": "ORGANIZATION",
"module_id": "25b3a235-f6c3-45d6-b64b-bb1e48639bfb",
"roles": [
"MEMBER"
],
"scopes": [
"READWRITE"
],
}
]
}

The same can be done in the aggregation pipeline using $match.
db.collection.aggregate([
{
$match:{
"participants":{
$elemMatch:{
"scopes":{
$in:[ 'READWRITE', 'READ', 'OWNER' ],
$nin:[ 'EXCLUDE' ]
},
"module_id":{
$in:[ 'bdc1ab4d-8c58-48cb-b811-e42a0d778df3','e0f26978-37ea-4415-9213-fb48dbfc3630' ]
}
}
}
}
}
]).pretty()

Related

How to use update aggregation in MongoDb

my sample db:
"Employee": [ { "empeId": "e001",
"fName": "James",
"lName": "Bond",
"email": "jamesbond#hotmail.com",
"experience": [
"Database Design",
"SQL",
"Java" ]
},
{ "empeId": "e002",
"fName": "Harry",
"lName": "Potter",
"experience": [
"Data Warehouse",
"SQL",
"Spark Scala",
"Java Scripts" ]
} ],
"Project": [ { "projectId": "p001",
"projectTitle": "Install MongoDB" },
{ "projectId": "p002",
"projectTitle": "Install Oracle" },
{ "projectId": "p003",
"projectTitle": "Install Hadoop" } ],
"EmployeeProject": [ { "empeId": "e001",
"projectId": "p001",
"hoursWorked": 4 },
{ "empeId": "e001",
"projectId": "p003",
"hoursWorked": 2 },
{ "empeId": "e002",
"projectId": "p003",
"hoursWorked": 5 } ]
I want to update the array experience with 'test' of user with empeId: e001
desired output:
"Employee": [ { "empeId": "e001",
"fName": "James",
"lName": "Bond",
"email": "jamesbond#hotmail.com",
"experience": [
"Database Design",
"SQL",
"Java",
"test"
]
}]
I tried using
db.emp.updateOne([
{$unwind: "$Employee"},
{$match: {"Employee.emepId" : "e001" }}
],
{$push: {"Employee.experience" : "test"}})
and I get Syntax Error: Invalid property id #(shell):1:31
is this how the syntax for update with pipeline aggregation works?
The easiest and clean way(using arrayFilters):
db.collection.update({ Employee.empeId" : "e001"},
{
"$push": {
"Employee.$[x].experience": "test"
}
},
{
arrayFilters: [
{
"x.empeId": "e001"
}
]
})
Playground1
And via update/aggregation:
db.collection.update({ Employee.empeId" : "e001" },
[
{
$set: {
"Employee": {
$map: {
input: "$Employee",
as: "m",
in: {
$cond: [
{
$eq: [
"$$m.empeId",
"e001"
]
},
{
$mergeObjects: [
"$$m",
{
experience: {
$concatArrays: [
"$$m.experience",
[
"test"
]
]
}
}
]
},
"$$m"
]
}
}
}
}
}
])
Playground2

Aggregation: Mongodb aggregation query example

Record is database:
[
{
"title": "title1",
"author": [
{
"name": "user1",
"register": true
},
{
"name": "user2",
"register": true
}
],
"tags": [
"tag1",
"tag2",
"tag3"
]
},
{
"title": "title2",
"author": [
{
"name": "user1",
"register": true
},
{
"name": "user2",
"register": true
}
],
"tags": [
"tag1",
"tag2",
"tag3"
]
},
{
"title": "title3",
"author": [
{
"name": "user1",
"register": true
},
{
"name": "user2",
"register": true
}
],
"tags": [
"tag1",
"tag2",
"tag3"
]
}
]
expected output:
{"tag":"tag1", "titles":["title1","title2","title3"], "size":3}
{"tag":"tag2", "titles":["title2","title4"], "size":2}
Can someone help with aggregate query?
You can use group
$unwind to deconstruct the array
$group to regroup the based on tags
$project to show the desired output
Here is the code,
db.collection.aggregate([
{ "$unwind": "$tags" },
{
"$group": {
"_id": "$tags",
"titles": { "$push": "$title" }
}
},
{
$project: {
tag: "$_id",
titles: 1,
size: { $size: "$titles" },
_id: "$$REMOVE"
}
}
])
Working Mongo playground

How to write an aggregate query to figure the count in Mongodb

I have the following JSON context and I have a requirement to aggregate the distinct keywords under conversion_token group and the count it is repeated; for instance:
"conversion_token": [
{
"keyword": "DBMS",
"count":4,
"classify":2
}
the keyword DBMS is used multiple times under different constructs in the json provided, the aggregate should display the
"conversion_token": [
{
"keyword": "DBMS",
"count":6,
"classify":2
}
{
"keyword": "NVL",
"count":2,`enter code here`
"classify":2
}
etc..
How can I do this?
{
"select_emp": {
"specification": {
"input": [
"p_empno"
],
"declare_stmt": {
"anchorvariable": [
"V_ENAME",
"V_HIREDATE",
"V_TITLE",
"V_REPORTSTO",
"V_DISP_DATE",
"V_INS_COUNT",
"CITY_FROM"
],
"tablename_variable": [
"EMPLOYEE.V_ENAME",
"EMPLOYEE.V_HIREDATE",
"EMPLOYEE.V_TITLE",
"EMPLOYEE.V_REPORTSTO",
"EMPLOYEE.V_DISP_DATE",
"EMPLOYEE.V_INS_COUNT",
"EMPLOYEE.CITY_FROM"
]
}
},
"body": {
"select_stmt1": {
"columns": [
"FIRSNAME",
"HIREDATE",
"TITLE",
"REPORTSTO"
],
"tablename": [
"EMPLOYEE"
],
"conversion_token": [
{
"keyword": "NVL",
"count": 1,
"classify": 2
}
]
},
"select_stmt2": {
"columns": [
"CITY"
],
"tablename": [
"EMPLOYEE"
],
"conversion_token": [
{
"keyword": "DECODE",
"count": 1,
"classify": 3
}
]
},
"dbms_stmt1": {
"dbms_putline": [
"P_EMPNO",
"V_ENAME",
"V_DISP_DATE",
"V_REPORTSTO"
],
"conversion_token": [
{
"keyword": "DBMS",
"count": 1,
"classify": 2
}
]
},
"forloop1": {
"select_stmt": {
"columns": [
"EMPLOYEEID",
"ROWID"
],
"tablename": [
"EMPLOYEE"
],
"conversion_token": [
{
"keyword": "DBMS",
"count": 1,
"classify": 2
}
]
}
},
"merge_stmt1": {
"merge_into": "EMPLOYEE",
"merge_using": {
"columns": [
"EMPLOYEEID",
"LASTNAME",
"TITLE",
"BIRTHDATE",
"HIREDATE",
"ADDRESS",
"CITY",
"STATE",
"COUNTRY",
"POSTALCODE",
"PHONE",
"FAX",
"EMAIL",
"BONUS"
],
"tablename": [
"EMPLOYEE"
]
},
"merge_update": {
"columns": [
"BONUS"
],
"tablename": [
"EMPLOYEE"
]
},
"merge_delete": {
"columns": [
"BONUS"
],
"tablename": [
"EMPLOYEE"
]
},
"merge_insert": {
"columns": [
"EMPLOYEEID",
"LASTNAME",
"FIRSTNAME",
"TITLE",
"BIRTHDATE",
"HIREDATE",
"ADDRESS",
"CITY",
"STATE",
"COUNTRY",
"POSTALCODE",
"PHONE",
"FAX",
"EMAIL",
"BONUS"
],
"tablename": [
"EMPLOYEE"
]
},
"conversion_token": [
{
"keyword": "Merge",
"count": 1,
"classify": 4
}
]
},
"exception_handling1": {
"dbms_putline": [
"P_EMPNO"
],
"conversion_token": [
{
"keyword": "DBMS",
"count": 1,
"classify": 2
}
]
}
}
}
}
First you need to concat conversion_token arrays into one using project and $concatArrays. Then using $unwind, you can separate each conversion_token object to make them ready for aggregation. After $unwind, you can group them by keyword using $group and you can take sum of count.
db.collection.aggregate([
{
$project: {
conversion_tokens: {
$concatArrays:
[
"$select_emp.body.select_stmt1.conversion_token",
"$select_emp.body.select_stmt2.conversion_token",
"$select_emp.body.dbms_stmt1.conversion_token",
"$select_emp.body.forloop1.conversion_token",
"$select_emp.body.merge_stmt1.conversion_token",
"$select_emp.body.exception_handling1.conversion_token"
]
}
}
}, {
$unwind: "$conversion_tokens"
}, {
$group: {
_id: "$conversion_tokens.keyword",
count: {
$sum: "$conversion_tokens.count"
}
}
}
])
This will produce an array like:
{
"_id": "DBMS",
"count": 3
}, {
"_id": "NVL",
"count": 1
}, {
"_id": "DECODE",
"count": 1
}
If you want to change the _id key with keyword, you can use $project again.

How to find distinct values of fields using facet operation in mongodb

The filteredAccording part and the categorizedBy is working as expected using the query which I provided in the link but I am facing issues in the findDistinct part.
In mongodb I have the following data:
{
"_id": 10001,
"university": "SPYU",
"Courses": [
"English",
"French"
],
"dept": [
"Literature"
],
"type": [
"Autonomous"
],
"status": "ACTIVE",
"isMarked": true
},
{
"_id": 10002,
"university": "SPYU",
"Courses": [
"English",
"French"
],
"dept": [
"Literature"
],
"type": [
"Autonomous"
],
"status": "NON-ACTIVE",
"isMarked": true
}
I wanted the response to be:
"university": [
{
"name": "Literature",
"values": [
{
"_id": 10001,
"university": "SPYU",
"Courses": [
"English",
"French"
],
"dept": [
"Literature"
],
"type": [
"Autonomous"
],
"status": "ACTIVE",
"isMarked": true
},
{
"_id": 10002,
"university": "SPYU",
"Courses": [
"English",
"French"
],
"dept": [
"Literature"
],
"type": [
"Autonomous"
],
"status": "NON-ACTIVE",
"isMarked": true
}
]
}
],
"findDistinct": [
{​​​​​​​​
"name": "Courses",
"values": [
"English",
"French"
]
}​​​​​​​​,
{​​​​​​​​
"name": "Status",
"values": [
"ACTIVE",
"NON-ACTIVE"
]
}​​​​​​​​
]
I tried it using this link but the response is not coming as expected.
https://mongoplayground.net/p/XECZvRMmt3T
Right now, the response is coming like this
"universities": [
{
"name": "Literature",
"values": [
{
"_id": 10001,
"university": "SPYU",
"Courses": [
"English",
"French"
],
"dept": [
"Literature"
],
"type": [
"Autonomous"
],
"status": "ACTIVE",
"isMarked": true
},
{
"_id": 10002,
"university": "SPYU",
"Courses": [
"English",
"French"
],
"dept": [
"Literature"
],
"type": [
"Autonomous"
],
"status": "NON-ACTIVE",
"isMarked": true
}
]
}
],
"findDistinct": [
{​​​​​​​​
"Courses": [
"English",
"French"
]
}​​​​​​​​,
{​​​​​​​​
"status": [
"ACTIVE",
"NON-ACTIVE"
]
}​​​​​​​​
]
Any Help will be appreciated!!
Quick fixes in your query,
universities:
$addFields, remove $project and add only one operation for isMarked
$unwind deconstruct dept array
$group by dept and get values array of root
findDistinct:
$group by null and get unique courses array and status
$reduce to iterate loop of Courses nested array and get unique array using $setUnion
Make array of course and status in dest field
$unwind deconstruct dest array
$replaceRoot replace dest object to root
db.collection.aggregate([
{ $match: { university: "SPYU" }
},
{
$facet: {
universities: [
{ $addFields: { isMarked: { $in: ["French", "$Courses"] } } },
{ $unwind: "$dept" },
{
$group: {
_id: "$dept",
values: { $push: "$$ROOT" }
}
}
],
findDistinct: [
{
$group: {
_id: null,
Courses: { $addToSet: "$Courses" },
Status: { $addToSet: "$status" }
}
},
{
$project: {
_id: 0,
dist: [
{
name: "Courses",
values: {
$reduce: {
input: "$Courses",
initialValue: [],
in: { $setUnion: ["$$this", "$$value"] }
}
}
},
{
name: "Status",
values: "$Status"
}
]
}
},
{ $unwind: "$dist" },
{ $replaceRoot: { newRoot: "$dist" } }
]
}
}
])
Playground

Getting Lifetime Values from Google Analytics API

Google Analytics API documentation shows that, for fetching the lifetime values, the date ranges should not be specified. But when I make such a request (without date range), it returns empty dimension and metrics result. But when I use date range, it returns dimension and metrics values for that date range.
The following is an excerpt from the API documentation :
Date ranges should not be specified for cohorts or Lifetime value
requests.
For example, if I make the request without date range, as follows:
{
"reportRequests": [
{
"viewId": "XXXXXXXXX",
"dimensions": [
{
"name": "ga:date"
},
{
"name": "ga:eventLabel"
}
],
"metrics": [
{
"expression": "ga:totalEvents"
}
]
}
]
}
I get the following response:
{
"reports": [
{
"columnHeader": {
"dimensions": [
"ga:date",
"ga:eventLabel"
],
"metricHeader": {
"metricHeaderEntries": [
{
"name": "ga:totalEvents",
"type": "INTEGER"
}
]
}
},
"data": {
"totals": [
{
"values": [
"0"
]
}
]
}
}
]
}
However, if I include the date range,
{
"reportRequests": [
{
"viewId": "XXXXXXXX",
"dimensions": [
{
"name": "ga:date"
},
{
"name": "ga:eventLabel"
}
],
"metrics": [
{
"expression": "ga:totalEvents"
}
],
"dateRanges": [
{
"startDate": "2016-01-01",
"endDate": "2016-04-30"
}
]
}
]
}
I get the following response:
{
"reports": [
{
"columnHeader": {
"dimensions": [
"ga:date",
"ga:eventLabel"
],
"metricHeader": {
"metricHeaderEntries": [
{
"name": "ga:totalEvents",
"type": "INTEGER"
}
]
}
},
"data": {
"rows": [
{
"dimensions": [
"20160412",
"http://mytestblog.com/"
],
"metrics": [
{
"values": [
"1"
]
}
]
},
{
"dimensions": [
"20160412",
"http://mytestblog.com/2016/04/first-post.html"
],
"metrics": [
{
"values": [
"3"
]
}
]
},
{
"dimensions": [
"20160419",
"http://mytestblog.com/"
],
"metrics": [
{
"values": [
"4"
]
}
]
},
{
"dimensions": [
"20160419",
"http://mytestblog.com/2016/04/fourth.html"
],
"metrics": [
{
"values": [
"13"
]
}
]
}
],
"totals": [
{
"values": [
"21"
]
}
],
"rowCount": 4,
"minimums": [
{
"values": [
"1"
]
}
],
"maximums": [
{
"values": [
"13"
]
}
]
}
}
]
}
Why is it that, even though specified in the documentation, I have to specify date range in the ReportRequest to get the values? Am I misunderstanding the meaning of Lifetime values here?
The reportRequest object should have either a value for dateRanges or a definition value for cohortGroup. When you omit both the requests assumes the default values for a startDate of 7daysAgo and an endDate of yesterday.
The correct interpretation of the docs is that the reportRequest should not have a dateRange defined for cohort and LTV requests. But in order to make a cohort or lifetime value request you must add a cohort definition. For Lifetime value requests the cohort definition should have a specific dateRange in addition to the lifetimeValue field set to true:
POST https://analyticsreporting.googleapis.com/v4/reports:batchGet
{
"reportRequests": [
{
"viewId": "XXXX",
"dimensions": [
{"name": "ga:cohort" },
{"name": "ga:cohortNthWeek" }],
"metrics": [
{"expression": "ga:cohortTotalUsersWithLifetimeCriteria"},
{"expression": "ga:cohortRevenuePerUser"}
],
"cohortGroup": {
"cohorts": [{
"name": "cohort 1",
"type": "FIRST_VISIT_DATE",
"dateRange": {
"startDate": "2015-08-01",
"endDate": "2015-09-01"
}
},
{
"name": "cohort 2",
"type": "FIRST_VISIT_DATE",
"dateRange": {
"startDate": "2015-07-01",
"end_date": "2015-08-01"
}
}],
"lifetimeValue": True
}
}]
}