How to update multiple objects in the array in MongoDB? Assume that I am retrieving from the client an array of titles that need to be changed.
Example:
Data from client:
[0,3]
Document in the MongoDB:
[
{
_id: 0,
dishes: [
{ title: 0, in_stock: true },
{ title: 1, in_stock: false },
{ title: 2, in_stock: true },
{ title: 3, in_stock: false },
],
},
];
The result should be:
[
{
_id: 0,
dishes: [
{ title: 0, in_stock: false },
{ title: 1, in_stock: false },
{ title: 2, in_stock: true },
{ title: 3, in_stock: true },
],
},
];
You can simply put a aggregation pipeline in your update clause.
db.collection.update({},
[
{
"$addFields": {
"dishes": {
"$map": {
"input": "$dishes",
"as": "d",
"in": {
"$cond": {
"if": {
"$in": [
"$$d.title",
[
0,
3
]
]
},
"then": {
title: "$$d.title",
in_stock: {
"$not": [
"$$d.in_stock"
]
}
},
"else": "$$d"
}
}
}
}
}
}
])
Here is the Mongo playground for your reference.
Related
I have a collection in MongoDB:
[
{
"uid": "a1"
},
{
"uid": "a2"
}
]
and a dictionary in my JS code
let dict = { "a1": "ref1", "a1": "ref2" };
I want to do an aggregate that will somehow join the two.
let k = ;
this.model.aggregate([
{
$match: {
uid: { $in: Object.keys(dict) }
}
},
{
$project: {
ref: // here is where I want to add the equivalent reference
}
}
])
The expected output would be something like this:
[{uid: "a1", ref: "ref1"}, {uid: "a2", ref: "ref2}]
Is there a way to get the reference from the dict into the $project?
I don't know if there is an easier way to do it, but here's how I achieved it:
db.collection.aggregate([
{
$match: {
uid: {
$in: [
"a1",
"a2"
]
}
}
},
{
"$project": {
"array": {
"$objectToArray": {
"a1": "ref1",
"a2": "ref2"
}
},
"uid": 1,
}
},
{
"$project": {
"ref": {
"$filter": {
"input": "$array",
"as": "elem",
"cond": {
"$eq": [
"$$elem.k",
"$uid"
]
}
}
},
uid: 1,
},
},
{
"$project": {
ref: {
"$arrayElemAt": [
"$ref.v",
0
]
},
uid: 1,
}
}
])
See it working here. I hope you get the idea and convert it into the nodejs equivalent version.
let me illustrate with an example,
suppose my collection is like this,
products = [
{
id: 1,
name: "a",
offer: true,
expiryDate: "23-03-2022"
},
{
id: 2,
name: "b",
offer: false,//no offer
},
{
id: 3,
name: "c",
offer: true,
expiryDate: "01-01-2021"//already expired
},
{
id: 4,
name: "d",
offer: true,
expiryDate: "01-06-2022"
},
]
I want here, the offered items comes first in descending order,
it should not expire and then the remaining item should come in
descending order so result would be like this
[
{
id: 4,
name: "d",
offer: true,
expiryDate: "01-06-2022"
},
{
id: 1,
name: "a",
offer: true,
expiryDate: "23-03-2022"
},
{
id: 3,
name: "c",
offer: true,
expiryDate: "01-01-2021"
},
{
id: 2,
name: "b",
offer: false,
},
]
db.collection.aggregate([
{
"$match": {}
},
{
"$set": {
"offer": {
"$cond": {
"if": {
"$lt": [
{
"$toDate": "$expiryDate"
},
"$$NOW"
]
},
"then": false,
"else": true
}
},
"expiryDate": {
"$toDate": "$expiryDate"
}
}
},
{
"$sort": {
"expiryDate": -1,
"offer": -1
}
}
])
mongoplayground
I have two collections Course and File.
This is my data from Course collection
[{
_id: 1,
name: "Course1",
price: "100"
},
{
_id: 2,
name: "Course2",
price: "200"
}]
This is my data from files collection
[{
_id: 1,
name: "File1",
isFree: true,
courseId: 1
}
{
_id: 2,
name: "File2",
isFree: false,
courseId: 1
}
{
_id: 3,
name: "File3",
isFree: false,
courseId: 1
}]
I want to retrieve Course 1 with Course name,Count of files which are free,Count of files which are not free.
My attempt:
Course.aggregate([
{$lookup:{
from:"files",
localField: "_id",
foreignField: "courseId",
as: "files"
}},
])
This is my response If I execute my above query:
[
{
"_id": "6161bdcc0aada09d81598840",
"name": "Course1",
"files": [
{
"_id": "6166a0814ad3f56b71232b9c",
"name": "File1.jpeg",
"isFree":true
},
{
"_id": "6166a0814ad3f56b71232b9d",
"name": "File2.jpeg",
"isFree":false
},
{
"_id": "6166a0814ad3f56b71232b9v",
"name": "File3.jpeg",
"isFree":false
}
]
}
]
I want my output as:
[
{
"_id": "6161bdcc0aada09d81598840",
"name": "Course1",
"files": {"free": 1,"paid": 2}
}
]
You can try lookup with pipeline,
$lookup with pipeline, pass courseId in let
$match courseId condition
$group by isFeee and get total count
$project to show required fields in k(key) and v(value) format
$cond to check if isFree true then return "free" otherwise return "paid"
$addFields to update files array
$arrayToObject convert files key-value array to an object
$ifNull to check if property is not exists then set 0
await Course.aggregate([
{
$lookup: {
from: "files",
let: { courseId: "$_id" },
pipeline: [
{ $match: { $expr: { $eq: ["$$courseId", "$courseId"] } } },
{
$group: {
_id: "$isFree",
count: { $sum: 1 }
}
},
{
$project: {
_id: 0,
k: { $cond: ["$_id", "free", "paid"] },
v: "$count"
}
}
],
as: "files"
}
},
{
$addFields: {
files: { $arrayToObject: "$files" }
}
},
{
$addFields: {
"files.free": { $ifNull: ["$files.free", 0] },
"files.paid": { $ifNull: ["$files.paid", 0] }
}
}
])
Playground
Try following:
db.course.aggregate([
{
$lookup: {
from: "files",
localField: "_id",
foreignField: "courseId",
as: "files",
},
},
{
$unwind: {
path: "$files",
preserveNullAndEmptyArrays: true,
},
},
{
$group: {
_id: "$_id",
name: {
$first: "$name",
},
price: {
$first: "$price",
},
free: {
$sum: {
$cond: [
{
$eq: ["$files.isFree", true],
},
1,
0,
],
},
},
paid: {
$sum: {
$cond: [
{
$eq: ["$files.isFree", false],
},
1,
0,
],
},
},
},
},
{
$addFields: {
"files.free": "$free",
"files.paid": "$paid",
},
},
{
$project: {
_id: 1,
name: 1,
files: 1,
price: 1,
},
},
]);
I cannot figure to add a new field with multiple conditions in MongoDB. Here is how DB looks like,
const db = [
{
_id: 1,
isActived: false, // Drafted
isScheduled: false,
isExpired: false
},
{
_id: 2,
isActived: true,
isScheduled: true, // Scheduled
isExpired: false
},
{
_id: 3,
isActived: true, // Expired
isScheduled: false,
isExpired: true
},
{
_id: 4,
isActived: true, // Actived
isScheduled: false,
isExpired: false
},
]
Conditions are:
if (!isActived) status = "Draft"
if (isActived && isScheduled) status = "Scheduled"
if (isActived && isExpired) status = "Expired"
if (isActived && !isScheduled && !isExpired) status = "Actived"
I can figure out only one condition and cannot solve it any further. Here is what I did so far
{
$addFields: {
status: {
$cond: [
{
$and: [
{ $eq: ["$isActived", true] },
{ $eq: ["$isScheduled", true] },
],
},
"Actived",
"Scheduled",
],
$cond: [
{
$and: [
{ $eq: ["$isActived", true] },
{ $eq: ["$isExpired", true] },
],
},
"Actived",
"Expired",
],
},
},
}
The result is not what I expected.
Any suggestion. Thank You.
You can concatenate if/else in this way:
Pseudocode:
if !isActived:
return "Draft"
else
if !isScheduled && !isExpired:
return "Actived"
else
if isScheduled:
return "Scheduled"
else
return "Expired" //<-- Since one of "isScheduled" and "isExpired" should be true
Take care on last line where may you can have isScheduled and isExpired to true, so you logic can change.
db.collection.aggregate([
{
"$set": {
"status": {
"$cond": {
"if": {
"$eq": [
"$isActived",
false
]
},
"then": "Draft",
"else": {
"$cond": {
"if": {
"$and": [
{
"$eq": [
"$isExpired",
false
]
},
{
"$eq": [
"$isScheduled",
false
]
}
]
},
"then": "Actived",
"else": {
"$cond": {
"if": {
"$eq": [
"$isScheduled",
true
]
},
"then": "Scheduled",
"else": "Expired"
}
}
}
}
}
}
}
}
])
Example here
Simple nested conditions, working solution
db.collection.aggregate([
{
$addFields: {
status: {
$cond: [
{
$and: [
{ $eq: [ "$isActived", true ] },
{ $eq: [ "$isScheduled", false ] },
{ $eq: [ "$isExpired", false ] },
],
},
"Actived",
{
$cond: [
{
$and: [
{ $eq: [ "$isActived", true ] },
{ $eq: [ "$isExpired", true ] },
],
},
"Expired",
{
$cond: [
{
$and: [
{ $eq: [ "$isActived", true ] },
{ $eq: [ "$isScheduled", true ] },
],
},
"Scheduled",
{
$cond: [
{
$and: [
{ $eq: [ "$isActived", false ] },
],
},
"Draft",
null,
],
},
],
},
],
},
],
},
},
},
])
I need help with the aggregate framework.
I have a model (currencies field can contain more than one object):
const schema = new mongoose.Schema({
country: { type: String },
code: { type: String },
region: [{
name: { type: String },
path: { type: Array },
city: [{
name: { type: String },
path: { type: Array },
latitude: { type: String },
longitude: { type: String },
}],
}],
currencies: [{
code: { type: String },
name: { type: String },
symbol: { type: String },
}],
})
And I need to receive all currencies without duplicates.
Received data can view like this:
[
{ code: 'string', name: 'sting', symbol: 'string' },
{ code: 'string', name: 'sting', symbol: 'string' },
...
]
// or like this:
[
currencies: [
{ code: 'string', name: 'sting', symbol: 'string' },
{ code: 'string', name: 'sting', symbol: 'string' },
...
]
]
I try to create a query
Geo.aggregate([
{
$group: {
_id: null,
currencies: { $addToSet: '$currencies' },
},
},
])
but receive this data with duplicates and it has many nested arrays:
[
{
"_id": null,
"currencies": [
[
{
"_id": "5cd9486248989616a411fac5",
"code": "JPY",
"name": "Japanese yen",
"symbol": "¥"
}
],
[
{
"_id": "5cd9491a48989616a411fb47",
"code": "TRY",
"name": "Turkish lira",
"symbol": null
}
],
I try this query:
Geo.aggregate([
{
$addFields: {
code: '$currencies.code',
name: '$currencies.name',
symbol: '$currencies.symbol',
},
},
])
But I receive error "TypeError: item is not iterable".
I need little help )
Db data views like this:
{
"_id": {
"$oid": "5c3334a8871695568817eadf"
},
"country": "Singapore",
"code": "sg",
"region": [
{
"path": [
"Singapore"
],
"_id": {
"$oid": "5c3366c63d92ac6e531e05c0"
},
"city": [],
"name": "Central Singapore Community Development Council"
},
....
],
"__v": 0,
"currencies": [
{
"_id": {
"$oid": "5cd948ec48989616a411fb28"
},
"code": "BND",
"name": "Brunei dollar",
"symbol": "$"
},
{
"_id": {
"$oid": "5cd948ec48989616a411fb27"
},
"code": "SGD",
"name": "Singapore dollar",
"symbol": "$"
}
]
}
In aggregate pipeline first you need to unwind the currencies array and then group them by condition to get desired result.
Geo.aggregate([
{
$unwind: '$currencies'
},
{
$group: {
_id: null,
currencies: { $addToSet: '$currencies' },
},
},
])
For more information you can look into documentation here
db.temp.aggregate([
{$project : {currencies : 1}},
{$unwind: "$currencies"},
{
$addFields: {
currencyHash: {
$concat : ['$currencies.code', "--", "$currencies.name", "--", "$currencies.symbol"]
}
}
},
{
$group: {
_id: "$currencyHash",
currency : {
$first : "$currencies"
}
}
},
{
$project: {
code : "$currency.code",
name : "$currency.name",
symbol : "$currency.symbol"
}
},
{
$project: {
_id : 0,
currency : 0
}
}
]).pretty()