how to retrive single element of a nested array in mongodb? - mongodb

let's say i have documents like this in my mongodb:
{
id: 1,
names: [
{
id: 2,
first: [
{
id: 3,
name: "alice",
},
{
id: 4,
name: "bob",
},
]
},
{
id: 5,
first: [
{
id: 6,
name: "berhe",
},
{
id: 7,
name: "belay",
},
]
}
]
}
{
id: 8,
names: [
{
id: 9,
first: [
{
id: 10,
name: "gemechu",
},
{
id: 11,
name: "samy",
},
]
},
{
id: 12,
first: [
{
id: 13,
name: "helen",
},
{
id: 14,
name: "natu",
},
]
}
]
}
now how to retrieve a value with
id=8,
names.id = 9,
names.first.id=10
which is like this:
{
id:10,
name:"gemechu"
}

$match to filter documents by all conditions using $elemMatch,
$unwind deconstruct names array
$match names id condition
$unwind deconstruct first array
$match first id condition
$project to format result
db.collection.aggregate([
{
$match: {
id: 8,
names: {
$elemMatch: {
id: 9,
"first.id": 10
}
}
}
},
{ $unwind: "$names" },
{ $match: { "names.id": 9 } },
{ $unwind: "$names.first" },
{ $match: { "names.first.id": 10 } },
{
$project: {
id: "$names.first.id",
name: "$names.first.name"
}
}
])
Playground

Related

Field combination in an array where another field is the same in MongoDB?

I want to find matches with the same gender and insert them into a new field array aka names but I am unable to solution using MongoDB. Or mongooese.
Input example:
db.students.insertMany([
{ id: 1, name: "Ryan", gender: "M" },
{ id: 2, name: "Joanna", gender: "F" },
{ id: 3, name: "Andy", gender: "M" },
{ id: 4, name: "Irina", gender: "F" }
]);
Desired output:
[
{ gender: "M", names: ["Ryan","Andy"]},
{ gender: "F", names: ["Joanna","Irina"]}
]
Note: the table has many records and I do not know those gender/name pairs in advance
I try this but no results. I don't know how I should write this query.
db.students.aggregate([
{
$group:{
names : {$push:"$name"},
}
},
{ "$match": { "gender": "$gender" } }
])
You did not specify how to group. Try this one:
db.students.aggregate([
{
$group: {
_id: "$gender",
names: { $push: "$name" }
}
},
{
$set: {
gender: "$_id",
_id: "$$REMOVE"
}
}
])

MongoDB query to find top store from list of orders

I'm pretty new to Mongo. I have two collections that look as follows.
Order collection
[
{
id: 1,
price: 249,
store: 1,
status: true
},
{
id: 2,
price: 230,
store: 1,
status: true
},
{
id: 3,
price: 240,
store: 1,
status: true
},
{
id: 4,
price: 100,
store: 2,
status: true
},
{
id: 5,
price: 150,
store: 2,
status: true
},
{
id: 6,
price: 500,
store: 3,
status: true
},
{
id: 7,
price: 70,
store: 4,
status: true
},
]
Store Collection
[
{
id: 1,
name: "Store A",
status: true
},
{
id: 2,
name: "Store B",
status: true
},
{
id: 3,
name: "Store C",
status: true
},
{
id: 4,
name: "Store D",
status: false
}
]
How to find the top store from the list of orders, which should be based on the total sales in each store.
I have tried the following
db.order.aggregate([
{
"$match": {
status: true
}
},
{
"$group": {
"_id": "$store",
"totalSale": {
"$sum": "$price"
}
}
},
{
$sort: {
totoalSale: -1
}
}
])
I got the sorted list of stores from the above snippets. But I want to add store details along with total sales.
For more: https://mongoplayground.net/p/V3UH1r6YRnS
Expected Output
[
{
id: 1,
name: "Store A",
status: true,
totalSale: 719
},
{
id: 1,
name: "Store c",
status: true,
totalSale: 500
},
{
_id: 2,
id: 1,
name: "Store B",
status: true,
totalSale: 250
},
{
_id: 4,
name: "Store D",
status: true,
totalSale: 70
}
]
$lookup - store collection joins order collection and generate new field store_orders.
$set - Filter order with status: true from store_orders.
$set - totalSale field sum for store_orders.price.
$sort - Sort totalSale by descending.
$unset - Remove store_orders field.
db.store.aggregate([
{
$lookup: {
from: "order",
localField: "id",
foreignField: "store",
as: "store_orders"
}
},
{
$set: {
"store_orders": {
$filter: {
input: "$store_orders",
as: "order",
cond: {
$eq: [
"$$order.status",
true
]
}
}
}
}
},
{
$set: {
"totalSale": {
"$sum": "$store_orders.price"
}
}
},
{
$sort: {
totalSale: -1
}
},
{
$unset: "store_orders"
}
])
Sample Mongo Playground
You can start from store collection, $lookup the order collection, $sum the totalSales, then wrangle to your expected form
db.store.aggregate([
{
"$lookup": {
"from": "order",
let: {
id: "$id"
},
pipeline: [
{
$match: {
$expr: {
$eq: [
"$$id",
"$store"
]
}
}
},
{
$group: {
_id: null,
totalSale: {
$sum: "$price"
}
}
}
],
"as": "totalSale"
}
},
{
$unwind: "$totalSale"
},
{
$addFields: {
totalSale: "$totalSale.totalSale"
}
},
{
$sort: {
totalSale: -1
}
}
])
Here is the Mongo playground for youre reference.

