MongoDB: Set the value of array element from values another array - mongodb

I want to create a new array methods[].linkToRegistry[] from methods[].settings[].linkToRegistry, i.e. I go through all the settings[] elements and extract data from the linkToRegistry fields into a new array methods[].linkToRegistry[] . The only condition is to skip empty linkToRegistry[] elements (optional, but desirable). Accordingly, for each methods[] element, the linkToRegistry[] array must be different. But I can only do this:
Source collection:
[
{
"methods": [
{
"settings": [
{
"linkToRegistry": "yes"
}
],
},
{
"settings": [
{
"linkToRegistry": "true"
},
{
"linkToRegistry": "false"
}
],
},
{
"settings": [
{
"someField": "yes"
}
],
},
],
},
{
"methods": [
{
"settings": [
{
"linkToRegistry": "NO"
}
],
},
{
"settings": [
{
"linkToRegistry": ""
}
],
}
],
}
]
The code:
db.collection.update({
"methods.settings.linkToRegistry": {
$exists: true
}
},
[
{
"$set": {
"methods.linkToRegistry": "$methods.settings.linkToRegistry"
}
}
],
{
multi: true
})
Final collection:
[
{
"_id": ObjectId("5a934e000102030405000000"),
"methods": [
{
"linkToRegistry": [
[
"yes"
],
[
"true",
"false"
],
[]
],
"settings": [
{
"linkToRegistry": "yes"
}
]
},
{
"linkToRegistry": [
[
"yes"
],
[
"true",
"false"
],
[]
],
"settings": [
{
"linkToRegistry": "true"
},
{
"linkToRegistry": "false"
}
]
},
{
"linkToRegistry": [
[
"yes"
],
[
"true",
"false"
],
[]
],
"settings": [
{
"someField": "yes"
}
]
}
]
},
{
"_id": ObjectId("5a934e000102030405000001"),
"methods": [
{
"linkToRegistry": [
[
"NO"
],
[
""
]
],
"settings": [
{
"linkToRegistry": "NO"
}
]
},
{
"linkToRegistry": [
[
"NO"
],
[
""
]
],
"settings": [
{
"linkToRegistry": ""
}
]
}
]
}
]
What I want to see:
[
{
"methods": [
{
"linkToRegistry": [
[
"yes"
]
],
"settings": [
{
"linkToRegistry": "yes"
}
]
},
{
"linkToRegistry": [
[
"true",
"false"
]
],
"settings": [
{
"linkToRegistry": "true"
},
{
"linkToRegistry": "false"
}
]
},
{
"linkToRegistry": [],
"settings": [
{
"someField": "yes"
}
]
}
]
},
{
"methods": [
{
"linkToRegistry": [
[
"NO"
]
],
"settings": [
{
"linkToRegistry": "NO"
}
]
},
{
"linkToRegistry": [
[
""
]
],
"settings": [
{
"linkToRegistry": ""
}
]
}
]
}
]
Link:
https://mongoplayground.net/p/vau9JljsjTW
How can i do this?
Thanks a lot in advance!

I'm complite
db.getSiblingDB("ervk_core").getCollection("supervision").find({}).forEach(function (supervision) {
if (supervision.methods instanceof Array) {
supervision.methods.forEach(function (method) {
var linkToRegistry = []
if (method.settings instanceof Array) {
method.settings.forEach(function (setting) {
linkToRegistry.push(setting.linkToRegistry);
});
db.getSiblingDB("ervk_core").getCollection("supervision").updateOne(
{
_id: supervision._id,
methods: method
},
{
'$set':
{
[`methods.$.linkToRegistry`] : linkToRegistry
}
}
);
}
});
}
});

Related

Get previous and next inside sub array in MongoDB

