Firebase: Update or Create - javascript

i am using Firebase and Node.
I would like to use the same method to either update an object, or create it, if for some reason it does not exist.
Consider the following method
const firebaseSave = async function(data) {
const uid = firebase.auth().currentUser.uid
const profilePath = `users/${uid}`
const profileData = {
name: data.name,
}
const userRef = firebaseDb.child(profilePath)
await userRef.set(profileData)
}
What would be the best and correct way to determine if update or set should be called?
thanks

basically with:
"set" you write or replace data to a defined path, like messages/users/
, you can update the information or create it.
Check this out: https://firebase.google.com/docs/database/admin/save-data

I'd say get the data, check if there was something, if there wasn't default to empty object - then update that object.
Something like:
const valueSnapshot = await userRef.once('value');
const userValue = valueShapshot.exists() ? valueShapshot.val() : {};
const profileData = { ...userValue, name: data.name };
await userRef.set(profileData);
That is, assuming you would want to keep the existing data, and merge any new data into it. If you don't care about overwriting anything, no check would be needed at all.

This is my idea, I apply it for the frontend side.
I use the id to identify if we should create or update.
Because on the frontend, usually, new data don't have any id.
In this way, we don't have to check the doc exists or not.
I'm not sure about backend, but it's okay on the frontend.
createOrUpdateTemplate(template: Label): Observable<unknown> {
if (!template.id) {
return from(
this.fs.collection<Label>('templates').add({
...template,
createdAt: firebase.default.firestore.FieldValue.serverTimestamp(),
updatedAt: firebase.default.firestore.FieldValue.serverTimestamp(),
})
);
} else {
return from(
this.fs
.collection<Label>('templates')
.doc(template.id)
.update({
...template,
updatedAt: firebase.default.firestore.FieldValue.serverTimestamp(),
})
);
}
}

await userRef.set(profileData, {merge: true})
You can add {merge: true} which will update the previous document and will create a new document if document do not already exist.

Related

Match object inside object in jest

I'm testing a function to see if, when called, it will return the proper created list.
To start, I create the elements, using the createDesign.execute() functions. It's tested on another file and working.
Then, I call the function I want to test: listAllDesigns.execute() and store it's value in a variable.
If I console.log(list), it returns the full list properly.
In pseudocode, what I'd like to do is: Expect list array to have an element with the design object and, within it, a design_id that equals "payload3".
How should I write this test?
Is there a better way to do this? (other than checking if list !== empty, please)
it('should return a list of all designs', async () => {
// Create fake payloads
const payload1 = {
...defaultPayload,
...{ design: { ...defaultPayload.design, design_id: 'payload1' } },
};
const payload2 = {
...defaultPayload,
...{ design: { ...defaultPayload.design, design_id: 'payload2' } },
};
const payload3 = {
...defaultPayload,
...{ design: { ...defaultPayload.design, design_id: 'payload3' } },
};
await createDesign.execute(payload1);
await createDesign.execute(payload2);
await createDesign.execute(payload3);
const list = await listAllDesigns.execute();
// expect(list). ????
});
The easiest method would be a combination of expect.arrayContaining and expect.objectContaining like so:
expect(list).toEqual(
expect.arrayContaining([
expect.objectContaining({
design: expect.objectContaining({
design_id: "payload3"
})
})
])
);

How to populate an array inside a map function in js and send it to the server?