Mongodb - Merge nested subdocuments array based on specific properties

I've built an aggregation that is returning this data:
[
{
"users": [
{
_id: 1,
name: "John",
age: 31
},
{
_id: 2,
name: "Jane",
age: 26
}
],
"teams": [
{
id: 1,
name: "Team 1",
color: "yellow"
},
{
id: 2,
name: "Team 2",
color: "red"
}
],
"moreTeams": [
{
id: 1,
name: "Team 1 - More",
},
{
id: 2,
name: "Team 2 - More",
},
{
id: 3,
name: "Team 3 - More",
extra: "extra"
}
]
}
]
How can I group "teams" and moreTeams into the same array (based on id), keeping all properties and eventually overriding where they have the same names?
Here's my desired result:
[
{
"users": [
{
_id: 1,
name: "John",
age: 31
},
{
_id: 2,
name: "Jane",
age: 26
}
],
"groupedTeams": [
{
id: 1,
name: "Team 1 - More",
color: "yellow"
},
{
id: 2,
name: "Team 2 - More",
color: "red"
},
{
id: 3,
name: "Team 3 - More",
extra: "extra"
}
]
}
]
I've tried using $unwind and $group with no success :(
Palyground example: https://mongoplayground.net/p/yZY8oQ9r-N1
$map to iterate loop of moreTeams array
$filter to iterate loop of teams array and filter matching object by id
$arrayElemAt to get first element object from above filter
$mergeObjects to merge above object from arrayElemAt and current object
db.collection.aggregate([
{
$addFields: {
moreTeams: {
$map: {
input: "$moreTeams",
as: "m",
in: {
$mergeObjects: [
{
$arrayElemAt: [
{
$filter: {
input: "$teams",
cond: { $eq: ["$$m.id", "$$this.id"] }
}
},
0
]
},
"$$m"
]
}
}
}
}
},
Now we have merged moreTeams in teams but we have to merge teams in moreTeams as well
$project, $filter to iterate loop of teams array and match condition if id is not in moreTeams
$concatArrays to concat moreTeams and above filtered array that is not in moreTeams
{
$project: {
users: 1,
moreTeams: {
$concatArrays: [
"$moreTeams",
{
$filter: {
input: "$teams",
cond: { $not: { $in: ["$$this.id", "$moreTeams.id"] } }
}
}
]
}
}
}
])
Playground

How to remove subdocument (by Id) embedded in sub array in MongoDB?

ProductCollection:
{
_id: { ObjectId('x1')},
products: [
{ listPrice: '1.90', product: {id: 'xxx1'} },
{ listPrice: '3.90', product: {id: 'xxx2'} },
{ listPrice: '5.90', product: {id: 'xxx3'} }
]
},
{
_id: { ObjectId('x2')},
products: [
{ listPrice: '2.90', product: {id: 'xxx4'} },
{ listPrice: '4.90', product: {id: 'xxx5'} },
{ listPrice: '5.90', product: {id: 'xxx6'} }
]
},
I want to remove subdocument (xxx3) from collection (x1), and try below:
ProductCollection.update(
{
"_id": mongoose.Types.ObjectId('x1')
},
{
$pull : { 'products.product': {id: 'xxx3' } }
}
It just doesn't seem to work. Can anyone please help me? Thank you
The field for $pull needs to be the array.
This should work:
$pull: { products: { 'product.id': 'xxx3' } }
add this _id: {id: false} while creating mongoose schema
for eg:
partners:[{
name: { type: String, default: '' },
logo: { type: mongoose.Schema.Types.Mixed, default: '' },
_id: { id: false }
}],

Mongo sorted list complexity

For the following sorted list:
{
sorted_list : [{name : <string>,score : <Number>}]
}
What are the complexities of the following commands (in 'O' notations)?
Find:
collection.find( { _id: 1}, { sorted_list: { $slice: [ <skip>, <limit> ] } } )
Insert:
collection.update(
{ _id: 1 },
{
$push: {
sorted_list: {
$each: [ { name: 3, score: 8 }, { name: 4, score: 7 }, { name: 5, score: 6 } ],
$sort: { score: 1 }
}
}
}
)
Remove:
collection.update({"sorted_list.name": name},{ $pull: { "sorted_list.name": <name> } },{ multi: true });
EDIT
Let's assume ther following index exists:
{ "sorted_list.name" : 1}