I am trying to update multiple docs in mongo.
I have an array, and I want to update multiple mongo docs with a specific object from that array.
let items = [{ order: 0 }, { order: 1 }, { order: 2 }]
I have tried this for updating mongo:
items.map((item) => {
db.items.findOneAndUpdate(
{ _id: item._id },
{ $set: { order: item.order } }
)
})
If I check items, it has actually updated the array:
console.log(items)
But when I look in my database, MongoDB has not updated.
Any suggestions what I'm doing wrong?
Thanks!
map is a synchronous function and you are using it to for an asynchronous update with findOneAndUpdate, akin to mixing water and oil.
You either need to use async/await or Promise.all with the map function. For example, with the above, you can create an array of promises with the map function and resolve with Promise.all
const updatePromises = items.map(({ _id, order }) => (
Item.findOneAndUpdate({ _id }, {'$set': { order } })
))
Promise.all(updatePromises).then(console.log).catch(console.error)
Using async/await
(async () => {
try {
for(let i=0; i<items.length; i++) {
await Item.findOneAndUpdate(
{ _id: item._id },
{ $set: { order: item.order } }
)
}
} catch (err) {
console.error(err)
}
})()
Related
I'm trying to find the right way to create and consequently update inside a map function.
These are the steps I need:
Map function "reads" the array of elements ids
Create new record on "leads_status" table
Using the new record id (from "leads_status") "leads" table is updated using "leads_status.id" as foreign key related to "leads.id_ls"
This is the code I tried.
const [create, { isLoading: isLoadingCreate, error: errorCreate }] = useCreate();
const [record, setRecord] = React.useState(null);
leadsIDS.map((value, index) => {
create('leads_status', {
data: {
id_lead: value,
id_status: 5
}
}, {
onSuccess: ({ id }) => {
setRecord([id, value]);
},
onError: () => {
console.log();
}
});
update('leads', {
id: record[1],
data: {
id_ls: record[0]
}
}, {
enabled: !isLoadingCreate && record !== null
}, {
onSuccess: () => {
console.log(record);
},
onError: error => notify('Error', { type: 'warning' })
})
})
I tried also to put the "update" function inside the "create --> onSuccess" but also there the code is not working as I want.
In "leads_status" table records are always created for each element in "leadsIDS" array but in "leads" table only 1 records is updating.
Where am I wrong?
The useCreate and useUpdate hooks are designed for single actions. If you want to chain several actions, I suggest you use the useDataProvider hook, instead, which lets you manipulate Promises.
const dataProvider = useDataProvider();
const notify = useNotify();
try {
await Promise.all(leadsIDS.map(async (value, index) => {
const { data: leadStatus } = await dataProvider.create('leads_status', {
data: {
id_lead: value,
id_status: 5
}
});
await dataProvider.update('leads', {
id: value,
data: { id_ls: leadStatus.id }
});
}));
} catch (e) {
notify('Error', { type: 'warning' });
}
I use Strapi V4. I have a link collection and I want to update likes.
How update the relation array ? When I put new data old value are replace by the new one.
Example :
likes : [1]
if I update another time
likes:[2].
BUT I want this likes : [1,2]
I try this but It d'oesn't work. Thans for your replay
'use strict';
/**
* link controller
*/
const { createCoreController } = require('#strapi/strapi').factories;
module.exports = createCoreController('api::link.link', ({ strapi }) => ({
// Method 2: Wrapping a core action (leaves core logic in place)
async find(ctx) {
const { data, meta } = await super.find(ctx);
const linkId = data.map((link) => link.id);
const allPosts = await strapi.entityService.findMany('api::link.link', {
fields: ["id"],
filters: { id: { $in: linkId } },
populate: {
likes: { count: true },
},
});
data.forEach(link => {
link.likes = allPosts.find(({ id }) => id === link.id)?.likes?.count || 0;
});
//update value with new array => need to be fix
await strapi.entityService.update("api::link.link", {
likes: [...allPosts.likes.map(({ id }) => id), ...likes],
});
return { data, meta };
},
}));
This part need to be fix. Can you help me ? Thanks
//update value with new array => need to be fix
await strapi.entityService.update("api::link.link", {
likes: [...allPosts.likes.map(({ id }) => id), ...likes],
});
I'm using React Query and I want to update cache after the mutation, but I want to update multiple keys in one go.
I have "invoices" and ["invoice", 1] and I update it as follows:
queryClient.setQueryData("invoices", ({invoices}) => {
return {
invoices: [
...filter(invoices, invoice => invoice.id !== activeInvoice),
{
...find(invoices, invoice => invoice.id === activeInvoice),
customer: data?.updateInvoiceCustomer?.customer
}
]
}
})
queryClient.setQueryData(["invoice", activeInvoice], ({invoice}) => {
return {
invoice: {
...invoice,
customer: data?.updateInvoiceCustomer?.customer
}
}
})
Thus, now I do it per key. Is there any way to do it in one go and to use something like this:
queryClient.setQueriesData(["invoices", ["invoice", activeInvoice]], ({invoices, invoice}) => {
return {
invoices: [
...filter(invoices, invoice => invoice.id !== activeInvoice),
{
...find(invoices, invoice => invoice.id === activeInvoice),
customer: data?.updateInvoiceCustomer?.customer
}
],
invoice: {
invoice: {
...invoice,
customer: data?.updateInvoiceCustomer?.customer
}
}
}
})
Thanks
The API is a bit different, to select both queries.
First option, you would have to change the keys to be ['invoices'] and ['invoices', id], then you can "match them partially", just by providing ['invoices'] to setQueriesData.
But even then you would have to check which one is which inside the updater:
queryClient.setQueriesData(["invoices"], (data) => {
if (Array.isArray(data)) {
// updating the list
} else {
// updating the resource
if (data.id === id) {
// the filter matches not only ['invoices', id] but all ids
}
}
})
To be honest, it doesn't look very ergonomic.
Second option, you can make it a bit more readable by using a predicate as well instead
queryClient.setQueriesData({
predicate: function ({ queryKey }) {
if (queryKey[0] === 'invoices') {
return true
}
if (queryKey[0] === 'invoice' && queryKey[1] === id) {
return true
}
return false
}
}, (data) => {
if (Array.isArray(data)) {
// updating the list
} else {
// updating the resource
}
})
But to be honest, your original code still looks more readble to me
With Meteor (1.4.2.3) and React, I have the collection Objects which has an itemId which refers to the collection Items.
Currently I subscribe to the collection on the client side with:
export default createContainer(() => {
let objectsSub = Meteor.subscribe('allObjects');
var objects = Objects.find({}, {
transform: function (doc) {
doc.item = Items.findOne({
_id: doc.itemId
});
return doc;
}
}).fetch();
return {
objects: objects,
}
}, App);
This works perfect, but I think it is more elegant to merge the collections on the server side. However, none of the solutions I found seem to work
Transform at collection definition
const Objects = new Mongo.Collection('objects',
{
transform: function (doc) {
doc.item = Items.findOne({
_id: doc.itemId
})
}
});
The console gives:
Error: transform must return object
Transform at publish
if (Meteor.isServer) {
Meteor.publish('allObjects', function () {
return Objects.find({}, {
sort: { startedAt: -1 },
transform: function (doc) {
doc.item = Items.findOne({
_id: doc.itemId
});
return doc;
}
});
});
};
TypeError: Cannot read property 'name' of undefined
Where name is a property of Items
i usually do it in the publish like this:
Meteor.publish('allObjects', function () {
let cursor = Objects.find({}, {
sort: { startedAt: -1 });
});
let transformData = (fields) => {
fields.item = Items.findOne({
_id: fields.itemId
});
return fields;
};
let handle = cursor.observeChanges({
added: (id, fields) => {
fields = transformData(fields);
this.added('objects', id, fields);
},
changed: (id, fields) => {
fields = transformData(fields);
this.changed('objects', id, fields);
},
removed: (id) => {
this.removed('objects', id);
}
});
this.ready();
this.onStop(() => {
handle.stop();
});
}
I'm not sure why the removal doesn't work using the latter method. (foundList is not null)
Latter method:
List.findOne({name: listType}, function(err, foundList){
if (err){
console.log(err);
} else {
foundList.updateOne({}, { $pull: { item: { _id: itemID } } });
console.log('deletion success');
res.redirect("/" + listType);
}
});
}
Schema:
const itemSchema = {text: String}
const listSchema = {
name: String,
item: [itemSchema]
}
Below line is wrong and wont work. This is because foundList contains result of the query findOne.
foundList.updateOne({}, { $pull: { item: { _id: itemID } } });
After you call List.findOne({name: listType}, function(err, foundList), foundList contains result of the query, and you cannot call any query/mongoose-api on that. You need to call mongoose APIs like updateOne on the model object, only then you will get the result.
What you can do is you can modify that document and then save it. You can do that like this:
List.findOne({name: listType}, function(err, foundList){
if (err){
console.log(err);
} else {
let index = foundList.findIndex((list: any) => list.item._id == itemID );
if (index !== -1) {
foundList.splice(index, 1);
}
foundList.save().then(()=>{
console.log('deletion success');
res.redirect("/" + listType);
})
}
})
Or you can do all that in one query. Try this:
List.findOneAndUpdate({name: listType}, {
$pull: {
item: { _id: itemID }
}, {new:true})
.then((response) => {
console.log('deletion success');
res.redirect("/" + listType);
})
.catch((err) => res.json(err));
NOTE: Also make sure itemID is of type ObjectId and not string. You can typecast string to ObjectId as shown here.