Remove from nested array by sub id , "nModified" : 0 - arrays

I'm trying to remove data from a sub array as follow but am having difficulties.
{
"_id" : "0",
"mainArray" : [
{
"price" : 12,
"informations" : [
{
"createdBy" : "0x957a1a87d653ea2218742aeea5a05f637b6509c4",
"orderId" : 1
},
{
"createdBy" : "0x957a1a87d653ea2218742aeea5a05f637b6509c4",
"orderId" : 2
}
]
},{
"price" : 45,
"informations" : [
{
"createdBy" : "0x957a1a87d653ea2218742aeea5a05f637b6509c4",
"orderId" : 5
},
{
"createdBy" : "0x957a1a87d653ea2218742aeea5a05f637b6509c4",
"orderId" : 6
}
]
}
I would like the output to be :
{
"_id" : "0",
"mainArray" : [
{
"price" : 12,
"informations" : [
{
"createdBy" : "0x957a1a87d653ea2218742aeea5a05f637b6509c4",
"orderId" : 1
},
{
"createdBy" : "0x957a1a87d653ea2218742aeea5a05f637b6509c4",
"orderId" : 2
}
]
},{
"price" : 45,
"informations" : [
{
"createdBy" : "0x957a1a87d653ea2218742aeea5a05f637b6509c4",
"orderId" : 5
}
]
}
I've tried this :
db.collection.update({ "_id": "0" }, { $pull: { 'mainArray.informations': { "orderId": 6 } } });
and
db.collection.update({ "_id": "0" }, { $pull: { 'mainArray.0.informations': { "orderId": 6 } } });
But both don't work, the best i'm getting :
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })
Anyone see where i'm going wrong ?

You need to use the positional operator '$' . $ is a positional operator which identifies an element in an array to update without explicitly specifying the position of the element in the array
db.collection.update({ "_id": "0" ,"mainArray.informations.orderId":6 },
{ $pull: { 'mainArray.$.informations': { "orderId": 6 } } });
Note : the array field must appear as part of the query document to figure out the matching position in mainArray.

Related

How to match foreign keys in MongoDB and perform following complex queries?

My database has 3 Collections :
Tour, 2) turism_ind, 3) customer
> db.tour.find({}).pretty()
{
"_id" : ObjectId("622385ab1b68d9136e48ba51"),
"source" : "Pune",
"destination" : "Kashmir"
}
{
"_id" : ObjectId("622385ba1b68d9136e48ba52"),
"source" : "Mumbai",
"destination" : "Shilong"
}
{
"_id" : ObjectId("622385ce1b68d9136e48ba53"),
"source" : "Nashik",
"destination" : "Goa"
}
> db.turism_ind.find({}).pretty()
{
"_id" : ObjectId("6223885d1b68d9136e48ba57"),
"ind_name" : "Veena World",
"package" : [
{
"pkg_id" : 111,
"tour_id" : ObjectId("622385ba1b68d9136e48ba52"),
"cost" : 85000
}
],
"cust_review" : [
{
"cust_id" : ObjectId("622387fa1b68d9136e48ba56"),
"rating" : 4
}
]
}
{
"_id" : ObjectId("622389191b68d9136e48ba58"),
"ind_name" : "GK Travels",
"package" : [
{
"pkg_id" : 222,
"tour_id" : ObjectId("622385ba1b68d9136e48ba52"),
"cost" : 82000
},
{
"pkg_id" : 223,
"tour_id" : ObjectId("622385ab1b68d9136e48ba51"),
"cost" : 78000
}
],
"cust_review" : [
{
"cust_id" : ObjectId("622387f51b68d9136e48ba55"),
"rating" : 5
}
]
}
{
"_id" : ObjectId("622389ae1b68d9136e48ba59"),
"ind_name" : "KK Tours",
"package" : [
{
"pkg_id" : 333,
"tour_id" : ObjectId("622385ce1b68d9136e48ba53"),
"cost" : 57000
},
{
"pkg_id" : 334,
"tour_id" : ObjectId("622385ab1b68d9136e48ba51"),
"cost" : 79000
}
],
"cust_review" : [
{
"cust_id" : ObjectId("622387f51b68d9136e48ba55"),
"rating" : 5
},
{
"cust_id" : ObjectId("622387ef1b68d9136e48ba54"),
"rating" : 4
}
]
}
> db.customer.find({}).pretty()
{
"_id" : ObjectId("622387ef1b68d9136e48ba54"),
"cust_name" : "Aniket",
"selected_pkg" : [
111
]
}
{
"_id" : ObjectId("622387f51b68d9136e48ba55"),
"cust_name" : "Nik",
"selected_pkg" : [
222,
333,
334
]
}
{
"_id" : ObjectId("622387fa1b68d9136e48ba56"),
"cust_name" : "Sham",
"selected_pkg" : [
111,
222
]
}
{
"_id" : ObjectId("62238c671b68d9136e48ba5a"),
"cust_name" : "John",
"selected_pkg" : [
111,
222,
223,
333,
334
]
}
I want to perform the following queries :
1] List all the details of expenses made by John on his first 3 trips.
Also display the total expenses
2] List the names of the customers who went on a tour to Shillong. [5]
It is possible to perform those queries on the given document or document structure is wrong?
I try this for 1st query
db.turism_ind.aggregate(
{$unwind : "$package"},
{$match : { "package.pkg_id" : {$in : [111,222,333] } } },
{$project :{ _id : 0 , package : 1, total_cost : {$sum : "$package.cost"} }}
)
Output (Not Correct)
{ "package" : { "pkg_id" : 111, "tour_id" : ObjectId("622385ba1b68d9136e48ba52"), "cost" : 85000 }, "total_cost" : 85000 }
{ "package" : { "pkg_id" : 222, "tour_id" : ObjectId("622385ba1b68d9136e48ba52"), "cost" : 82000 }, "total_cost" : 82000 }
{ "package" : { "pkg_id" : 333, "tour_id" : ObjectId("622385ce1b68d9136e48ba53"), "cost" : 57000 }, "total_cost" : 57000 }
Here is the aggregation for your first part. There might be some bug depending on your other documents inside your collections.
Here's a quick breakdown of your question.
List all the details of expenses made by John
Utilize $match to get john document
on his first 3 trips.
Utilize $project to slice the first 3 elements out and the do a $lookup on the turism_ind to grab the necessary documents
Also display the total expenses
Had to utilize the $reduce in a projection to flatten 2D array into 1D, and follow by another project to $sum all the cost together as total.
MongoDB Playground
db.customer.aggregate([
{
"$match": {
"cust_name": "John"
}
},
{
"$project": {
"firstThree": {
"$slice": [
"$selected_pkg",
0,
3
]
}
}
},
{
"$lookup": {
"from": "turism_ind",
"localField": "firstThree",
"foreignField": "package.pkg_id",
"as": "tour"
}
},
{
"$project": {
"_id": 0,
"packages": {
$reduce: {
input: "$tour.package",
initialValue: [],
in: {
$concatArrays: [
"$$value",
"$$this"
]
}
}
}
}
},
{
"$project": {
"packages": 1,
"total": {
"$sum": "$packages.cost"
}
}
}
])
Result
[
{
"packages": [
{
"cost": 85000,
"pkg_id": 111,
"tour_id": ObjectId("622385ba1b68d9136e48ba52")
},
{
"cost": 82000,
"pkg_id": 222,
"tour_id": ObjectId("622385ba1b68d9136e48ba52")
},
{
"cost": 78000,
"pkg_id": 223,
"tour_id": ObjectId("622385ab1b68d9136e48ba51")
}
],
"total": 245000
}
]

