Firebase cloudstore collections map vs. forEach - javascript

I am trying to use the map function to create an array of the items returned from a collection.
My implementation is using the forEach to iterate which works fine. However, I can't get it to work with the map function.
Here's the code:
firestore.collection("notes").doc(this.props.id).collection('items').get()
.then((snap) => {
let items = []
snap.forEach((doc) => {
items.push({id:doc.id,text:doc.data().text})
console.log(`${doc.id} => ${doc.data()}`);
});
console.log(items)
});
However, this doesn't work:
firestore.collection("notes").doc(this.props.id).collection('items').get()
.then((snap) => {
let items = snap.map((doc) => {
return {id:doc.id, text:doc.data().text}
})
console.log(items)
});
It shoots an error that 'snap.map' is not a function.
I can't figure out where I'm tripping?

The forEach method exists, but not map.
However you can get an array of docs:
An array of all the documents in the QuerySnapshot.
Which you can call map on, like this:
let items = snap.docs.map(doc => {
return { id: doc.id, text: doc.data().text }
})

snap may not be a true array. It's probably some sort of array-like object. Try creating a new array with the spread operator (...), then working on that, like this:
firestore.collection("notes").doc(this.props.id).collection('items').get()
.then((snap) => {
let items = [...snap].map((doc) => {
return {id:doc.id, text:doc.data().text}
})
console.log(items)
});
That should convert it to a true array which will give you the ability to use the .map function.

A little example, in my case, Im update something:
const query = ...collection("notes").doc(this.props.id).collection('items');
query.snapshotChanges().map(changes => {
changes.map(a => {
const id = a.payload.doc.id;
this.db.collection("notes").doc(this.props.id).collection('items').update({
someItemsProp: newValue,
})
})
}).subscribe();
}

Related

Argument of type 'void' is not assignable to parameter of type 'SetStateAction<never[]>'.ts(2345)

