How to delete an element of an array using mongoose - node.js

CustomItemCollection.find({name: urlPath}, function(err, data){
if(err){
console.log(err);
}
else{
let returnedData = data[0].item;
for(var i = 0; i < returnedData.length; i++){
if(returnedData[i]._id === checkboxButton){
//delete this object nested inside the array
}
}
}
})
Im trying to delete an element inside an array. I tried following some other posts on here but they didn't work. Can someone tell me how to do this? Here's the schema if that'll help:
const CustomItemSchema = {
name: {
type: String,
required: 1,
unique: 1
},
item: [{
myItems: String
}]
}
i wanna delete one of the "myItems" created using its id. I have gotten its id but i don't know how to delete it.
Thanks for your help!

If I've understood correctly you only need to use $pull like this:
CustomItemCollection.updateOne(
{
"name": urlPath
},
{
"$pull": {
"item": {
"_id": checkboxButton
}
}
})
Example here
Note that I've used updateOne but you can use other query as findOneAndUpdate or updateMany if you need.

Related

aggregate with dynamic field path- mongo DB and nodeJS

I have a collection- 'products' that contains the following documents:
{
productName: "computer",
updateAt: "2022-07-12T12:44:47.485Z",
createAt: ""2022-06-12T10:34:03.485Z",
changeAt: ""2022-09-12T10:39:40.485Z"
}
I want to create an aggregation that convert the field "updateAt" from string to date.
for this, I created this aggregation:
db.products.aggregate([{
$set: {
updateAt: {
$dateFromString: {
dateString: '$updateAt'
}
}
},
},
{
$out: 'products'
}]
)
It works fine for this need, but as you can see I specified the field path "updateAt" in a hard coded way.I want to use the above aggregation in a dynamic way-
considering I have an array of fields that I want to change:
const fields = ['updateAt', 'createAt', 'changeAt']
I want to loop over the fields array and use each field as a fieldPath so I can transfer the field name to the aggregation, something like that-
fields.forEech(field -> {
db.products.aggregate([{
$set: {
`${field}`: {
$dateFromString: {
dateString: `$${field}`
}
}
},
},
{
$out: 'products'
}]
)
}
As you can understand it's not working for me....
How can I achieve my goal?
You have some errors in your nodejs function, also, aggregate method returns a Promise, so you will need to wait for it, to resolve before moving further.
Try this:
const fields = ['updateAt', 'createAt', 'changeAt']
for(let i=0; i < fields.length; i++) {
let field = fields[i];
await db.products.aggregate([{
"$set": {
[field]: {
"$dateFromString": {
"dateString": `$${field}`
}
}
},
},
{
"$out": 'products'
}]
)
}
Also, make the function containing this piece of code async.

For each MongoDB document, add a new variable that increments

I have a collection with the following documents:
[{_id: abc, name: "foo"}, {_id: def, name: "bar"}, {_id: ghi, name: "baz"}]
I want to change every document in that collection so it has a new field, which is unique, and that has a letter and a number, the number increases with each document. So I want to have this:
[{_id: abc, name: "foo", customId: "m1"}, {_id: def, name: "bar", customId: "m2"}, {_id: ghi, name: "baz", customId: "m3"}]
I tried using the most voted answer in this question, but it only has a number which is kind of the index in the array, but I want a letter and the number next to it.
I am using NodeJS and Express with the mongoose package. I don't mind if the answer is either using javascript code or a mongo cli command.
Any help is very appreciated, thanks in advance.
I'm assuming you need to update the existing table and also need to create the counter field for the upcoming data's,
function update() { //updating existing table
user.aggregate(
[{
$match: {
"counter": { $exists: false }
}
}],
function (err, res) {
if (err) {
console.log(err)
}
var i = 0;
var newId;
res.forEach((element, index) => {
i = i + 1;
newId = "count" + i
user.update(
{ id: element.id },
{ $set: { "Counter": newId } }
);
});
})
}
function create(userparam) {//while creating new table
autonumber.find({}, function (err, res) {
let counter_value = "Count" + res[0].incrementer
//assuming incrementer to be feild in autonumber table
const user = new User(userparam);
user.Counter = counter_value;
return await user.save()
})
}
I'm beginner,so if this code is inefficient or wrong .... sorry in advance.

Update nested array objects in MongoDB

I have to deal with objects of the following type in a NodeJS app (using mongodb driver):
data_test = {
"id": "105-20090412",
"date": new Date('2020-09-04T14:00:00.000Z'),
"station": {
"name": "AQ105",
"loc": {
"type": "Point",
"coordinates": [14.324498, 40.821930]
},
"properties": {}
},
"samples": [{
"t": new Date('2020-09-04T14:14:00.000Z'),
"data": {
//"temp_celsius": 31.81,
//"humRelPercent": 39,
"press_mBar": 1021.12,
"PM10": 200
}
}]
}
I receive every 2 minutes data as above.
I want to:
If the data received has an id not yet present on MongoDB do an insert
If the data received has a sample object with a Date (t property) yet present then add properties to this one (for example readings of different sensors)
If the data received has a sample object with a Date (t property) not yet present in samples array, then add this new one
I would like to do what described above with the minor count possible of round-trips to the MongoDB server.
I hope to have been clear enough.
Any suggestion?
Thanks in advance.
Here's my suggestion, this is not the correct answer. You will need to fiddle with the query portion. The query below should work for 1 & 3, for 2 you will have to play around.
db.collection.updateOne(
{ "id" : "105-20090412", "samples.t": <Date> },
{ $push: { "samples" : <sample> } },
{ $setOnInsert: { station: <station> } },
{ upsert: true }
);
References:
https://docs.mongodb.com/manual/reference/method/db.collection.updateOne/
https://docs.mongodb.com/manual/reference/operator/update/setOnInsert/#up._S_setOnInsert
https://docs.mongodb.com/manual/reference/operator/update/push/
I finally came to the following solution, perhaps not the most efficient one:
try {
const db = client.db(dbName);
const collection = db.collection(collectionName);
// retrive id, station, date and samplesToAdd as separate objects
let {
id,
...dataToInsert
} = data
//id = new ObjectID(id)
const queryBy_id = {
_id: id
}
// first check if doc exists
let res_query = await collection.findOne(queryBy_id)
// if doc does not exists then insert a new one
if (!res_query) {
res_insert = await collection.insertOne({
_id: id,
...dataToInsert
})
return res_insert;
} else {
// retrive samples from initial query
let current_samples = res_query.samples
// check if sample in dataToInsert yet exists
// use getTime to correctly compare dates
let idx = current_samples.findIndex(x => x.t.getTime() == dataToInsert.samples[0].t.getTime())
if (idx >= 0) {
// find index of sample to update
let current_t = current_samples[idx].t
// merge data yet stored with new one
current_samples.data = {
...current_samples[idx].data,
...dataToInsert.samples[0].data
}
let resUpdateSample = await collection.updateOne({
_id: id,
'samples.t': current_t
}, {
$set: {
'samples.$.data': current_samples.data
}
})
return resUpdateSample
} else {
// add data to samples array
let resAddToSamples = await collection.updateOne({
_id: id
}, {
$push: {
samples: dataToInsert.samples[0]
}
})
return resAddToSamples
}
}
} catch (err) {
logger.error(err);
}
How can I improve it?
Thanks.

Updating an Array at a specific Index using Mongoose

I have a collection of documents in mongoDB. One of the schema's properties has an array, that contains objects. Each object within this array contains a property that has another array as it's value.
const userSchema = new mongoose.Schema({
username: "",
password: "",
firstName: "",
clients: [],
});
The "clients" property array looks like this:
[
{
clientName: 'Chabad of Closter',
activeInvoice: [],
pastInvoices: []
},
{
clientName: 'Chabad UC',
activeInvoice: [],
pastInvoices: []
},
{
clientName: 'Chabad Mobile',
activeInvoice: [],
pastInvoices: []
}
]
My goal is to push an Object into any of the "activeInvoice" arrays by using the index of its object. I used this code and it works when I specify the index manually:
User.findByIdAndUpdate(id, {"$push": {"clients.2.activeInvoice": newCharge}}, {new : true},
function(err, updatedCharge){
if (err) {
console.log(err)
} else {
console.log(updatedCharge);
}
});
In the example above, I used the "2" index. I need to be able to change that index dynamically. I tried this:
// code to find the index I want and save it to indexer
const indexer = clientsArr.findIndex(i => i.clientName == newCharge.clientName);
// form it into a string
const mongooseLink = "clients." + indexer + ".activeInvoice";
//place it into the mongoose request
User.findByIdAndUpdate(id, {"$push": {mongooseLink: newCharge}}, {new : true},
function(err, updatedCharge){
if (err) {
console.log(err)
} else {
console.log(updatedCharge);
}
});
but this doesn't work. I double checked to make sure the indexer is working. No error, just the document doesn't get updated.

How to update a value with aggregate in mongodb and node

I post this question in relation to my use case.
It is true that there is a lot of response in the same subject but I do not find an answer .
that is why I ask you for help Thanks.
I would like to be able to update the lineItemStatus inside lineItems array.
Here is my model :
const orderSchema = new Schema(
lineItems: [{
lineItemStatus: {
type: String,
default: 'en waiting for validation',
lowercase: true
}
}]
)
The result look like this
{
"_id": "5c659cd0be79c124126d5ec2",
"lineItems": [{
"lineItemStatus": "waiting for validation", //the status to update
"_id": "1"
},
{
"lineItemStatus": "delivered",
"_id": "2"
}
]
}
First I'm able to get a single item of lineItems.
this is the code
async updateStatus(req, res) {
let givenLineItemId = req.body.lineItemId
let givenlineItemStatus = req.body.status // the status to update
try {
const ObjectId = mongoose.Types.ObjectId
const aggregationStages = [
{
$unwind: '$lineItems'
},
{
$match: {
'lineItems._id': ObjectId(givenLineItemId)
}
}
]
await Order
.aggregate(aggregationStages)
.exec(function(err, orders) {
if (err) res.status(400).send(err)
res.status(200).send(orders)
})
} catch (err) {
return res.status(500).send(err)
}
}
But now i'm not able to to update the lineItemStatus i see some way to use set or push but it doesn't work.
Thanks a lot for the help.
The aggregation stage itself does not support updates. You have two options:
1) Collect the aggregate results into a variable and do a bulk update. See link.
2) Call forEach on the aggregate. You can see samples provided in this answer.

Resources