How do I retrieve firebase child data without also retrieving the key? - javascript

I'm currently trying to get the child data without the key in an array so I can add that to my redux state.
ref.orderByChild(`userId`).equalTo(googleUserInfo[0].userId).once('value', streamSnapshot => {
if (streamSnapshot.exists()) {
googleUserInfo[0] = (streamSnapshot.val())
}
Currently this returns
[{…}]0: {-LbJneodI2SaUglB6fwx: {…}}length: 1__proto__: Array(0)
But I would like this
[{…}]
0:
{
displayName: "Seth Jones"
userAvi: "https://lh6.googleusercontent.com/-RABcz3kK1ew/AAAAAAAAAAI/AAAAAAAAAAA/ACHi3rfzHCLV9A7LfiGWKtTOuq5rJmtQpg/s96-c/photo.jpg"
userEmail: "xxxx#gmail.com"
userFirstName: "Seth"
userId: "103977654052015523435"
userLastName: "Jones"
}

When you execute a query against the Firebase Database, there will potentially be multiple results. So the snapshot contains a list of those results. Even if there is only a single result, the snapshot will contain a list of one result.
You need to loop over the results, to get to the individual values:
ref.orderByChild('userId').equalTo(googleUserInfo[0].userId).once('value', streamSnapshot => {
streamSnapshot.forEach((child) => {
googleUserInfo[0] = (child.val())
});

Related

Model.updateOne() correct syntax [duplicate]

Consider I have this document in my MongoDB collection, Workout:
{
_id: ObjectId("60383b491e2a11272c845749") <--- Workout ID
user: ObjectId("5fc7d6b9bbd9473a24d3ab3e") <--- User ID
exercises: [
{
_id: ObjectId("...") <--- Exercise ID
exerciseName: "Bench Press",
sets: [
{
_id: ObjectId("...") <--- Set ID
},
{
_id: ObjectId("...") <--- Set ID
}
]
}
]
}
The Workout object can include many exercise objects in the exercises array and each exercise object can have many set objects in the sets array. I am trying to implement a delete functionality for a certain set. I need to retrieve the workout that the set I want to delete is stored in. I have access to the user's ID (stored in a context), exercise ID and the set ID that I want to delete as parameters for the .findOne() function. However, I'm not sure whether I can traverse through the different levels of arrays and objects within the workout object. This is what I have tried:
const user = checkAuth(context) // Gets logged in user details (id, username)
const exerciseID, setID // Both of these are passed in already and are set to the appropriate values
const workoutLog = Workout.findOne({
user: user.id,
exercises: { _id: exerciseID }
});
This returns an empty array but I am expecting the whole Workout object that contains the set that I want to delete. I would like to omit the exerciseID from this function's parameters and just use the setID but I'm not sure how to traverse through the array of objects to access it's value. Is this possible or should I be going about this another way? Thanks.
When matching against an array, if you specify the query like this:
{ exercises: { _id: exerciseID } }
MongoDB tries to do an exact match on the document. So in this case, MongoDB would only match documents in the exercises array of the exact form { _id: ObjectId("...") }. Because documents in the exercises have other fields, this will never produce a match, even if the _ids are the same.
What you want to do instead is query a field of the documents in the array. The complete query document would then look like this:
{
user: user.id,
"exercises._id": exerciseID
}
You can perform both find and update in one step. Try this:
db.Workout.updateOne(
{
"user": ObjectId("5fc7d6b9bbd9473a24d3ab3e"),
},
{
$pull: {
"exercises.$[exercise].sets": {
"_id": ObjectId("6039709fe0c7d52970d3fa30") // <--- Set ID
}
}
},
{
arrayFilters: [
{
"exercise._id" : ObjectId("6039709fe0c7d52970d3fa2e") // <--- Exercise ID
}
]
}
);

Updating Multiple Records with varying conditions in a single query

With Typeorm + NestJS + Postgres is there a way to update multiple records with varying conditions and varying values in a single query. Normally I could do
await getConnection()
.createQueryBuilder()
.update(Entity)
.set({ columnName: "newValue" })
.where({ id: In(1,2,3,4,5,6) })
.execute();
and this will update all entries with the specified ID. But in the case of having the following data structure
const array = [{id: 1, value: 'New Value For Record 1'},..., {id: 1000, value: 'New Value For Record 1000'}]
I could use for loop to update each single entry as below:
array1.forEach(rec => {
usersRepo.update(
{ id: rec.id },
{
columnName: rec.value
}
);
})
but this does not seem to be efficient and won't give good performance. Is there a way to do achieve multiple update on varying conditions with query builder.
You can achieve that we two queries
Get all the rows you need, basically a find would be enough, like this.
after that map the results array to do your changes
Last thing to do is to use save method, from Typeorm docs:
save - Saves a given entity or array of entities. If the entity already exists in the database, then it's updated. If the entity does not exist in the database yet, it's inserted.
const array = await yourRepo.find()
const newArr = array.map(el=> ({...el, value: 'New Value For Record'+el.id})
await yourRepo.save(newArr)
Cheers.

Access array.length to render number from MongoDB database in React app

I have the following Mongo Schema which has an array of objects inside of 'balance':
const SubmitDebtSchema = new Schema ({
balance: [{
balanceDate: Date,
newBalance: Number
}],
});
An example console.log of said Schema would then be like this:
balance: Array [
{
id: "20304929403048fd636",
balanceDate: "2020-11-23T10:57:58.845Z",
newBalance: 300
},
{
id:"20fhdjfjwjh39g9395",
balanceDate: "2020-11-23T11:54.58.845Z",
newBalance: 200
} ]
I then have an Axios call which sets an array state like follows:
componentDidMount() {
axios.get("/api/fetch/fetchDebtCards")
.then((response) => {
this.setState({
debts: response.data
})
console.log(this.state.debts)
})
}
Finally, I have the following function which I'm attempting to use to render the results on my web page.
const fetchDebts = this.state.debts.map (debt => {
return (
<IndividualDebtCard key={debt._id}
balance={debt.balance[debt.balance.length - 1][2]}
/>
)
})
This maps through my database, and is attempting to pull the last balance entry to render as props on my web page.
From this last balance entry, I then want to pull the newBalance figure to render in my webpage. So in this example, balance would equal 200.
However, the array.length isn't working. I can't seem to access the last newBalance in my array.
I have simplified my call as it also pulls other entry details, but for simplicity's sake I have removed them. The rest of the call works fine! So it's not a problem with anything else, just getting the last value in the array.
Can anyone point out what I'm doing wrong here?
I think the error is happening because you are using array of objects and trying to get the second index from balance object. For example balance[1][2] is undefined because you can't access objects by index. You can only access objects by key. You can try using balance[1].newBalance if that solves it.
const fetchDebts = this.state.debts.map (debt => {
return (
<IndividualDebtCard key={debt._id}
balance={debt.balance[debt.balance.length - 1].newBalance}
/>
)
})

Cloud firestore: Update field values in nested array object with dot notation

Please help me solve this, I would like to update the fields using dot notation, using set() but each time I run with the below implementation. I have the fields added to firestore as e.g studentInfo.0.course.0.courseId instead of updating the already existing ones.
Json sample as it sits in firestore
"schoolId": "school123",
"studentInfo": [
{
"studentId": "studentI23",
"regDate": "2020-04-18",
"course": [
{
"courseId": "cs123",
"regDate": "2020-05-28",
"status": "COMPLETED"
}
]
}
],
"registered":"yes"
}
Code logic
const query = firestore.collection('users').where('registered', '==', 'yes')
const students = await query.get()
students.forEach(student => {
firestore.doc(student.ref.path).set({
'studentInfo.0.studentId': '345','studentInfo.0.course.0.courseId': '555'
}, { merge: true })
})
On the docs https://firebase.google.com/docs/firestore/manage-data/add-data#update_fields_in_nested_objects I can only find updating nested objects but not nested array objects.
It is indeed not possible to update a single element in an array using dot notation, or otherwise. To update an array you'll need to:
Read the document
Get the current value of the array from it
Determine the new array contents
Write the entire updated array back to the database.
The only alternative array operations are array-union and array-remove, which add and remove unique elements to/from the array - essentially treating it as a mathematical set. But since you are looking to update an existing element, these operations are of no use here.
Also see:
Firestore Update single item in an array field
Firestore update specific element in array
How to update an "array of objects" with Firestore?
There is no direct way to update the as stated in the article. You can either run a transaction to get the latest array value and then updating the array with the final array value. That would be as below:
await firestore.runTransaction((transaction: Transaction) => {
const students: Array<Students> = firestore
.collection("users")
.where("registered", "==", "yes");
students.forEach((student) => {
const firebaseDoc = firestore.doc(student.ref.path);
transaction.set(
firebaseDoc,
{
"studentInfo.0.studentId": "345",
"studentInfo.0.course.0.courseId": "555",
},
{ merge: true }
);
});
});
Inside transaction I am getting the array first and then updating each values as per my need. This will make the whole operation atomic so the issues mentioned in the article will not come.
Alternatively, you can also model your firestore database as below
"schoolId": "school123",
"studentInfo": {
"studentI23": {
"studentId": "studentI23",
"regDate": "2020-04-18",
"course": [
{
"courseId": "cs123",
"regDate": "2020-05-28",
"status": "COMPLETED"
}
]
}
},
"registered":"yes"
}
Above I have changed the array to map, since in map you can update the each field based on dot notation fields(doc), hence. you can achieve your end result. This solution will avoid any transaction query and will be faster

Javascript, Redux thunk, synchronous / nested promises

I have a direct messaging application. All the data is stored in Firebase. Each chat contains an array of user IDs.
I use the following function to get all chats from componentDidMount():
return dispatch => new Promise(resolve => FirebaseRef.child('chats')
.on('value', snapshot => resolve(dispatch({
type: 'CHATS_REPLACE',
data: snapshot.val() || [],
})))).catch(e => console.log(e));
Which goes through:
chatReducer(state = initialState, action) {
case 'CHATS_REPLACE': {
let chats = [];
if (action.data && typeof action.data === 'object') {
chats = Object.values(action.data).map(item => ({
id: item.id,
title: item.title,
authorizedUsers: Object.values(item.authorizedUsers).map(user => ({
id: user.id,
// Somedata: fetchUserData(user.id)
// -> pretty sure it can't be done here <-
})),
}));
}
return {
...state,
error: null,
loading: false,
chats,
};
How would I go about fetching more data of every user inside each chat from Firebase at users/:uid?
I don't know what is the use case of this. It would be great if you can share, like how much information about the user you want to use. If its small data, why don't you add it in same API Only. You can pass the users data in the same object with user id as keys, and use the same keys inside your nested data like (only if user data is small or you know API data is always limited like because of pagination or page size. :
{
posts : [
{
title : 'abc'
authorizedUsers : ['1a', '2b', '3c']
}, ....
],
users : {
'1a' : {
name : 'john doe',
profileImage : 'https://some.sample.link',
},
'2b' : {
name : 'bob marshal',
profileImage : 'https://some.sample.link2',
}
}
}
If data is huge or cannot be added in the API ( because API is owned by 3rd party), then only place you can put you code is, instead of just dispatching the actions after the response is recieved, loop over the response in your service only, make async calls to get all "Unique users" only, append that data to the data you recieved from the previous api call, and then dispatch the action with the complete data to the store. It might not be the best way, as everything will have to stall i.e. even the data recieved in 1st api also will stall(not updated on screen) till all the users data is fetched. But best solution can only be given once we know more details about the use case. Like maybe lazy fetching the users data as end user scrolls the screen and may see a particular post Or fetching the user details once you start rendering your data from 1st API call like making a component for showing user associate with a post and in its componentDidMount, you pass the userIds as props from top component which might be "article/post/blog" component and it fetched the data at the time when it is actually rendering that "article/blog/post".
Hope this helps.

Categories