This is my ObjectIds array -
obj_ids = [
"5ee71cc94be8d0180c1b63db",
"5ee71c884be8d0180c1b63d9",
"5ee71c494be8d0180c1b63d6",
"5ee71bfd4be8d0180c1b63d4"
]
I am using these objectids to serach whether they exist in the db or not and based on that I want to send the response to server.
This is the code I am trying but I dont know how to populate the array and send it to the server.
var msg = [];
obj_ids.map((ele) => {
Lead.find({ _id: ele._id }, async function (error, docs) {
if (docs.length) {
msg.push(
`Lead already exist for Lead id - ${ele._id} assgined to ${docs[0].salesPerson}`
);
} else {
msg.push(`Lead doesn't exist for Lead id: ${ele._id}`);
const newDuty = new AssignedDuty({
duty: ele._id,
salesPerson: req.body.salesPerson,
});
await newDuty.save();
}
});
});
res.json(msg);
By doing this approach I am getting an empty array. I cannot put res.json(msg) inside the loop. If it is possible by using async-await, please guide me through.
You don't need to make multiple queries to find whether given object ids exist in the database.
Using $in operator, you can make one query that will return all the documents where the _id is equal to one of the object id in the list.
const docs = await Lead.find({
_id: {
$in: [
"5ee71cc94be8d0180c1b63db",
"5ee71c884be8d0180c1b63d9",
"5ee71c494be8d0180c1b63d6",
"5ee71bfd4be8d0180c1b63d4"
]
}
});
After this query, you can check which object id is present in the docs array and which is absent.
For details on $in operator, see $in comparison operator
Your code can be simplified as shown below:
const obj_ids = [
"5ee71cc94be8d0180c1b63db",
"5ee71c884be8d0180c1b63d9",
"5ee71c494be8d0180c1b63d6",
"5ee71bfd4be8d0180c1b63d4"
];
const docs = await Lead.find({
_id: { $in: obj_ids }
});
const msg = [];
obj_ids.forEach(async (id) => {
const doc = docs.find(d => d._id == id);
if (doc) {
msg.push(
`Lead already exist for Lead id - ${doc._id} assgined to ${doc.salesPerson}`
);
}
else {
msg.push(`Lead doesn't exist for Lead id: ${id}`);
const newDuty = new AssignedDuty({
duty: id,
salesPerson: req.body.salesPerson
});
await newDuty.save();
}
});
res.json(msg);

Insert and merge a particular field in firestore firebase

