Currently using Strapi v3.5.3ce. I have a post model with has relation with category model.
A post already exist, but I want to update the post with multiple categories, so I thought of using $addToSet / $set in order to eliminate duplicates added.
But it doesn't seem to push.
ways I have tried are
// with $set
const up = {
$set: { categories: [ '6073ac8f68f6971f3edfc898', '6073ac8f68f888f3edfc111' ] },
...update
}
// with $addToSet
const up = {
$addToSet: { categories: [ '6073ac8f68f6971f3edfc898', '6073ac8f68f888f3edfc111' ] },
...update
}
// with $addToSet and $each
const up = {
$addToSet: { categories: { $each: [ '6073ac8f68f6971f3edfc898', '6073ac8f68f888f3edfc111' ] } },
...update
}
const post = await strapi.query('post').model
.findOneAndUpdate({_id: 'blahblahblah'}, up, { new: true })
.populate({
path: 'categories',
select: 'name'
});
the above uses findOneAndUpdate but I have also tried using update / updateOne
None of the above worked, anyone has any idea what I have gone wrong?
Thanks in advance for any suggestions.
Related
In my application, I am attempting to update a object nested in an array as a below. When testing in postman, there is a delay causing me to have to make two requests in order to see the updated value.
if (taskStatus) {
const taskStatusNew = await Board.findOneAndUpdate(
{
"columns.tasks._id": req.params.id,
},
{
$set: {
"columns.$[].tasks.$[t]": req.body,
},
},
{
arrayFilters: [
{
"t._id": req.params.id,
},
],
}
);
res.status(200).json(taskStatusNew);
}
By default, findOneAndUpdate() returns the document as it was before the update was applied. So you have to set the new option to true if you are using mongoose.
const taskStatusNew = await Board.findOneAndUpdate(
{
"columns.tasks._id": req.params.id,
},
{
$set: {
"columns.$[].tasks.$[t]": req.body,
},
},
{
arrayFilters: [
{
"t._id": req.params.id,
},
],
new: true
}
);
Documentation article for reference: https://mongoosejs.com/docs/tutorials/findoneandupdate.html
If your question is like to return the updated value then use this,- {returnDocument: 'after'}, you just need to add this in other parameter, then it will give you updated value.
How to include aggregation in if condition ,do I need to use project or condition method in if condition above as well catalogue populate. I need to get the data from the mongo dB in the same order as video Ids array but it's coming in a different order so I decided to use aggregation to get the video in a proper order as in the video Ids array. Please help me to resolve this issue.
let filter = {$match:{
customerId: customerId,
_id: {
$in: _.map(videoIds, id => mongoose.Types.ObjectId(id)) || []
},
_isDeleted: false,
isActive: true
},
$lookup:{
from:'catalogues',
localField:'_isDeleted',
foreignField:'_id',
as:false
}
}
if (!req.isLocationWhitelisted) {
if (req._countryCode) {
$or=
filter.$match['languages.country'] = {
$in: req._countryCode
}
filter.$lookup['languages.country'] = {
$in: req._countryCode
}
,
filter.$match['languages.country.141'] = { $exists: true }
filter.$lookup['languages.country.141'] = { $exists: true }
}
}
let videoList = await Video.aggregate(filter);
I have got a data structure:
{
field: 1,
field: 3,
field: [
{ _id: xxx , subfield: 1 },
{ _id: xxx , subfield: 1 },
]
}
I need to update a certain element in the array.
So far I can only do that by pulling out old object and pushing in a new one, but it changes the file order.
My implementation:
const product = await ProductModel.findOne({ _id: productID });
const price = product.prices.find( (price: any) => price._id == id );
if(!price) {
throw {
type: 'ProductPriceError',
code: 404,
message: `Coundn't find price with provided ID: ${id}`,
success: false,
}
}
product.prices.pull({ _id: id })
product.prices.push(Object.assign(price, payload))
await product.save()
and I wonder if there is any atomic way to implement that. Because this approach doesn't seem to be secured.
Yes, you can update a particular object in the array if you can find it.
Have a look at the positional '$' operator here.
Your current implementation using mongoose will then be somewhat like this:
await ProductModel.updateOne(
{ _id: productID, 'prices._id': id },//Finding Product with the particular price
{ $set: { 'prices.$.subField': subFieldValue } },
);
Notice the '$' symbol in prices.$.subField. MongoDB is smart enough to only update the element at the index which was found by the query.
I have a Model, Users, and each user has an array of objects called habits.
{
_id: 606f1d67aa1d5734c494bf0a,
name: 'Courtney',
email: 'c#gmail.com',
password: '$2b$10$WQ22pIiwD8yDvRhdQ0olBe6JnnFqV2WOsC0cD/FkV4g7LPtUOpx1C',
__v: 35,
habits: [
{
_id: 6081d32580bfac579446eb81,
name: 'first',
type: 'good',
days: 0,
checked: false
},
{
_id: 6081d32f80bfac579446eb82,
name: 'seconds',
type: 'bad',
days: 0,
checked: false
},
]
}
From my client side, I send over a list of ids of the habits I want to delete out of the array, that looks like this..
[
'6081d32580bfac579446eb81',
'6081d32f80bfac579446eb82',
]
I am trying to find a way to delete the IDs in the habits array, deleting only the habits whose IDs are sent in the array above.
Here is what I have tried....
router.post('/delete', validate, async (req, res) =>{
const user = await User.findById(req.user._id)
const idList = req.body.ids.checkedItems
const updatedList = user['habits'].filter(habit=> {
return !idList.includes(`${habit._id}`)
})
user['habits'] = updatedList;
try {
await user.save()
res.send(updatedList)
} catch (err) {
res.status(400).send(err)
}
})
-idList is the array of ids as strings.
-user['habits'] accesses the list from the document, user.
-my filter method only returns the habits that are NOT included in the idList array. Because the Ids in the array are the ones to be deleted.
This solution is obviously just vanilla javascript, what I am looking for is if anyone knows how to achieve this using mongoose.js syntax.
I think you could do this by using deleteMany or deleteOne on the Model, but I am not sure how to achieve this.
Thank you for taking the time to help or give suggestions.
The solution that worked for me in the end is to use the Model method 'findByIdAndUpdate'.
const { itemsToDelete } = req.body
User.findByIdAndUpdate(req.user._id,
{ $pull: { habits: { _id: itemsToDelete } } },
{ new: true , useFindAndModify: false},
function (err, data) {
if (err) {
res.send(err)
} else {
res.send(data.habits)
}
}
)
You can use a combination of $pull and $in for this.
User.update({_id: userId},
{
$pull: {
habits: {
_id: {
$in: [
ObjectId("6081d32580bfac579446eb81"),
ObjectId("6081d32f80bfac579446eb82")
]
}
}
}
})
I have a query where I first want to match find the list of matched users and then filter the matches out from the array of external users that was passed in so that I am left with users Id's that have not been matched yet.
Here is a the match Schema:
const mongoose = require('mongoose'); // only match two users at a time.
const Schema = mongoose.Schema;
const MatchSchema = new Schema({
participants: [{
type: String, ref: 'user'
}],
blocked: {
type: Boolean,
default: false
}
});
Here is the query with explanations:
db.getCollection('match').aggregate([
{
'$match': {
'$and': [
{ participants: "599f14855e9fcf95d0fe11a7" }, // the current user.
{ participants: {'$in': [ "598461fcda5afa9e0d2a8a64","598461fcda5afa9e0d111111", "599f14855e9fcf95d0fe5555"] } } // array of external users that I want to check if the current user is matched with.
]
}
},
{
'$project': {
'participants': 1
}
},
This returns the following result:
{
"_id" : ObjectId("59c0d76e66dd407f5efe7112"),
"participants" : [
"599f14855e9fcf95d0fe11a7",
"599f14855e9fcf95d0fe5555"
]
},
{
"_id" : ObjectId("59c0d76e66dd407f5efe75ac"),
"participants" : [
"598461fcda5afa9e0d2a8a64",
"599f14855e9fcf95d0fe11a7"
]
}
what I want to do next it merge the participants array form both results into one array.
Then I want to take away the those matching items from the array of external users so that I am left with user id's that have not been matched yet.
Any help would be much appreciated!
If you don't want those results in the array, then $filter them out.
Either by building the conditions with $or ( aggregation logical version ):
var currentUser = "599f14855e9fcf95d0fe11a7",
matchingUsers = [
"598461fcda5afa9e0d2a8a64",
"598461fcda5afa9e0d111111",
"599f14855e9fcf95d0fe5555"
],
combined = [currentUser, ...matchingUsers];
db.getCollection('match').aggregate([
{ '$match': {
'participants': { '$eq': currentUser, '$in': matchingUsers }
}},
{ '$project': {
'participants': {
'$filter': {
'input': '$participants',
'as': 'p',
'cond': {
'$not': {
'$or': combined.map(c =>({ '$eq': [ c, '$$p' ] }))
}
}
}
}
}}
])
Or use $in ( again the aggregation version ) if you have MongoDB 3.4 which supports it:
db.getCollection('match').aggregate([
{ '$match': {
'participants': { '$eq': currentUser, '$in': matchingUsers }
}},
{ '$project': {
'participants': {
'$filter': {
'input': '$participants',
'as': 'p',
'cond': {
'$not': {
'$in': ['$p', combined ]
}
}
}
}
}}
])
It really does not matter. It's just the difference of using JavaScript to build the expression before the pipeline is sent or letting a supported pipeline operator do the array comparison where it is actually supported.
Note you can also write the $match a bit more efficiently by using an "implicit" form of $and, as is shown.
Also note you have a problem in your schema definition ( but not related to this particular query ). You cannot use a "ref" to another collection as String in one collection where it is going to be ObjectId ( the default for _id, and presumed of the hex values obtained ) in the other. This mismatch means .populate() or $lookup functions cannot work. So you really should correct the types.
Unrelated to this. But something you need to fix as a priority.