I want to implement a code to retrieve data from firestore collection
I use typescript as a programming language. But found an error while following the existing example, while the example uses javascript
the error occured at setBookmark(querySnapshot.forEach((doc) => {
React.useEffect(() => {
const getBookmark = async () => {
try {
const querySnapshot = await getDocs(collection(db, "bookmark"));
setBookmark(querySnapshot.forEach((doc) => {
id: doc.id,
data: doc.data()
// console.log(`${doc.id} => ${doc.data()}`);
}));
} catch (e) {
console.error("Error getting document: ", e);
}
};
getBookmark();
}, []);
forEach returns void hence the error. I think you want to use .map instead. Your current would always set setBookmark(null). I would write the code for you but its not clear what goes into bookmark.
map will return a new list based on the old list while forEach is to just iterate and not returning anything.
As I remember, you can't use map with querySnapshot, but you can create an array from it.
const myArray = [];
querySnapshot.forEach((doc) => {
myArray.push({
id: doc.id,
data: doc.data()
})
setBookmark(myArray)

FirebaseError: Function DocumentReference.update() called with invalid data. Nested arrays are not supported

.
I am trying to implement the add-to-cart feature with my firebase using firestore.
I have a fetch function that gets any existing item that is in the cart already. But all I need is the value in the Array items but when I add it to my fectchedcartItems list, it creates a nested array which gives me issues when I am trying to update the cart as it doesn't support nested array. Is there a way to just get the values and not create a nested array?
fetchItems = () => {
Fire.shared.firestore.collection("cart").where("userId", "==", this.state.uid).get().then((qSnap) => {
let itemList = []
qSnap.docs.forEach(item => {
itemList.push(item.data().items)
this.setState({
fetchedcartItems: itemList
})
})
console.log("fectched product", this.state.fetchedcartItems);
});
}
addItemToCart = () => {
this.fetchItems()
let items = this.state.fetchedcartItems
items.push({ item: this.state.name, userId: this.state.uid })
this.setState({
items: items,
fectchingitemsloading: true,
},
() => Fire.shared
.firestore
.collection('cart')
.doc(this.state.cartId)
.update({
items: items
})
.then(() => this.fetchItems())
)
}
let itemList = []
qSnap.docs.forEach(item => {
itemList.push(item.data().items)
this.setState({
fetchedcartItems: itemList
})
})
item.date().items appears to be an array. So when you push an array into another array, you get a nested array. Instead, you should push the individual items into the array so that you end up with just a single toplevel array:
let itemList = [];
qSnap.docs.forEach(item => {
itemList.push(...item.data().items) // <---- added spread syntax
})
// Moved the setState outside the loop; there's no need to set state multiple times
this.setState({
fetchedcartItems: itemList
})
Or an alternative using flatMap:
let itemList = qSnap.docs.flatMap(item => item.data().items);
this.setState({
fetchedcartItems: itemList
})

Array.filter() with async arrow function

I am trying to filter my array using Array.filter() function, however I came across this issue.
I need to call some other function inside the filter function asynchronously. However the array is not changing its value based on the conditions that I define in the function.
const filterWithEmail = data.filter(async (doc) =>
{
const findUser = await UserService.findUser(doc.id).catch(err => {});
if (findUser)
{
const { email } = findUser;
return regexFilter ? regexFilter.test(email.normalize("NFKC")) : false;
}
});
This code doesn't affect the data array at all for some reason.
Any suggestions what I'm doing wrong?
Thank you in advance.
filter expects the return value of the callback to be a boolean but async functions always return a promise.
You don't know if you want to return true or false in time to tell filter which it is.
What you possibly want to do is:
map the data from data to { keep: true, original_data: data } (using an async callback)
Pass the resulting array of promises to Promise.all
await the return value of Promise.all
filter that array with: .filter(data => data.keep)
Get the original objects back with .map(data => data.original_data)
Something along these lines (untested):
const filterWithEmail = (
await Promise.all(
data.map(async (data) => {
const findUser = await UserService.findUser(doc.id).catch((err) => {});
let keep = false;
if (findUser && regexFilter)
keep = regexFilter.test(email.normalize("NFKC"));
return { data, keep };
})
)
)
.filter((data) => data.keep)
.map((data) => data.data);

Svelte derived stores and array sort

I set up a store containing a list of rides loaded from my API:
const loadRides = () => client.service('rides').find({
query: {
$sort: {
date: -1,
}
}
});
const createRides = () => {
const { subscribe, update } = writable([], async (set) => {
try {
const rides = await loadRides().then((result) => result.data);
set(rides);
} catch (e) {
console.error(e);
}
// Socket update binding?
});
subscribe((rides) => console.debug('rides', rides));
return {
subscribe,
refresh: () => loadRides().then((result) => update(() => result.data)),
};
};
export const rides = createRides();
Then I set a two derived stores for past and future rides:
export const pastRides = derived(
rides,
($rides) => $rides
.filter((ride) => ride.steps.every((step) => step.doneAt))
,
);
export const comingRides = derived(
rides,
($rides) => $rides
.filter((ride) => ride.steps.some((step) => !step.doneAt))
.sort((rideA, rideB) => {
const compare = new Date(rideA.date) - new Date(rideB.date);
console.log(rideA.date, rideB.date, compare);
return compare;
})
,
);
The sort method on the second one does not have any effect.
So I tried to put this method before the filter one. It works, but it also sort $pastRides. In fact, it is sorting the full $rides array and it make sens.
But I does not understand why the sort after filter does not work.
What did I miss?
Array.sort is mutable. Meaning, when you call rides.sort, it will sort and modify rides and return the sorted rides.
When you use derived(rides, ($rides) => ... ), the $rides you received is the original rides array that you call set(rides). So you can imagine that both the pastRides and comingRides received the same $rides array.
you can observe this behavior in this repl
To not having both derived interfere with each other, you can create a new array and sort the new array:
const sorted_1 = derived(array, $a => [...$a].sort());
you can try this out in this repl.

How to add doc ID to every element of querySnapshot from firestore query?

I am trying to add the document ID from each doc in the query snapshot to the corresponding element in the array I am pushing then committing to state.
I need the document ID in my state so I can query specific docs by there id in order to update them.
I am currently looping through the snapshot with forEach() like the google docs suggest but I am trying to use map() to put each element in my let cards =[] array but also include the doc.id in the element somewhere.
actions: {
async nuxtServerInit(vuexContext, context) {
let cards = []
await db
.collection('Assets')
.get()
.then(snapshot => {
snapshot.forEach(doc => {
cards.map(doc.data())
})
vuexContext.commit('setCards', cards)
})
},
Currently in my state I am still seeing
loadedCards:Array[1]
0:Object
authors:"noFace1"
description:"hopefully this fucking works"
imageurl:Array[1]
slug:"the-big-test-bunny"
title:"The big test bunny"
type:"Video"
user:"VbomJvANYDNe3Bek0suySs1L8oy1"
I need to have another property of id: xblkujoifduodsifl (just example id) included in my state.
change this line cards.map(doc.data()) for this
cards.map(doc => {
const id = doc.id
const data = doc.data()
return Object.assign({}, id, { ...data })
})
the id is not part of the doc unless you explicit copy it
This is are more verbose solution from my code that worked:
.db
.collection("organizations")
.doc(activeOrganization)
.collection("people")
.get()
.then((snapshot) => {
if (!snapshot.empty) {
let people = [];
snapshot.forEach((doc) => {
let dict = { ...doc.data(), id: doc.id};
people.push(dict);
});
console.log(people);
} else {
reject("No document corresponding to the query!");
}
})
.catch((err) => reject(err));

Categories