I have a doc in my collection in this format
name: xyz,
email: xyz#email.com,
age: 30,
address: {
street_no: {
complete_address: somedata,
pincode: somepin
},
city:somecity,
state:somestate,
landmark:nearby
}
And inside this doc I am trying to insert and merge the complete_address with the previous record. To achieve that I am trying this
const database = firebase.firestore();
var dataRef = database.collection('collection');
var query = dataRef.doc(key+"").get().then(doc=>{
if(!doc.exists){
res.send("doesn't exist");
}else{
//few checks
if(doc.data().accessid != accessid){
res.send("accessid doesn't match")
}
//here I am trying to insert and merge with the previous data
var form_path = 'address.street_no.complete_address';
dataRef.doc(key+"."+form_path).set({
another_address
}, {merge: true});
}
}).catch(err=>{
console.log(err)
})
But when I execute this it just add another document in a collection followed by this path key.address.street_no.complete_address.
What can I do to only insert and merge with the previous complete_address ?
There is . instead of / in form_path because got few ideas from this link
I believe your issue lies within the next couple of lines starting at
var form_path = 'address.street_no.complete_address';
Next, You're using dataRef.doc(key+"."+form_path)
which means the only document being set is
/addressCollection/key.{addressCollectionId}
and addressCollectionId being address.street_no.complete_address
Instead what you want to do is access the property within the document using dot notation like so.
address: {
street_no: {
complete_address
Example.
someDocument.update({
"address.street_no.complete_address": "some_data"
});
Note that "some_data" will replace what ever data is currently stored. You'll want to do one read and merge the data. For example.
const anotherAddress = { address: "123 Fake Street" };
const document = firebase
.firestore()
.collection("addressCollection")
.doc("someAddressId");
document
.get()
.then(snap => {
const data = snap.data();
const completeAddress = data.address.street_no.complete_address };
// We're using the spread operator here to combine the current completeAddress with anotherAddress
return { completeAddress, ...anotherAddress };
})
.then(newCompleteAddress =>
document.update({
"address.street_no.complete_address": newCompleteAddress
})
);
I got this working.
So I figured out what I was trying to do earlier will create another document in a collection with data respect to it. So I start treated everything as an object and passed an object data to set() method.
const database = firebase.firestore();
var dataRef = database.collection('collection');
var query = dataRef.doc(key+"").get().then(doc=>{
if(!doc.exists){
res.send("doesn't exist");
}else{
//few checks
if(doc.data().accessid != accessid){
res.send("accessid doesn't match")
}
//here I am trying to insert and merge with the previous data
var mergeData = {
address : {
}
}
var new_address = {
key: "address_data"
}
mergeData.address[street_no] = {complete_address : address}
if(dataRef.doc(key+"").set(mergeData, {merge: true})){
res.send("done")
}else{
res.send("failed")
}
}
}).catch(err=>{
console.log(err)
})

Using react-apollo (GraphQL) to do an insert mutation, how do I get the inserted id for the local store update?

I have form with the following submit handler, which then uses react-apollo's mutate method to send the GraphQL mutation:
const doSubmit = (values, { setSubmitting, setErrors }) => {
//create a new obj: can't submit the extra stuff in values, GraphQL will yell at us!
const newUser = {
firstName: values.firstName,
lastName: values.lastName,
emailAddress: values.emailAddress,
isActive: values.isActive,
sharedAccount: values.sharedAccount,
userId: values.userId
};
mutate({
variables: { user: newUser },
update: store => {
//only update if adding a new user
if (values.userId !== 0) {
return;
}
let data = store.readQuery({ query: USER_LIST_QUERY });
data.users.push(newUser);
store.writeQuery({ query: USER_LIST_QUERY, data });
}
}).then(() => {
//let formik know that submit is complete
setSubmitting(false);
//todo: redirect to userlist
});
};
This approach is based on this mutate-with-update example in the docs.
I know that the mutate promise handler will have access to the inserted id because it will be returned in the graphql response, but that doesn't seem to be in time.
Unless I could have access to the store from that promise handler too, I don't see how this is possible. But it seems like such a common use case that there has to be a way, right? Am I missing something obvious?
The update method provides the updated object as the second parameter
update: (store, { ... access userId here ... }) => { ... }
From the docs:
This function will be called twice over the lifecycle of a mutation. Once at the very beginning if an optimisticResponse was provided. The writes created from the optimistic data will be rolled back before the second time this function is called which is when the mutation has succesfully resolved. At that point update will be called with the actual mutation result and those writes will not be rolled back.

Apollo Client: Upsert mutation only modifies cache on update but not on create

I have an upsert query that gets triggered on either create or update. On update, Apollo integrates the result into the cache but on create it does not.
Here is the query:
export const UPSERT_NOTE_MUTATION = gql`
mutation upsertNote($id: ID, $body: String) {
upsertNote(id: $id, body: $body) {
id
body
}
}`
My client:
const graphqlClient = new ApolloClient({
networkInterface,
reduxRootSelector: 'apiStore',
dataIdFromObject: ({ id }) => id
});
The response from the server is identical: Both id and body are returned but Apollo isn't adding new ids into the data cache object automatically.
Is it possible to have Apollo automatically add new Objects to data without triggering a subsequent fetch?
Here is what my data store looks like:
UPDATE
According to the documentation, the function updateQueries is supposed to allow me to push a new element to my list of assets without having to trigger my origin fetch query again.
The function gets executed but whatever is returned by the function is completely ignored and the cache is not modified.
Even if I do something like this:
updateQueries: {
getUserAssets: (previousQueryResult, { mutationResult }) => {
return {};
}
}
Nothing changes.
UPDATE #2
Still can't get my assets list to update.
Inside updateQueries, here is what my previousQueryResult looks like:
updateQueries: {
getUserAssets: (previousQueryResult, { mutationResult }) => {
return {
assets: []
.concat(mutationResult.data.upsertAsset)
.concat(previousQueryResult.assets)
}
}
}
But regardless of what I return, the data store does not refresh:
For reference, here is what each asset looks like:
Have you followed the example here ?
I would write the updateQueries in the mutate like this:
updateQueries: {
getUserAssets: (previousQueryResult, { mutationResult }) => {
const newAsset = mutationResult.data.upsertAsset;
return update(prev, {
assets: {
$unshift: [newAsset],
},
});
},
}
Or with object assign instead of update from immutability-helper:
updateQueries: {
getUserAssets: (previousQueryResult, { mutationResult }) => {
const newAsset = mutationResult.data.upsertAsset;
return Object.assign({}, prev, {assets: [...previousQueryResult.assets, newAsset]});
},
}
As you state in your update, you need to use updateQueries in order to update the queries associated with this mutation. Although your question does not state what kind of query is to be updated with the result of the mutation, I assume you have something like this:
query myMadeUpQuery {
note {
id
body
}
}
which should return the list of notes currently within your system with the id and body of each of the notes. With updateQueries, your callback receives the result of the query (i.e. information about a newly inserted note) and the previous result of this query (i.e. a list of notes) and your callback has to return the new result that should be assigned to the query above.
See here for an analogous example. Essentially, without the immutability-helper that the given example uses, you could write your updateQueries callback as follows:
updateQueries: {
myMadeUpQuery: (previousQueryResult, { mutationResult }) => {
return {
note: previousQueryResult.note(mutationResult.data.upsertNode),
};
}
}

Categories