MongoDB $addToSet replace - arrays

I have a document like the following one:
{
_id: "00112233",
array: [
{
_id: "potatoes",
amount: 5
},
{
_id: "carrots",
amount: 6
}
]
}
and I need to update or push the documents so that if there is one in the array, it gets replaced, if not, it gets pushed.
If i try to do an update like this:
db.collection.update(
{ _id: "00112233" },
{
"$push": { "array": { "$each": [
{
_id: "potatoes",
amount: 6
},
{
_id: "apples",
amount: 2
}
]}}
}
)
In the database I will find two "potatoes" field. But if I try to replace the "push" with a "addToSet" I won't have the "potatoes" field updated.
Is there any way I can do this in only one query?

Unfortunately, you can't achieve the result that you are expecting in single update operation.
The addToSet doesn't work for this scenario because the array has embedded documents. addToSet checks for both the attribute names and values whether it matches. If match is found, it does nothing. If the match is not found, it adds the data to the array attribute.
In the above case for "potatoes", the amount value of potatoes doesn't match. So it inserts a new element into array. So the result has two potatoes with 2 different values.
{
_id: "potatoes",
amount: 6
}
You may need to perform two update operations.
1) Update the value of an array field if it exists using $set
db.collection.update(
{ _id: "00112233", "array._id": "potatoes"},
{
"$set": { "array.$":
{
_id: "potatoes",
amount: 7
}
},
}
);
2) Add the new element to an array field if it doesn't exists using $addToSet
db.collection.update(
{ _id: "00112233"},
{
"$addToSet": { "array":
{
_id: "apples",
amount: 7
}
},
}
);

you need to use $set instead of $push/$addToSet.
https://docs.mongodb.com/manual/reference/operator/update/set/

