EDIT:
I am aware of setDefaultsOnInsert but I cant seem to figure out how to pass in that option...
activity.notify.forEach( user_id => {
query = { 'notification._id': activity._id, user_id, type: "replied to an idea you're subscribed to" }
update = { $set: { notification: activity }, $addToSet: { user_details: user_data } }
options = { setDefaultsOnInsert: true }
bulk.find( query ).upsert().update( update, options )
} )
Currently building out a notification system.
This is what my code currently looks like:
let bulk = Notification.collection.initializeUnorderedBulkOp()
activity.notify.forEach( user_id => {
query = { 'notification._id': activity._id, user_id, type: "replied to an idea you're subscribed to" }
update = { $set: { notification: activity }, $addToSet: { user_details: user_data } }
bulk.find( query ).upsert().update( update )
} )
bulk.execute()
.then( r => console.log( 'r', r ) )
.catch( err => console.log( 'bulk exec err ', err.writeErrors[ 0 ] ) )
What the code does is that it looks through an array called notify that contains user_ids.
It then looks to see if a similar notification for that said user_id already exists. If it does, update it with $addToSet to the array user_details.
The issue with this method is that it doesn't have the timestamps. Within my model, I have the line: timestamps: true but since I am upserting some documents, they dont go through the normal instantiation of a document with: new Notification()
Is there a way or method to automatically include those or do I have to create some helper functions to generate the createdAt and updatedAt
This didnt work for me
Related
How can I update many documents on mongoose and return those updated documents so I then I can pass the updated docs to a different service on my code? This is seems like something simple to do but I'm getting confused on my head on how to implement it
In my current code I just update the documents on batch with updateMany, but as the mongo documentation says the writeConcern returned is just the # of docs updated {n: 0 } not the actual documents.
Current Code:
const checkAndUpdateSubscription = () => {
const filter = {
"payments.0.stripeSubscriptionEndDate": { $lte: today },
hasPaid: true,
};
const update = { $set: { hasPaid: false, isSubscriptionNew: 0 } };
const options = { new: true, useFindAndModify: false };
return new Promise((resolve, reject) => {
ModelModel.updateMany(filter, update, options)
.then((response) => {
console.log('response inside checkAndUpdateSubscription', response)
resolve(response);
})
.catch((error) => {
reject(error);
});
});
};
I would like to change it to something similar to my pseudo code below.
What I would like to do:
const checkAndUpdateSubscription = () => {
const filter = {
"payments.0.stripeSubscriptionEndDate": { $lte: today },
hasPaid: true,
};
const update = { $set: { hasPaid: false, isSubscriptionNew: 0 } };
const options = { new: true, useFindAndModify: false };
return new Promise((resolve, reject) => {
// 1. ModelModel.find where stripeSubscriptionEndDate $lte than today ..etc
// 2. Update the document(s)
// 3. Return the updated document(s)
(//4.) .then(updatedModel => sendUpdateModelToOutsideService(updatedModel))
});
};
I don't know if this is necessary in the context of this question but the checkAndUpdateSubscription method is a function that runs every 1min in my db for all my users (# ~thousands)
You can do those as alternative solutions
(maybe there is simpler way, but those will work i think)
Find the ids
find to get those ids
ids = find(your_filter,project_keep_id_only), and then make ids=[_id1, _id2 ...] an array of the ids
update with filter _id $in ids
update({"_id" : {"$in" : ids}},your_update_set_etc)
find to get the updated documents
docs=find({"_id" : {"$in" : ids}})
*if ids are not too many it will be fine i think
Mark the updated with extra field
on update set one extra field with the update_id
after the update, do a find based on that update_id, to get the documents
and if you want your remove that extra field after
If this run parallel, this extra field could be an array, with many update_ids, that you remove them after you get those documents back.
I have a document consisting of a Post. Each Post has an array of Comments, which are an object each. So my document looks like this
Now, I want to be able to update the message property in a given Comment object.
So I'll be using the $set method, but how would I be able to select the specific object. Currently, my unfinished method looks like this
export const editComment = async (req, res) => {
const { id } = req.body;
const post = await Post.findById(req.params.id);
const _id = post.id;
const postComments = post.comments.map((comment) => comment._id);
const commentIndex = postComments.indexOf(id.id);
const message = post.comments[commentIndex].message;
try {
await Post.updateOne(
{ _id },
{
$set: {
// Action to update the comment
},
},
{ new: true }
);
res.status(200).json({ message: post });
} catch (error) {
res.status(400).json({ error: error.message });
}
}
I figured selecting the right index of the comment was a good start, but how would I, in the $set method, select the correct Comment object, and then update the message property?
You have to find the right data in database and update its required property. You can do it by the following method
exports.updateMessage= async (_id,objectId, newMessage) => {
return await TableName.updateOne({_id: _id},{{comments: {$elemMatch: {_id: objectId}$set:{message:newMessage}}});
};
Post.updateOne(_id,
{
$set: {
"comments.$[elem].message": "the value you want to set for message"
}
},
{
arrayFilters: [
{
"elem._id": 1 // _id of comment object you want to edit
}
]
})
Is it possible to delete records from dynamodb based on PrimaryKey and ConditionExpression on SortKey?
Following code sample is throwing an exception for me
DeleteVideoCall = async function (pk, sk) {
let params = {
TableName: this._tableName,
Key: {
pk: { S: pk.toString() },
sk: { S: sk.toString() }
},
ConditionExpression: "begins_with(sk,:sk)",
ExpressionAttributeValues: {
":sk" : { S: sk.toString() + "_" }
}
};
return this._ddb
.deleteItem(params)
.promise()
.then((data) => {
console.log(`Video Call '${pk}/${sk}' deleted`);
return null;
})
.catch((error) => {
console.error(
`Error deleting video room '${pk}/${sk}' (${error})`
);
throw error;
});
};
I want to delete all records that begins_with sk and . For example if sk is 560622 then delete all records where sk begins_with 560622
with the code above I get this error:
Error deleting video room '10900/560622'
(ConditionalCheckFailedException: The conditional request failed)
You can't do that. You need whole key to perform a delete. What you can do:
query items (limit retrieved properties to your PK and SK)
use batch-write to remove multiple items, it also accepts delete requests
I'm using mongoose to handle my DB queries. I'm trying to update a set of records entirely using this method. Mode code looks like this:
// prepare database query
const filter = { type: 'company' };
const update = req.body.payload; // payload contains the array of objects (i.e. updated records)
const options = { new: true, runValidators: true }
// find and update the taxonomy record
await Taxonomy.updateMany(filter, update, options);
But whenever I run this query I'm getting following error in the console:
Error [MongooseError]: Invalid update pipeline operator: "_id"
I suppose there is something wrong in my update payload. The req.body.payload looks like this:
[
{
_id: '5ef3d08c745428001d92f896',
type: 'company',
name: 'Company Size',
__v: 0
},
{
_id: '5ef3cdc5745428001d92f893',
type: 'company',
name: 'Company Industry',
__v: 0
}
]
Can you please tell me what actually is wrong here?
This is not the right usage of updateMany() - it is aimed to update many documents with a single change.
To update many documents use bulkwrite() (docs) :
async function myUpdateMany(Model, objectsArray) {
try {
let ops = []
for (let obj of (objectsArray || [])) {
ops.push({
updateOne: {
filter: { platformId: obj.platformId },
update: obj,
upsert: false, // set "true" if you want to add a new document if it doesn't exist
}
})
}
Model.bulkWrite(ops, { ordered: false });
} catch (err) {
throw Error("myUpdateMany error: " + err)
}
}
Regarding runValidators, according to this, it seems to work by default.
Background: Im developing an app that shows analytics for inventory management.
It gets an office EXCEL file uploaded, and as the file uploads the app convert it to an array of JSONs. Then, it comapers each json object with the objects in the DB, change its quantity according to the XLS file, and add a timestamp to the stamps array which contain the changes in qunatity.
For example:
{"_id":"5c3f531baf4fe3182cf4f1f2",
"sku":123456,
"product_name":"Example",
"product_cost":10,
"product_price":60,
"product_quantity":100,
"Warehouse":4,
"stamps":[]
}
after the XLS upload, lets say we sold 10 units, it should look like that:
{"_id":"5c3f531baf4fe3182cf4f1f2",
"sku":123456,
"product_name":"Example",
"product_cost":10,
"product_price":60,
"product_quantity":90,
"Warehouse":4,
"stamps":[{"1548147562": -10}]
}
Right now i cant find the right commands for mongoDB to do it, Im developing in Node.js and Angular, Would love to read some ideas.
for (let i = 0; i < products.length; i++) {
ProductsDatabase.findOneAndUpdate(
{"_id": products[i]['id']},
//CHANGE QUANTITY AND ADD A STAMP
...
}
You would need two operations here. The first will be to get an array of documents from the db that match the ones in the JSON array. From the list you compare the 'product_quantity' keys and if there is a change, create a new array of objects with the product id and change in quantity.
The second operation will be an update which uses this new array with the change in quantity for each matching product.
Armed with this new array of updated product properties, it would be ideal to use a bulk update for this as looping through the list and sending
each update request to the server can be computationally costly.
Consider using the bulkWrite method which is on the model. This accepts an array of write operations and executes each of them of which a typical update operation
for your use case would have the following structure
{ updateOne :
{
"filter" : <document>,
"update" : <document>,
"upsert" : <boolean>,
"collation": <document>,
"arrayFilters": [ <filterdocument1>, ... ]
}
}
So your operations would follow this pattern:
(async () => {
let bulkOperations = []
const ids = products.map(({ id }) => id)
const matchedProducts = await ProductDatabase.find({
'_id': { '$in': ids }
}).lean().exec()
for(let product in products) {
const [matchedProduct, ...rest] = matchedProducts.filter(p => p._id === product.id)
const { _id, product_quantity } = matchedProduct
const changeInQuantity = product.product_quantity - product_quantity
if (changeInQuantity !== 0) {
const stamps = { [(new Date()).getTime()] : changeInQuantity }
bulkOperations.push({
'updateOne': {
'filter': { _id },
'update': {
'$inc': { 'product_quantity': changeInQuantity },
'$push': { stamps }
}
}
})
}
}
const bulkResult = await ProductDatabase.bulkWrite(bulkOperations)
console.log(bulkResult)
})()
You can use mongoose's findOneAndUpdate to update the existing value of a document.
"use strict";
const ids = products.map(x => x._id);
let operations = products.map(xlProductData => {
return ProductsDatabase.find({
_id: {
$in: ids
}
}).then(products => {
return products.map(productData => {
return ProductsDatabase.findOneAndUpdate({
_id: xlProductData.id // or product._id
}, {
sku: xlProductData.sku,
product_name: xlProductData.product_name,
product_cost: xlProductData.product_cost,
product_price: xlProductData.product_price,
Warehouse: xlProductData.Warehouse,
product_quantity: productData.product_quantity - xlProductData.product_quantity,
$push: {
stamps: {
[new Date().getTime()]: -1 * xlProductData.product_quantity
}
},
updated_at: new Date()
}, {
upsert: false,
returnNewDocument: true
});
});
});
});
Promise.all(operations).then(() => {
console.log('All good');
}).catch(err => {
console.log('err ', err);
});