Update array at specific index by other filed in MongoDB

I have a collection, consist of name and data.
data is an array with 2 elements, each element is the object with code and qty.
{
"_id" : ObjectId("605c666a15d2612ed0afedd2"),
"name" : "Anna",
"data" : [
{
"code" : "a",
"qty" : 3
},
{
"code" : "b",
"qty" : 4
}
]
},
{
"_id" : ObjectId("605c666a15d2612ed0afedd3"),
"name" : "James",
"data" : [
{
"code" : "c",
"qty" : 5
},
{
"code" : "d",
"qty" : 6
}
]
}
I want to update the code of the first element to name of its document. The result I want is
{
"_id" : ObjectId("605c666a15d2612ed0afedd2"),
"name" : "Anna",
"data" : [
{
"code" : "Anna",
"qty" : 3
},
{
"code" : "b",
"qty" : 4
}
]
},
{
"_id" : ObjectId("605c666a15d2612ed0afedd3"),
"name" : "James",
"data" : [
{
"code" : "James",
"qty" : 5
},
{
"code" : "d",
"qty" : 6
}
]
}
I just google to find how to:
update array at a specific index (https://stackoverflow.com/a/34177929/11738185)
db.Collection.updateMany(
{ },
{
$set:{
'data.0.code': '$name'
}
}
)
But the code of the first element in data array is a string '$name', not a value (Anna, James)
{
"_id" : ObjectId("605c666a15d2612ed0afedd2"),
"name" : "Anna",
"data" : [
{
"code" : "$name",
"qty" : 3
},
{
"code" : "b",
"qty" : 4
}
]
},
{
"_id" : ObjectId("605c666a15d2612ed0afedd3"),
"name" : "James",
"data" : [
{
"code" : "$name",
"qty" : 5
},
{
"code" : "d",
"qty" : 6
}
]
}
update a field by the value of another field. It takes me to use pipeline updating (https://stackoverflow.com/a/37280419/11738185): the second param of updateMany is array (pipeline)
db.Collection.updateMany(
{ },
[{
$set:{
'data.0.code': '$name'
}
}]
)
and It adds field 0 to each element in data array
{
"_id" : ObjectId("605c666a15d2612ed0afedd2"),
"name" : "Anna",
"data" : [
{
"0" : {
"code" : "Anna"
},
"code" : "a",
"qty" : 3
},
{
"0" : {
"code" : "Anna"
},
"code" : "b",
"qty" : 4
}
]
},
{
"_id" : ObjectId("605c666a15d2612ed0afedd3"),
"name" : "James",
"data" : [
{
"0" : {
"code" : "James"
},
"code" : "c",
"qty" : 5
},
{
"0" : {
"code" : "James"
},
"code" : "d",
"qty" : 6
}
]
}
I can't find the solution for this case. Could anyone to help me? How can I update array at fixed index by other field. Thanks for reading!
1. update array at a specific index
You can't use internal fields as value of another fields, it will work only when you have external value to update like { $set: { "data.0.code": "Anna" } }.
2. update a field by the value of another field
Update with Aggregation pipeline can't allow to access data.0.code syntax.
You can try using $reduce in update with aggregation pipeline,
$reduce to iterate loop of data array, set empty array in initialValue of reduce, Check condition if initialValue array size is zero then replace code with name and merge with current object using $mergeObjects, else return current object,
$concatArrays to concat current object with initialValue array
db.collection.update({},
[{
$set: {
data: {
$reduce: {
input: "$data",
initialValue: [],
in: {
$concatArrays: [
"$$value",
[
{
$cond: [
{ $eq: [{ $size: "$$value" }, 0] },
{ $mergeObjects: ["$$this", { code: "$name" }] },
"$$this"
]
}
]
]
}
}
}
}
}],
{ multi: true }
)
Playground
I think easier would be another way.
Just save the model before and use it for updating after
var annaModel = nameModel.findOne({_id: "605c666a15d2612ed0afedd2" })
nameModel.findOneAndUpdate({_id: "605c666a15d2612ed0afedd2"},{$set:{'data.0.code': annaModel.name}})

Aggregating MongoDB, displaying top results from two separate collections

I have am trying to perform an aggregate function on my collection but I can't seem to fit the right query for the job.
My goal is to display the top 2 fastest laps on all maps and show the associated user first name and last name.
Here is my stats collections:
{
"_id" : ObjectId("5c86674d87e8cd468c850c86"),
"lapTime" : "1:32:29",
"map" : "France",
"driver" : [
ObjectId("5c7c499b555fa13f50c9c248")
],
"date" : ISODate("2019-03-11T13:49:01.472Z"),
"__v" : 0
}
{
"_id" : ObjectId("5c8667ec87e8cd468c850c87"),
"lapTime" : "2:32:34",
"map" : "France",
"driver" : [
ObjectId("5c7c499b555fa13f50c9c248")
],
"date" : ISODate("2019-03-11T13:51:40.895Z"),
"__v" : 0
}
{
"_id" : ObjectId("5c86674x87e8Sd567c120c86"),
"lapTime" : "1:12:29",
"map" : "France",
"driver" : [
ObjectId("5c7c499b555fa13f50c9c248")
],
"date" : ISODate("2019-03-11T10:49:01.472Z"),
"__v" : 0
}
{
"_id" : ObjectId("5c8667f887e8cd468c850c88"),
"lapTime" : "1:88:29",
"map" : "Italy",
"driver" : [
ObjectId("5c7c499b555fa13f50c9c248")
],
"date" : ISODate("2019-03-11T13:51:52.727Z"),
"__v" : 0
}
{
"_id" : ObjectId("5c866970c65910291c6f2000"),
"lapTime" : "1:34:29",
"map" : "Italy",
"driver" : [
ObjectId("5c80f78ca0ecdf26c83dfc8a")
],
"date" : ISODate("2019-03-11T13:58:08.135Z"),
"__v" : 0
}
{
"_id" : ObjectId("5c868532b5c50c17b0917f9e"),
"lapTime" : "1:43:33",
"map" : "Italy",
"driver" : [
ObjectId("5c80f78ca0ecdf26c83dfc8a")
],
"date" : ISODate("2019-03-11T15:56:34.869Z"),
"__v" : 0
}
Since I am passing the driver ID by reference here:
"driver":[ObjectId("5c7c499b555fa13f50c9c248")] , I want to display the driver's attributes from my users collection.
Here is one of my user objects:
{
"_id" : ObjectId("5c7c499b555fa13f50c9c248"),
"password" : "$2a$10$L..Pf44/R7yJfNPdikIObe04aiJaY/e94VSKlFscjgYOe49Y7iwJK",
"email" : "john.smith#yahoo.com",
"firstName" : "John",
"lastName" : "Smith",
"laps" : [],
"__v" : 0,
}
Here is what I tried so far:
db.getCollection('stats').aggregate([
{ $group: {
_id: { map: "$map" }, // replace `name` here twice
laps: { $addToSet: "$lapTime" },
driver:{$addToSet: "$driver"},
count: { $sum: 1 }
} },
{$lookup:
{
from: "users",
localField: "firstName",
foreignField: "lastName",
as: "driver"
}},
{ $match: {
count: { $gte: 2 }
} },
{ $sort : { count : -1} },
{ $limit : 10 }
]);
As a result, I am getting drivers as a empty array.
What I am actually trying to achieve is something like this:
{
"_id" : {
"map" : "France"
},
"laps" : [
"Jonathan Smith":"2:32:34",
"Someone Else":"1:32:29"
],
"count" : 2.0
}
I think this should work:-
db.getCollection('stats').aggregate([
{ $unwind: "$driver" },
{$lookup:
{
from: "users",
localField: "driver",
foreignField: "_id",
as: "driver"
}},
{ $group: {
_id: { map: "$map" }, // replace `name` here twice
laps: { $addToSet:
{
lapTime: "$lapTime",
driverName: "$driver.firstName" + "$driver.lastName"
}
},
count: { $sum: 1 }
} },
{ $match: {
count: { $gte: 2 }
} },
{ $sort : { count : -1} },
{ $limit : 10 }
]);

count inner array mongoose

I want to do a count on an array. This my model
modelDetail.aggregate([
{
$group: {
_id: '$main_section.dept',
count: {$sum: 'first_array.second_array.main_data'}
}
}
], function (err, result) {
if (err) {
//next(err);
console.log(err);
} else {
console.log(JSON.stringify(result));
res.json(result);
}
});
This is bringing out 0 for all the result. Output is below
[{"_id":"design","count":0},
{"_id":"training","count":0},
{"_id":"forecast","count":0},
{"_id":"internal audit","count":0},
{"_id":"research","count":0}]
I wanted to get the count of all the data
this is my schema
var userSchema = mongoose.Schema({
main_section :{
dept : String
},
first_array :[{//
floor : String,
second_array :[{//3rd level
name : String,//4th level
date : String
}]
}]
});
this is the data in the json
{ "_id" : "5bb39baf40f87f17f01734f8",
"main_section" : {
"dept" : "design"
},
"first_array" : [{ "_id" : "5bb39baf40f87f17f01734fc", "floor" : "2nd floor" ,
"second_array" : [
{ "_id" : "5bc102f6dff41a0e844dd2b7", "name" : "Blake Tyson", "date" : "2018-10-12T00:00:00Z" },
{ "_id" : "5bc102fddff41a0e844dd2bb", "name" : "Meagan Shawn", "date" : "2018-10-12T00:00:00Z" },
{ "_id" : "5bc1044dbba8b31ed42e7408", "name" : "Stephen Stone", "date" : "2018-10-12T00:00:00Z" }
]
}]
},
{ "_id" : "5bb39baf40f87f17f01734f8",
"main_section" : {
"dept" : "training"
},
"first_array" : [{ "_id" : "5bb39baf40f87f17f01734fc", "floor" : "1st floor",
"second_array" : [
{ "_id" : "5bc102f6dff41a0e844dd2b7", "name" : "Micheal Harrison", "date" : "2018-10-12T00:00:00Z" },
{ "_id" : "5bc102fddff41a0e844dd2bb", "name" : "Favour Reality", "date" : "2018-10-12T00:00:00Z" },
{ "_id" : "5bc1044dbba8b31ed42e7108", "name" : "Gift Myers", "date" : "2018-10-12T00:00:00Z" },
{ "_id" : "5bc1044dbba8b31ed42e71a1", "name" : "Drake Hills", "date" : "2018-10-12T00:00:00Z" },
{ "_id" : "5bc1044dbba8b31ed42e74c2", "name" : "Hashtan Priest", "date" : "2018-10-12T00:00:00Z" }
]
}]
},
{ "_id" : "5b98987f08925c0f5cd86780",
"main_section" : {
"dept" : "forecast"
},
"first_array" : [{ "_id" : "5b98987f08925c0f5cd86784", "floor" : "4th floor",
"second_array" : [ ]
}]
},
{ "_id" : "5b98187f08924c0f5ad86790",
"main_section" : {
"dept" : "internal audit"
},
"first_array" : [{ "_id" : "5b98987f08925c0f5cd86784", "floor" : "4th floor",
"second_array" : [ ]
}]
},
{ "_id" : "5b98187f08924c0f5ad86790",
"main_section" : {
"dept" : "research"
},
"first_array" : [{ "_id" : "5b98987f08925c0f5cd86784", "floor" : "4th floor",
"second_array" : [ ]
}]
}
Basically what i was hoping to do is to let each of the array produces the total number of elements
in the array for second alongside with the main section so for example
design would output 3
training would output 5
forecast would output 0
internal audit would output 0
research would output 0
So, any help is greatly appreciated!

MongoDB Querying Nested Arrays

I'm having some trouble with querying a Mongo Collection.
I have a Collection like this:
{
"_id" : "555bd34329de3cf232434ef2",
"cars" : [
{
"0" : {
"parts" : [
{
"name" : "x1",
"price" : 12
},
{
"name" : "x2",
"price" : 14
}
]
},
"1" : {
"parts" : [
{
"name" : "y1",
"price" : 8
},
{
"name" : "y2",
"price" : 12
}
]
}
}
]
}
I'd like to return just the following:
"parts" : [
{
"name" : "x1",
"price" : 12
},
{
"name" : "x2",
"price" : 14
}
]
In other words, I need to figure out how to query the Collection by two parameters at the same time:
where the ID matches "555bd34329de3cf232434ef2"
where the "name" of the part matches "x1"
Does anyone know how to do this kind of nested query?
Assuming a document structure like this:
{
"_id" : ObjectId("555bd34329de3cf232434ef2"),
"cars" : [
{
"parts" : [
{
"name" : "x1",
"price" : 12
},
{
"name" : "x2",
"price" : 14
}
]
},
{
"parts" : [
{
"name" : "y1",
"price" : 8
},
{
"name" : "y2",
"price" : 12
}
]
}
]
}
you can run the following query:
db.collection.find({ "_id": ObjectId("555bd34329de3cf232434ef2"), "cars.parts.name" : "x1" }, { "_id": 0, "cars.$": 1 })
which will get you pretty close to where you want to be:
{
"cars" : [
{
"parts" : [
{
"name" : "x1",
"price" : 12
},
{
"name" : "x2",
"price" : 14
}
]
}
]
}
You could get closer using the aggregation framework if that's not good enough...

Resources