Had a similar issue and tried notion quest's approach and it did work.My schema was as follows
const MatchInstanceSchema = new Schema({
_id: false,
pseudoKey: { type: String, required: true, unique: true },
service: { type: String, required: true },
region: { type: String, required: true },
team1: {
name: { type: String, required: true },
price: { type: String, required: true }
},
team2: {
name: { type: String, required: true },
price: { type: String, required: true }
},
drawPrice: { type: String, required: true },
url: { type: String, required: true }
})
const MatchSchema = new Schema({
pseudoKey: { type: String, required: true, unique: true },
sport: { type: String, required: true },
league: { type: String, required: true },
date: { type: Date, required: true },
team1: { type: String, required: true },
team2: { type: String, required: true },
matchInstances: [MatchInstanceSchema]
})
I wanted to update the following matchInstances subdocument if there exists a match whose pseudoKey was same as the top level(parent document) pseudoKey without
duplicating the value and create a new entry in the matchInstances subdocument if it did not exist.
{
"_id" : ObjectId("5b47d47e7fd01d29178aa3a5"),
"pseudoKey" : "newcastle-tottenham",
"sport" : "Soccer",
"league" : "Premier League",
"date" : ISODate("2018-08-11T11:30:00.000Z"),
"team1" : "Newcastle",
"team2" : "Tottenham",
"matchInstances" : [
{
"team1" : {
"name" : "Newcastle",
"price" : "74.6"
},
"team2" : {
"name" : "Tottenham",
"price" : "2.06"
},
"pseudoKey" : "newcastle-tottenham",
"service" : "Betin",
"region" : "Kenya",
"drawPrice" : "3.45",
"url" : "https://web.betin.co.ke/Sport/SubEventOdds.aspx?SubEventID=16701401"
}
]
This was my solution
//If it already exists we just want to update with the new values without
//duplicating
Match.findOneAndUpdate(
{ pseudoKey: pseudoKey, "matchInstances.service": service },
{ $set: { "matchInstances.$": matchInstances[0] } }
//If it does not exist we want to add it as a new entry into the matchInstances
//subdocument
Match.findOneAndUpdate(
{ pseudoKey: pseudoKey },
{ $addToSet: { matchInstances: matchInstances } }
)

Related

Subtract value in array based on other collection mongoose

I have the following collections
const TransactionSchema = mongoose.Schema({
schedule: {
type: mongoose.Schema.ObjectId,
required: true,
ref: "Schedule"
},
uniqueCode: {
type: String,
required: true
},
item: {
type: mongoose.Schema.ObjectId,
required: false,
ref: "Item"
},
created: {
type: Date,
default: Date.now
},
status: {
type: String,
required: false
},
})
schedule
const ScheduleSchema = mongoose.Schema({
start: {
type: Date,
required: true,
},
end: {
type: Date,
required: false,
},
questions: {
type: Array,
default: [],
},
items: [{
item: {
type: mongoose.Schema.ObjectId,
require: true,
ref: "Item"
},
stock: {
type: Number,
required: true
}
}],
status: {
type: String,
required: false
},
})
I want to return how many times the item appear in transaction and reduce it with the number of total item I have in array of objects items in schedule collection.
For example I have the following data:
transaction
[
{
"_id":ObjectId('626134d547c28b6ecc6d0c6c'),
"schedule":624edc44ac2edc1cf07821de,
"uniqueCode":"312312312312",
"created":2022-04-21T10:41:25.901+00:00,
"item": 61efd8a812432135c08a748d,
"status": "Active"
},
{
"_id":ObjectId('62615ea3db7d00061b7cc437'),
"schedule":624edc44ac2edc1cf07821de,
"uniqueCode":"1213123123",
"created":2022-04-21T13:39:47.351+00:00,
"item": 624c6b26a41c439987b1eb42,
"status": "Active"
}
]
schedule
[
{
"_id":ObjectId('624edc44ac2edc1cf07821de'),
"start":2000-01-01T00:00:00.000+00:00,
"end":2000-01-01T00:00:00.000+00:00,
"questions":[
12,
32,
122
],
"items":[
{
"item":61efd8a812432135c08a748d,
"stock":120
},
{
"item":624c6b26a41c439987b1eb42,
"stock":1000
}
],
"status":"Active"
},
]
Item
[
{
"_id": ObjectId('61efd8a812432135c08a748d'),
"name": "Samsung S21"
},
{
"_id": ObjectId('624c6b26a41c439987b1eb42'),
"name": "Iphone 10"
}
]
I want to get the following result:
[
{
"item":"Samsung S21",
"total":119
},
{
"item":"Iphone 10",
"total":999
}
]
Note: I want the query to query one schedule data only so the result only shows the items in that schedule. The first row shows 119 from total stock of item 120 - 1 which is how many times the item appeared in transaction (where the status is active). The second row shows 999 because the item has appeared in one transaction.
Thank you. Sorry for my bad English.

Mongoose: Find() return document with the the object that contains the value

I'm using MongoDB, and express.
I have a collection with all the countries, contains the states and cities.
Picture of a part of the collection
Now,
lets say I want to get all the cities that contains "Los" (like "San Antonio", "San Diego"),
when I'm using Find() - its return the all document with all the states and all the cities in the country (like in the picture above),
I want to return the all documents, but return only the objects that contains the value, inside array "cities"
Note: I expect to get different countries with cities that contains a part of the value in their names.
how to use the Find() to return as I want ?
hers is the schema I'm using:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const CitiesSchema = new Schema({
capital:{
type: String,
required: true
},
currency:{
type: String,
required: true
},
emoji: {
type: String,
required: true
},
emojiU: {
type: String,
required: true
},
id: {
type: Number,
required: true
},
iso2: {
type: String,
required: true
},
iso3: {
type: String,
required: true
},
name: {
type: String,
required: true
},
native: {
type: String,
required: true
},
phone_code: {
type: String,
required: true
},
region: {
type: String,
required: true
},
states:
[{
cities:[{
id:{
type: Number,
required: true
},
latitude:{
type: String,
required: true
},
longitude:{
type: String,
required: true
},
name:{
type: String,
required: true
},
}],
id:{
type: Number,
required: true
},
name:{
type: String,
required: true
},
state_code:{
type: String,
required: true
},
}]
,
subregion: {
type: String,
required: true
}
});
const Cities = mongoose.model('Cities',CitiesSchema);
module.exports = Cities;
Edit: my Find() code:
Cities.find({"states.cities.name": city})
.then((data)=>{
return res.json(data);
})
lets say I want to search cities with the name "Los Santos". I want the result to be like this:
Picture of the result I expect to be return
instead, I'm getting the all states and cities in the countries - which I don't want.
UPDATE:
I found a way to return the data as I wish, by using aggregate with $unwind.
Cities.aggregate([{
$unwind: '$states'
},
{
$unwind: '$states.cities'
},
{
$match: {
'states.cities.name': {$regex: city, $options: "i"}
}
}
],
function (error, data) {
return res.json(data);
})
Try using RegEx, I use it while doing searches on MongoDB.
Cities.find({"states.cities.name": new RegExp(city)})
.then((data)=>{
return res.json(data);
})
I found a way to return the data as I wish, by using aggregate with $unwind.
Cities.aggregate([{
$unwind: '$states'
},
{
$unwind: '$states.cities'
},
{
$match: {
'states.cities.name': {$regex: city, $options: "i"}
}
}
],
function (error, data) {
return res.json(data);
})

