Fetching all Grandchild's from single collection using Self join in mongoose - mongodb

Currently i have one collection "Category" which has a self join with ref on parent column. In that I have 3 Document.
Document 1 is the parent.
Document 2 is the child of Document 1
Document 3 is the child of Document 2
CategoryModel
var CategoryTable = new Schema({
categoryName : String,
parent : {
type : ObjectID,
ref : 'category',
default : null
}
});
MyCode
CategoryModel
.aggregate([
{
$match : { "parent" : null }
},
{
"$lookup":{
"from" : "categories",
"localField":"_id",
"foreignField":"parent",
"as": "child"
}
}
])
.exec((err,data) => {
if(err)
{
throw err;
}
res.send(data);
})
Current Output
[
{
"_id": "5d8de924b4672e2744dedbb9",
"parent": null,
"categoryName": "Software",
"__v": 0,
"child": [
{
"_id": "5d8de972b4672e2744dedbba",
"parent": "5d8de924b4672e2744dedbb9",
"categoryName": "Antivirus",
"__v": 0
}
]
}
]
Expected Output
[
{
"_id": "5d8de924b4672e2744dedbb9",
"parent": null,
"categoryName": "Software",
"__v": 0,
"child": [
{
"_id": "5d8de972b4672e2744dedbba",
"parent": "5d8de924b4672e2744dedbb9",
"categoryName": "Antivirus",
"__v": 0,
"child": [
{
"_id": "5d8e1a303bcfb6085c48e4dc",
"parent": "5d8de972b4672e2744dedbba",
"categoryName": "Quick Heal",
"__v": 0
}
]
}
]
}
]
I want to fetch all the child and subchild collection using aggregate. Can someone please help me in this?

Related

Retrieve highest score for each game using aggregate in MongoDB

I am working on a database of various games and i want to design a query that returns top scorer from each game with specific player details.
The document structure is as follows:
db.gaming_system.insertMany(
[
{
"_id": "01",
"name": "GTA 5",
"high_scores": [
{
"hs_id": 1,
"name": "Harry",
"score": 6969
},
{
"hs_id": 2,
"name": "Simon",
"score": 8574
},
{
"hs_id": 3,
"name": "Ethan",
"score": 4261
}
]
},
{
"_id": "02",
"name": "Among Us",
"high_scores": [
{
"hs_id": 1,
"name": "Harry",
"score": 926
},
{
"hs_id": 2,
"name": "Simon",
"score": 741
},
{
"hs_id": 3,
"name": "Ethan",
"score": 841
}
]
}
]
)
I have created a query using aggregate which returns the name of game and the highest score for that game as follows
db.gaming_system.aggregate(
{ "$project": { "maximumscore": { "$max": "$high_scores.score" }, name:1 } },
{ "$group": { "_id": "$_id", Name: { $first: "$name" }, "Highest_Score": { "$max": "$maximumscore" } } },
{ "$sort" : { "_id":1 } }
)
The output from my query is as follows:
{ "_id" : "01", "Name" : "GTA 5", "Highest_Score" : 8574 }
{ "_id" : "02", "Name" : "Among Us", "Highest_Score" : 926 }
I want to generate output which also provides the name of player and "hs_id" of that player who has the highest score for each game as follows:
{ "_id" : "01", "Name" : "GTA 5", "Top_Scorer" : "Simon", "hs_id": 2, "Highest_Score" : 8574 }
{ "_id" : "02", "Name" : "Among Us", "Top_Scorer" : "Harry", "hs_id": 1, "Highest_Score" : 926 }
What should be added to my query using aggregate pipeline?
[
{
$unwind: "$high_scores" //unwind the high_scores, so you can then sort
},
{
$sort: {
"high_scores.score": -1 //sort the high_scores, irrelevant of game, because we are going to group in next stage
}
},
{
//now group them by _id, take the name and top scorer from $first (which is the first in that group as sorted by score in descending order
$group: {
_id: "$_id",
name: {
$first: "$name"
},
Top_Scorer: {
$first: "$high_scores"
}
}
}
]