I have a database as below.
[
{
"title": "Data 1",
"slug": "data-1",
"sub_docs": [
{
"name": "Sub Doc 1",
"sub_slug": "sub-doc-1",
"urls": [
"https://example.com/path-1",
]
},
{
"name": "Sub Doc 2",
"sub_slug": "sub-doc-2",
"urls": [
"https://example.com/path-2"
]
},
{
"name": "Sub Doc 3",
"sub_slug": "sub-doc-3",
"urls": [
"https://example.com/path-3",
]
}
]
}
]
When I give data-1 and sub-doc-2 as input, I want to get the following result.
{
"title": "Data 1",
"sub_doc_title": "Sub Doc 2",
"urls": [
"https://example.com/path-2"
],
"prev": {
"name": "Sub Doc 1",
"sub_slug": "sub-doc-1"
},
"next": {
"name": "Sub Doc 3",
"sub_slug": "sub-doc-3"
}
}
I'm a bit of a beginner at MongoDB.
How can I do this query? Do you have an idea?
That's a tough query for beginner, although I still consider myself a novice so maybe there's a better/easy way.
Here's one way you could do it.
db.collection.aggregate([
{
"$match": {
"slug": "data-1" // given argument
}
},
{
"$set": {
"reduceOut": {
"$reduce": {
"input": "$sub_docs",
"initialValue": {
"sub_doc_title": null,
"urls": [],
"prev": null,
"next": null
},
"in": {
"$cond": [
{
"$eq": [
"$$this.sub_slug",
"sub-doc-2" // given argument
]
},
{
"$mergeObjects": [
"$$value",
{
"sub_doc_title": "$$this.name",
"urls": "$$this.urls"
}
]
},
{
"$cond": [
{ "$eq": [ "$$value.sub_doc_title", null ] },
{
"$mergeObjects": [
"$$value",
{
"prev": {
"name": "$$this.name",
"sub_slug": "$$this.sub_slug"
}
}
]
},
{
"$cond": [
{ "$eq": [ "$$value.next", null ] },
{
"$mergeObjects": [
"$$value",
{
"next": {
"name": "$$this.name",
"sub_slug": "$$this.sub_slug"
}
}
]
},
"$$value"
]
}
]
}
]
}
}
}
}
},
{
"$replaceWith": {
"$mergeObjects": [
{
"title": "$title"
},
"$reduceOut"
]
}
}
])
Try it on mongoplayground.net.
Thanks rickhg12hs for your answer. I also found a solution to this question.
Here is my answer;
db.collection.aggregate([
{
"$match": {
"slug": "data-1"
}
},
{
"$addFields": {
"sub_doc_index": {
"$indexOfArray": [
"$sub_docs.sub_slug",
"sub-doc-2"
]
}
}
},
{
"$project": {
"title": 1,
"sub_doc_title": {
"$arrayElemAt": [
"$sub_docs.name",
"$sub_doc_index"
]
},
"urls": {
"$arrayElemAt": [
"$sub_docs.urls",
"$sub_doc_index"
]
},
"prev": {
"$cond": {
"if": {
"$eq": [
{
"$subtract": [
"$sub_doc_index",
1
]
},
-1
]
},
"then": {},
"else": {
"name": {
"$arrayElemAt": [
"$sub_docs.name",
{
"$subtract": [
"$sub_doc_index",
1
]
}
]
},
"slug": {
"$arrayElemAt": [
"$sub_docs.sub_slug",
{
"$subtract": [
"$sub_doc_index",
1
]
}
]
}
},
},
},
"next": {
"name": {
"$arrayElemAt": [
"$sub_docs.name",
{
"$add": [
"$sub_doc_index",
1
]
}
]
},
"slug": {
"$arrayElemAt": [
"$sub_docs.sub_slug",
{
"$add": [
"$sub_doc_index",
1
]
}
]
}
}
}
},
])

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

MongoDB $facet aggregation

I'm having a problem in my $facet aggregation. I want to know also if it is byAllProduct I will know that this product is the bestSeller, newlyAdded, and mostReviewed.
$facet: {
"byBestSeller": [
{ $sort: { "sold":-1 }},
{ $limit: 1 }
],
"byMostReviewed": [
{ $addFields: { "noOfReviews": {$size:"$reviews.message"}}},
{ $sort: { "noOfReviews":-1 }},
{ $limit: 1 }
],
"byNewlyAdded": [
{ $sort: { "createdAt":-1 }},
{ $limit: 1 }
],
"byAllProducts": [
{ $limit: 100 }
],
}
Now, my result is this.
"byBestSeller": [
{
"sold": 150,
"name": "Big Mac",
"shop": [
{
"name": "McDonald's"
}
],
"createdAt": {
"2020-05-11T01:55:17.623Z"
},
"reviews": [
{
"message": "Yummylicious"
},
{
"message": "Yummy burger"
}
]
}
],
"byMostReviewed": [
{
"sold": 100,
"name": "Spaghetti",
"shop": [
{
"name": "McDonald's"
}
],
"createdAt": {
"2020-06-11T01:55:17.623Z"
},
"reviews": [
{
"message": "Yummylicious"
},
{
"message": "Yummy Spaghetti"
},
{
"message": "Yummy super rich spag."
}
],
"noOfReviews": 3
}
],
"byNewlyArrived": [
{
"sold": 0,
"name": "Fries",
"shop": [
{
"name": "McDonald's"
}
],
"createdAt": {
"2020-7-11T01:55:17.623Z"
},
"reviews": [
{
"message": "Yummylicious"
},
]
}
],
"byAllProducts": [
{
"sold": 150,
"name": "Big Mac",
"shop": [
{
"name": "McDonald's"
}
],
"createdAt": {
"2020-05-11T01:55:17.623Z"
},
"reviews": [
{
"message": "Yummylicious"
},
{
"message": "Yummy burger"
}
]
}
{
"sold": 100,
"name": "Fries",
"shop": [
{
"name": "McDonald's"
}
],
"createdAt": {
"2020-06-11T01:55:17.623Z"
},
"reviews": [
{
"message": "Yummylicious"
},
{
"message": "Yummy Spaghetti"
},
{
"message": "Yummy super rich spag."
}
]
},
{
"sold": 0,
"name": "Fries",
"shop": [
{
"name": "McDonald's"
}
],
"createdAt": {
"2020-07-11T01:55:17.623Z"
},
"reviews": [
{
"message": "Yummylicious"
},
]
}
],
Is there a way so that even if it is included in all products I can know that it is the bestSeller, mostReviewed, and newlyarrived?

$elemMatch in an MongoDB aggregation

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()

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
}
}]
}