mongodb schema how to set default value for array of sub documents?

Example I have this schema.
{
field1: {
type: String,
trim: true
},
field2: [{
subField1: {
type: String,
},
subField2: {
type: Number,
default: 0
}
}]
}
How should I set the default value on the schema for the field2 if I just provided the field1 on the document insert?
example If i just insert {field1: "sample string data"}, then the value on the field2 is an empty array.
can you try this?, use type to specify the schema and default for default values
const TesterSchema = new Schema({
field1: {
type: String,
trim: true
},
field2: {
type : [{
subField1: {
type: String
},
subField2: {
type: Number
}
}],
default :[{subField2 : '0'}]
}
}
);
example
Tester.create({field1 : 'testing'}, function(err,doc){
if(err) console.log(err)
console.log(doc)
})
console output
Mongoose: testerzz.insert({ field1: 'testing', _id: ObjectId("5a6ae40a351d4254c6e0fdc2"), field2: [ { subField2: 0, _id: ObjectId("5a6ae40a351d4254c6e0fdc1") } ], __v: 0 })
{ __v: 0,
field1: 'testing',
_id: 5a6ae40a351d4254c6e0fdc2,
field2: [ { subField2: 0, _id: 5a6ae40a351d4254c6e0fdc1 } ] }
mongo CLI
> db.testerzz.find({_id: ObjectId("5a6ae40a351d4254c6e0fdc2")}).pretty()
{
"_id" : ObjectId("5a6ae40a351d4254c6e0fdc2"),
"field1" : "testing",
"field2" : [
{
"subField2" : 0,
"_id" : ObjectId("5a6ae40a351d4254c6e0fdc1")
}
],
"__v" : 0
}
>

Save current User into field within array in Mongoose

Here is a relevant part of my Schema, where I'll make reservations to a "space":
var spaceSchema = new mongoose.Schema({
spaceName: String,
scheduledDates: [{
scheduledDates: String,
author: {
id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
username: String
}
}]
});
Author should be the current user that's logged in. Here is my route to update those fields:
router.put('/:space_id/schedule', function(req, res) {
Space.findByIdAndUpdate(req.params.space_id, {
'$push': { 'scheduledDates': req.body.space, 'author': req.user._id }
}, { "new": true, "upsert": true }, function(err, space) {
if (err) {
console.log(err);
} else {
console.log(req.body.space);
}
});
});
I can't access "author" correctly, because it's inside the array. What can I do to update this array, adding a new date and user to make the reservation?
Thank you
UPDATE
I tried to use "_id" instead of "id" in my property but got the same result. It seems like it's ignoring the "author" field, and only saving "scheduledDates"
So the schema was like this:
scheduledDates: [{
scheduledDates: String,
author: {
_id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
username: String
}
}]
And then in my route, I changed what I was 'pushing':
'$push': { 'scheduledDates': req.body.space, 'author._id': req.user._id }
UPDATED 2
Changed the way I was getting the object to push:
'$push': {
'scheduledDates': {
'scheduledDates': req.body.space,
'author': { _id: req.user._id, username: req.user.username }
}
}
Now I'm getting the following error:
message: 'Cast to string failed for value "{ scheduledDates: \'04/11/2017\' }" at path "scheduledDates"',
name: 'CastError',
stringValue: '"{ scheduledDates: \'04/11/2017\' }"',
kind: 'string',
value: [Object],
path: 'scheduledDates',
reason: undefined } } }

Mongoose: Update by pushing an object in an array

This is the model I am working with:
name: {
type: String
},
payment: {
id: {
type: String,
required: true
},
cards: [
{
id: {
type: String
},
is_default: {
type: Boolean,
"default": false
}
}
]
}
I want to add a card to the cards array, for example:
card =
id: "some_token"
is_default: true
I am using the update method to push the card to the array, but it won't add the card to the document. Instead, it creates a new document with only those fields:
{
id: "some_token",
is_default: true,
_id: someId
}
Any idea how I can update the actual document I am targeting instead of creating a new document?
Here's my code (using CoffeeScript):
update_where =
payment:
id: "some_id"
update_push =
$push:
'payment.cards':
id: card_token
is_default: false
Customer.update update_where, update_push, {upsert: true}, (err, results) ->
# Do something with the results
Oh… I just noticed my mistake. The problem was in the where statement.
I was doing:
payment:
id: "some_id"
But the right thing to write is the following:
'payment.id': 'some_id'
And it now works!

Resources