Mongoose - Calculate or merge value of data

I'm very new with mongoose. Can mongoose do something like i want to see how many linkview of James where store is Red only . The expected outcome should be linkview = 2 because 1+1. Please help
So this is User schema
{
"_id": "234",
"name": "James",
"__v": 0,
}
So this is affiliatelink schema
{
"_id": "11",
"store": "Red",
"linkview": "1",
"date": 12/12/12,
"affiliate": "James"
},
{
"_id": "22",
"store": "Red",
"linkview": "1",
"date": 13/12/12,
"affiliate": "James"
},
{
"_id": "33",
"store": "Blue",
"linkview": "1",
"date": 13/12/12,
"affiliate": "James"
}
Here is an aggregation you're looking for:
db.collection.aggregate([
{
$match: {
"affiliate": "James",
store: "Red"
}
},
{
$group: {
_id: null,
"linkview_count": {
$sum: 1
}
}
},
{
$project: {
_id: 0
}
}
])
The result will be:
[
{
"linkview_count": 2
}
]
Playground: https://mongoplayground.net/p/lPPhv9_w4iT

mongodb query update select nested fields

this is my document in mongo:
"calendar": {
"_id": "5cd26a886458720f7a66a3b8",
"hotel": "5cd02fe495be1a4f48150447",
"calendar": [
{
"_id": "5cd26a886458720f7a66a413",
"date": "1970-01-01T00:00:00.001Z",
"rooms": [
{
"_id": "5cd26a886458720f7a66a415",
"room": "5cd17d82ca56fe43e24ae5d3",
"price": "",
"remaining": 0,
"reserved": 0
},
{
"_id": "5cd26a886458720f7a66a414",
"room": "5cd17db6ca56fe43e24ae5d4",
"price": "",
"remaining": 0,
"reserved": 0
}
]
},
}
I need to update the objects in the inner rooms array . I tried a query that selects a matching element no syntax error but an error comes in:
"errmsg" : "The field 'calendar.0.rooms.0.price' must be an array but
is of type string in document {_id:
ObjectId('5cd26a886458720f7a66a3b8')}",
and this my query:
db.calendars.updateOne({_id:ObjectId("5cd26a886458720f7a66a3b8"),
"calendar":{"$elemMatch":{"_id":ObjectId("5cd26a886458720f7a66a413"),"rooms._id":
ObjectId("5cd26a886458720f7a66a415")}}},
{"$push":{"calendar.$[outer].rooms.$[inner].price":"100000"}}, {"arrayFilters":[{"outer._id":ObjectId("5cd26a886458720f7a66a413")},{"inner._id":ObjectId("5cd26a886458720f7a66a415")}]})
this is some reference I found in StackOverflow but not helped:
Updating a Nested Array with MongoDB
You can use below query
db.getCollection("test").updateOne(
{
"_id": ObjectId("5cd26a886458720f7a66a3b8"),
"calendar.calendar": {
"$elemMatch": {
"_id": ObjectId("5cd26a886458720f7a66a413"),
"rooms._id": ObjectId("5cd26a886458720f7a66a415")
}
}
},
{ "$set": { "calendar.calendar.$[outer].rooms.$[inner].price": "100000" } },
{
"arrayFilters": [
{ "outer._id": ObjectId("5cd26a886458720f7a66a413") },
{ "inner._id": ObjectId("5cd26a886458720f7a66a415") }
]
}
)
I will update my answer with some explanation afterward

Inserting/Updating Aggregated data on Mongo

Consider the following collection:
requests->
{
_id : ObjectId("573f28f49b0ffc283676f736"),
date : '2018-12-31',
userId: ObjectId("5c1239e93a7b9a72ef1c9197"),
serviceId: ObjectId("572e29b0116b5db3057bd821"),
status: 'completed'
}
I have an aggregate operation on requests collection which returns documents in the following format:
{
"grossRequests" : 2,
"grossData" : [
{
"date" : "2018-08-04",
"count" : 1,
"requests" : [
ObjectId("5b658147c73beb5ea3debc6e")
]
},
{
"date" : "2018-08-05",
"count" : 1,
"requests" : [
ObjectId("5b658160572faa5dd033fb48")
]
}
],
"netData" : [
{
"date" : "2018-08-05",
"count" : 1,
"requests" : [
ObjectId("5b658160572faa5dd033fb48")
]
}
],
"netRequests" : 1.0,
"userId" : ObjectId("5c1239e93a7b9a72ef1c9197"),
"serviceId" : "572e29b0116b5db3057bd821"
}
Above is the document to be inserted in cumulativeData
Now, I need to add all the documents returned by my aggregation operation, into a collection called cumulativeData.
The cumulativeData collection has one document per userId per serviceType.
I am running a query for specific date ranges and while inserting them into the collection, would like to "merge" the documents, rather than replacing them.
so, for example, if I am looping through all the documents returned by the aggregation operation using forEach, for each new document I get ({userId,serviceType} pair which is not present in the cumulativeData collection) I need to insert it as is and for every document which is already present in the collection, I need to update it as follows.
grossRequests -> add both values
grossData -> push the new values into the existing set
netData -> push the new values into the existing set
netRequests -> add both values
The operation would be as follows,
Existing doc in ** cumulativeData **
{
"grossRequests": 2,
"grossData": [{
"date": "2018-08-04",
"count": 1,
"requests": [
ObjectId("5b658147c73beb5ea3debc6e")
]
}, {
"date": "2018-08-05",
"count": 1,
"requests": [
ObjectId("5b658160572faa5dd033fb48")
]
}
],
"netData": [{
"date": "2018-08-05",
"count": 1,
"requests": [
ObjectId("5b658160572faa5dd033fb48")
]
}
],
"netRequests": 1,
"userId": ObjectId("5c1239e93a7b9a72ef1c9197"),
"serviceId": "572e29b0116b5db3057bd821"
}
New Document generated after aggregation on a new date range
{
"grossRequests": 2,
"grossData": [{
"date": "2018-08-04",
"count": 1,
"requests": [
ObjectId("5b658147c73beb5ea3debc8e")
]
}, {
"date": "2018-08-05",
"count": 1,
"requests": [
ObjectId("5b658160572faa5dd033fb4l")
]
}
],
"netData": [{
"date": "2018-08-05",
"count": 1,
"requests": [
ObjectId("5b658160572faa5dd033fb4l")
]
}
],
"netRequests": 1,
"userId": ObjectId("5c1239e93a7b9a72ef1c9197"),
"serviceId": "572e29b0116b5db3057bd821"
}
Final Result
{
"grossRequests": 4,
"grossData": [{
"date": "2018-08-04",
"count": 2,
"requests": [
ObjectId("5b658147c73beb5ea3debc6e") , ObjectId("5b658147c73beb5ea3debc8e")
]
}, {
"date": "2018-08-05",
"count": 2,
"requests": [
ObjectId("5b658160572faa5dd033fb48"), ObjectId("5b658160572faa5dd033fb4l")
]
}
],
"netData": [{
"date": "2018-08-05",
"count": 2,
"requests": [
ObjectId("5b658160572faa5dd033fb48"),ObjectId("5b658160572faa5dd033fb4l")
]
}
],
"netRequests": 2,
"userId": ObjectId("5c1239e93a7b9a72ef1c9197"),
"serviceId": "572e29b0116b5db3057bd821"
}
You can use below code to perform updates.
It involves two steps first step when record with userid/service id not found insert data else update data.
Within update step first update the top fields i.e count followed by iterating netData and grossData to perform the merge.
To perform merge we use the writeresult which has nmodified count to identify if we have to update the array value or insert new array value.
You can adjust below query to meet your needs.
db.getCollection('requests').aggregate(your aggregation query).forEach(function(doc) {
var user_id = doc.userId;
var service_id = doc.serviceId;
var gross_requests = doc.grossRequests;
var net_requests = doc.netRequests;
var gross_data = doc.grossData;
var net_data = doc.netData;
var matched = db.getCollection('cumulativeData').findOne({
"userId": user_id,
serviceId: service_id
});
if (matched == null) {
db.getCollection('cumulativeData').insert(doc);
} else {
db.getCollection('cumulativeData').update({
"userId": user_id,
serviceId: service_id
}, {
$inc: {
"grossRequests": gross_requests,
"netRequests": net_requests
}
});
gross_data.forEach(function(grdoc) {
var writeresult = db.getCollection('cumulativeData').update({
"userId": user_id,
serviceId: service_id,
"grossData.date": grdoc.date
}, {
$inc: {
"grossData.$.count": grdoc.count
},
$push: {
"grossData.$.requests": {
$each: grdoc.requests
}
}
});
if (writeresult.nModified == 0) {
db.getCollection('cumulativeData').update({
"userId": user_id,
serviceId: service_id
}, {
$push: {
"grossData": {
"count": grdoc.count,
"requests": grdoc.requests,
"date": grdoc.date
}
}
});
}
});
net_data.forEach(function(nrdoc) {
var writeresult = db.getCollection('cumulativeData').update({
"userId": user_id,
serviceId: service_id,
"netData.date": nrdoc.date
}, {
$inc: {
"netData.$.count": nrdoc.count
},
$push: {
"netData.$.requests": {
$each: nrdoc.requests
}
}
});
if (writeresult.nModified == 0) {
db.getCollection('cumulativeData').update({
"userId": user_id,
serviceId: service_id
}, {
$push: {
"netData": {
"count": nrdoc.count,
"requests": nrdoc.requests,
"date": nrdoc.date
}
}
});
}
});
}
});

Upsert KV pair in subdocument for specific rules

How to update a document and insert key-value in subdocument for specific rules?
MongoDB version: 3.4
Use this CLI to insert simulation data
db.country.insertMany([{"_id":"us","groups":[{"group":"1"},{"group":"2"} ]},{"_id":"eu","groups":[{"group":"1"},{"group":"2"}]}, {"_id":"jp","groups":[{"group":"2"}]}])
original data
db.country.find()
{
"_id": "us", "groups": [ { "group" : "1" }, { "group": "2" } ]
}
{
"_id": "eu", "groups": [ { "group" : "1" }, { "group" : "2" } ]
}
{
"_id": "jp", "groups": [ { "group" : "2" } ]
}
How to get this result? ( just add status: happy to group 1 )
{
"_id": "us", "groups": [ { "group" : "1", "status": "happy" }, { "group": "2" } ]
}
{
"_id": "eu", "groups": [ { "group" : "1", "status": "happy" }, { "group" : "2" } ]
}
{
"_id": "jp", "groups": [ { "group" : "2" } ]
}
I know how to select all groups that match group=1
db.country.aggregate([
{'$unwind': '$groups'},
{'$match': {'groups.group': '1'}} ,
{'$project': {'group': '$groups.group', _id:0 }}
])
{ "group" : "1" }
{ "group" : "1" }
and also know how to use update + $set like this
// { "_id": 1, "people": {"name": "tony" } }
db.test.update({_id: 1}, { $set: {'people.country': 'taiwan'}})
// { "_id": 1, "people": {"name": "tony" , "country": "taiwan" } }
but how to merge update + $set and aggregate function? Please help me.
pymongo is OK for me.
How to get this result? ( just add status: happy to group 1 )
Use $ to refer the position of the matched sub-document in array.
db.coll.update_many({'groups.group':'1'}, {'$set': {'groups.$.status': 'happy'}})
see more here