I have an array that contains the documents id of the firebase. I need to click on the button to delete these documents in the firebase.
deletePosts() {
db.collection("users")
.doc(user.email)
.collection("posts")
.doc(this.selectedPosts[0].id)
.delete()
.then(() => {
console.log("Success!");
})
.catch(err => {
console.log(err);
});
}
},
How can I iterate documents and delete them?
You could use a batched write as follows:
deletePosts() {
let batch = db.batch();
this.selectedPosts[0].forEach(element => {
batch.delete(db.collection("users").doc(user.email).collection("posts").doc(element.id));
});
batch.commit()
.then(() => {
console.log("Success!");
})
.catch(err => {
console.log(err);
});
}
Note that a batched write can contain up to 500 operations. In case you foresee that you could have to delete more than 500 you could use Promise.all(), as follows:
deletePosts() {
const promises = [];
this.selectedPosts[0].forEach(element => {
promises.push(db.collection("users").doc(user.email).collection("posts").doc(element.id).delete());
});
Promise.all(promises);
.then(() => {
console.log("Success!");
})
.catch(err => {
console.log(err);
});
}
Related
I am refactoring some code and removing unnecessary callbacks.
The original code was:
create(_job_id).then(function (create_id) {
latest().then(function (latest_config) {
update(create_id).then(function () {
// More code
}).catch(function (err) {
console.log(err);
});
.catch(function (err) {
console.log(err);
});
.catch(function (err) {
console.log(err);
});
I have refactored it to:
const setup = async () => {
const create_id = create(_job_id);
const latest_config = await latest();
await update(create_id);
return { create_id, latest_config };
}
setup().then((setup) => {
console.log(setup);
})
But now the rejections are not handled. How would I go about elegantly handling the "catches". Promises.all looks good but I have tried to use it unsuccessfully.
Any help is appreciated.
The non-async version could also look like this:
const p_latest_config = latest();
create(_job_id)
.then(create_id => {
update(create_id).catch(err => console.log(err));
return Promise.all([create_id, p_latest_config]);
})
.then(([create_id, latest_config]) => ({create_id, latest_config}))
.then(setup => console.log(setup))
.catch(err => console.log(err));
The above assumes that create(_job_id), latest() and update(create_id) are not depended on each other via internal mutation.
add .catch(err => {//do sth with err}) on the setup() as you do the then(). Which means it catches any of the exception returned by setup().
setup()
.then((setup) => {
console.log(setup);
})
.catch(err => {console.log("Error", err.message)})
Let's say I'm using knex to run queries against an SQL database. I chain a few methods to build the query.
For example:
const sqlConfig = require('./sql.config');
var knex = require('knex')(sqlConfig);
knex.select("*")
.from("books")
.where("author", "=", "José Saramago")
.then((rows) => {
console.log(rows);
})
.catch((err) => {
console.log(err);
})
.finally(() => {
knex.destroy();
})
Now, my question is:
Is there a way to store the method chain before the knex object is created and call it later when it is created?
Something like this:
const methodChain = <<<
.select("*"),
.from("books"),
.where("author", "=", "José Saramago")
>>>
const sqlConfig = require('./sql.config');
var knex = require('knex')(sqlConfig);
knex
.methodChain()
.then((rows) => {
console.log(rows);
})
.catch((err) => {
console.log(err);
})
.finally(function() {
knex.destroy();
})
You could create a function that accepts the initial parameter in the chain:
function methodChain(in) {
return in.select("*")
.from("books")
.where("author", "=", "José Saramago");
}
methodChain(knex)
.then((rows) => {
console.log(rows);
})
.catch((err) => {
console.log(err);
})
.finally(function() {
knex.destroy();
})
Sure.
const methodChain = (x) => x
.select("*"),
.from("books"),
.where("author", "=", "José Saramago");
then later
methodChain(knex)
.then((rows) => {
console.log(rows);
})
.catch((err) => {
console.log(err);
})
.finally(function() {
knex.destroy();
})
// delete function
delete =(index) => {
const st = this.state.data;
const newSt = st[index]._id;
// fetch delete api
fetch(`http://localhost:4000/users/${newSt}`, {
method: 'DELETE'
}, (err,result) => {
if(err){
console.log(err)
}else{
result.json({'msg': 'ok'})
}
})
st.splice(index,1)
this.setState({data: st})
}
I just created a delete function for my react-express-mongoose app. but the (err,result) isnt working. What did I do wrong? (the delete function works) I am just confused about the {(err,result) => {...} and what should I do inside it.
I believe fetch is a promise so it needs the following syntax
fetch(opts)
.then(result => {
console.log(result);
})
.catch(err => {
console.error(err);
});
Fetch API: Using Fetch
Please make your function like the following structure.
// delete function
delete =(index) => {
const st = this.state.data;
const newSt = st[index]._id;
// fetch delete api
fetch(`http://localhost:4000/users/${newSt}`, {method: 'DELETE'}).then((res) => { console.log(res); })
, (err,result) => {
if(err){
console.log(err)
}else{
result.json({'msg': 'ok'})
}
})
st.splice(index,1)
this.setState({data: st})
}
I'm try to chain a couple of then functions which execute sequentially, but the last .then() is being executed before the previous is done executing and as a result it sends an empty payload. Following is the snippet:
router.get("/selectedHotels", function(req, res) {
let payload = [];
return collectionRef
.where("isOwner", "==", true)
.get() //fetches owners
.then(snapshot => {
snapshot.forEach(user => {
console.log("User", user);
collectionRef
.doc(user.id)
.collection("venues")
.get() // fetches hotels from owners
.then(snapshot => {
snapshot.forEach(doc => {
if (
doc.data().location.long == req.query.long &&
doc.data().location.lat == req.query.lat
) {
console.log(doc.id, "=>", doc.data());
payload.push({
id: doc.id,
data: doc.data()
});
}
});
})
.catch(err => {
console.log("No hotels of this user", err);
});
});
})
.then(() => {
console.log("Payload", payload);
response(res, 200, "Okay", payload, "Selected hotels");
})
.catch(err => {
console.log("Error getting documents", err);
response(res, 404, "Data not found", null, "No data available");
});
});
Any suggestions? Thanks
Your main mistake is that you have a non-promise returning function, forEach, in the middle of your nested promise chain.
router.get('/selectedHotels',function(req,res){
let payload = [];
return collectionRef.where(...).get()
.then((snapshot)=>{
snapshot.forEach(user => {
// ^^^^^^^^^^^^^^^^^ this means the outer promise doesn't wait for this iteration to finish
// ...
The easiest fix is to map your array of promises, pass them into Promise.all and return them:
router.get('/selectedHotels',function(req,res){
let payload = [];
return collectionRef.where(...).get()
.then((snapshot)=> {
return Promise.all(snapshot.map(
// ...
return collectionRef.doc(user.id).collection('venues').get()
.then(...)
))
That being said, nesting promises like this is an anti-pattern. A promise chain allows us to propagate values through the then callbacks so there's no need to nest them.
Instead, you should chain them vertically.
Here's an example of how you can do that:
router.get("/selectedHotels", function(req, res) {
return collectionRef
.where("isOwner", "==", true)
.get() //fetches owners
// portion of the chain that fetches hotels from owners
// and propagates it further
.then(snapshot =>
Promise.all(
snapshot.map(user =>
collectionRef
.doc(user.id)
.collection("venues")
.get()
)
)
)
// this portion of the chain has the hotels
// it filters them by the req query params
// then propagates the payload array
// (no need for global array)
.then(snapshot =>
snapshot
.filter(
doc =>
doc.data().location.long == req.query.long &&
doc.data().location.lat == req.query.lat
)
.map(doc => ({ id: doc.id, data: doc.data() }))
)
// this part of the chain has the same payload as you intended
.then(payload => {
console.log("Payload", payload);
response(res, 200, "Okay", payload, "Selected hotels");
})
.catch(err => {
console.log("Error getting documents", err);
response(res, 404, "Data not found", null, "No data available");
});
});
Your using firestore so you need to give all documents to map and you also need to return some values to next then. I hope this will help you to solve your problem.
router.get('/selectedVenues',function(req,res){
return collectionRef.where('isOwner', '==', true).get()
.then(snapshot => {
let venues = [];
snapshot.docs.map(user => {
venues.push(collectionRef.doc(user.id).collection('venues').get());
});
return Promise.all(venues);
}).then(snapshots => {
let payload = [];
snapshots.forEach(venues => {
venues.docs
.filter(doc =>
doc.data().longitude == req.query.lng &&
doc.data().latitude == req.query.lat
)
.map(doc =>
payload.push({
id: doc.id,
data: doc.data()
})
)
});
return payload ;
}).then(payload => {
console.log('Payload', payload);
response(res, 200, "Okay", payload, "Selected hotels");
}).catch(err => {
console.log('Error getting documents', err);
response(res, 404, 'Data not found', null, 'No data available');
});
});
You're not returning a promise from within your first then, so there's no way for the code to know that it should wait for an asynchronous result.
router.get('/selectedHotels',function(req,res){
let payload = [];
return collectionRef.where('isOwner', '==', true).get() //fetches owners
.then((snapshot)=>{
var userVenuesPromises = [];
snapshot.forEach(user => {
userVenuesPromises.push(collectionRef.doc(user.id).collection('venues').get());
})
return Promise.all(userVenuesPromises);
})
.then((snapshots) => {
snapshots.forEach((snapshot) => {
snapshot.forEach((doc)=> {
if (doc.data().location.long == req.query.long && doc.data().location.lat == req.query.lat){
console.log(doc.id, '=>', doc.data());
payload.push({
id: doc.id,
data: doc.data()
});
}
});
});
return payload;
})
.then((payload) => {
...
In addition to using Promise.all() to ensure all nested loads are done before continuing to the next step, this also removes the nested promise, instead unpacking the values from the snapshots in a an additional step.
When chaining .then with asynchronous work, you need to return the promise you want to resolve before the next .then is executed. Something like this :
return Promise.all(snapshot.map(user => {
console.log("User", user);
return collectionRef.doc(user.id).collection('venues').get() // fetches hotels from owners
.then(snapshot => {
snapshot.forEach((doc)=> {
if (doc.data().location.long == req.query.long && doc.data().location.lat == req.query.lat){
console.log(doc.id, '=>', doc.data());
payload.push({
id: doc.id,
data: doc.data()
});
}
});
}).catch((err)=>{
console.log('No hotels of this user', err);
});
});
)
You can see it in action in this sample snippet :
function asyncStuff() {
return new Promise(resolve => {
setTimeout(() => {
console.log('async')
resolve();
}, 100)
});
}
function doStuff() {
console.log('started');
asyncStuff()
.then(() => {
return Promise.all([0,1,2].map(() => asyncStuff()));
})
.then(() => {
console.log('second then');
})
.then(() => console.log('finished'));
}
doStuff();
And see that without the return it gives your initial behaviour :
function asyncStuff() {
return new Promise(resolve => {
setTimeout(() => {
console.log('async')
resolve();
}, 100)
});
}
function doStuff() {
console.log('started');
asyncStuff()
.then(() => {
Promise.all([0,1,2].map(() => asyncStuff()));
})
.then(() => {
console.log('second then');
})
.then(() => console.log('finished'));
}
doStuff();
I have two tables in mysql and want to query a table depending on the result of another, so I wrote a function like
export function getLocations(req, res) {
const database = new Database();
database.query('select * from districts')
.then(rows => {
let appData = [];
rows.forEach(row => {
const new_database = new Database();
new_database.query(`SELECT locations.id,locations.name, IF(subscriptions.id IS NULL,0,1) as subscribed
FROM locations
LEFT JOIN subscriptions
ON (subscriptions.location_id = locations.id AND subscriptions.user_id=1)
WHERE locations.district=?`,row.id)
.then(sub_rows => {
let district=row;
district["locations"]=sub_rows;
appData.push(district);
new_database.close();
}, err => {
return new_database.close().then(() => { throw err; })
})
.catch(err => {
console.log(err);
res.status(500).json("Database Error");
})
});
res.status(200).json(appData); //final result here
database.close()
}, err => {
return database.close().then(() => { throw err; })
})
.catch(err => {
console.log(err);
res.status(500).json("Database Error");
})
}
Here I want to get run second query based for each of the row of first query.
I am getting an empty array as result. My first query is executing properly and I logged to see all rows are being returned. What could be the issue?
You can make it work by making this async
rows.forEach(async row => {
const new_database = new Database();
await new_database.query(`SELECT locations.id,locations.name, IF(subscriptions.id IS NULL,0,1) as subscribed
FROM locations
LEFT JOIN subscriptions
ON (subscriptions.location_id = locations.id AND subscriptions.user_id=1)
WHERE locations.district=?`,row.id)
.then(sub_rows => {
let district=row;
district["locations"]=sub_rows;
appData.push(district);
new_database.close();
}, err => {
return new_database.close().then(() => { throw err; })
})
.catch(err => {
console.log(err);
res.status(500).json("Database Error");
})
});
The operation you are performing is I/O and JS is single threaded. It means in layman terms it will not wait will iterating your loop where it is going to be making a request where there is some wait while the request processes. You need to tell JS that this event is asynchronous. For this you need to use async/await
Guides
forEach async/await
async/await MDN documentation
I don't have the environment in this machine. it may have some errors but you can fix it if it have, take a look at the following logic
export function getLocations(req, res) {
const database = new Database();
const promises=[];
database.query('select * from districts')
.then(rows => {
let appData = [];
rows.forEach(row => {
promises.push(getAnotherQuery(row));
});
database.close()
}, err => {
return database.close().then(() => { throw err; })
})
.catch(err => {
console.log(err);
res.status(500).json("Database Error");
})
return Promise.all(promises).then(result)=> res.status(200).json(result); //final result here
}
getAnotherQuery=(row)=>{
return new Promise((resolve,reject)=>{
const new_database = new Database();
const appData=[]
new_database.query(`SELECT locations.id,locations.name, IF(subscriptions.id IS NULL,0,1) as subscribed
FROM locations
LEFT JOIN subscriptions
ON (subscriptions.location_id = locations.id AND subscriptions.user_id=1)
WHERE locations.district=?`,row.id)
.then(sub_rows => {
let district=row;
district["locations"]=sub_rows;
appData.push(district);
new_database.close();
resolve(appData);
}, err => {
return new_database.close().then(() => { throw err; })
})
.catch(err => {
console.log(err);
res.status(500).json("Database Error");
})
});
}