sails beforeUpdate doesn't contains the object Id - node.js

I need to add some condition to beforeUpdate, sending to a specific event only if certain property was updated, and need to know its previous value.
I need the object Id to do that, but it is not appears in beforeUpdate values. How can I know the object id?
beforeUpdate(values, cb) {
// if another field was updated except of the lastActive field
if (values.length > 1) {
const id = this.update.arguments[0];
// id is undefined now. how can I get user id?
const oldUser = User.findOne({id: values.id});
// because I need to know user old values in sendUser function
myFunction.sendUser(oldUser);
}
cb();
}

Related

How to fill a reference field automatically in Wix (Velo), with or without code?

I am using a reference field, which contains users nickname, to make a connection between my main collection and 'public data' collection created as default by Wix. I created this reference field to populate a repeater from the two 'main' and 'public data' collections. Is it possible to automatically fill a reference field without using code? If not, then how can use 'beforeInsert' hook to fill the 'the reference' field using code.
I tried to do so in the backend by this code, but it doesn't work.
import { currentMember } from 'wix-members-backend';
export function Collection1_beforeInsert(item, context) {
currentMember.getMember()
.then((member) => {
const id = member._id;
const fullName = `${member.contactDetails.firstName} ${member.contactDetails.lastName}`;
const nickname = member.profile.nickname
console.log(nickname)
item.referenceField= "nickname"
// return member;
})
.catch((error) => {
console.error(error);
});
return item;
}
First off, I'm assuming you're using a regular reference field and not a multi reference field.
Second, it's important to understand how reference fields work before continuing. The reference field holds the ID of the item it is referencing. So when you say the reference field contains a user's nickname, that can't be true. It can contain the ID of the item in PrivateMembersData collection with the nickname you want.
Third, as just mentioned, the nickname field does not exist in the PublicData collection. It is part of the PrivateMembersData collection.
So, if you want to connect your collection to another with the nickname field, you need to set your reference field to reference the PrivateMembersData collection and then store the proper ID in that field.
(Side point: In your code you are putting the same string, "nickname", in every reference field value. You probably meant to use nickname without the quotes. You're also not using promises correctly.)
You can try this code. It should get you closer to what you're looking for.
import { currentMember } from 'wix-members-backend';
export async function Collection1_beforeInsert(item, context) {
const member = await currentMember.getMember();
item.referenceField = member._id;
return item;
}

Firestore add Document ID to Data via functions

I'm looking to add the Firestore ID to the DocumentData, so that I can easily utilize the ID when referring to rows in a table, without having to use document.data().property everytime I call a property of a document. Instead, I want to be able to call document.id.... document.property... and so on.
Is there an easy way to do this? Possibly with a Cloud Function that adds the auto-generated ID to the document data?
Thanks!
Example:
export const getSpaces = async () => {
const spaceDocs = await getDocs(spacesCollection)
spaceDocs.docs.forEach((spaceDoc) => {
const spaceID = spaceDoc.id
const spaceData = spaceDoc.data()
console.log(spaceID)
spaces.value.push(spaceData)
})
}
Now, the spaces array has objects containing the data of the documents. But, I loose the ability to reference the ID of a document.
Alternatively, I can add the entire document to the array, but following that, I'll have to access the properties by always including the data() in between. I.e. space.data().name
I'm certain, theres a better way
You don't need Cloud Functions to add the document ID to the data of that document. If you look at the third code snippet in the documentation on adding a document, you can see how to get the ID before writing the document.
In some cases, it can be useful to create a document reference with an auto-generated ID, then use the reference later. For this use case, you can call doc() [without any arguments]:
const newCityRef = db.collection('cities').doc(); // 👈 Generates a reference, but doesn't write yet
// Later...
const res = await newCityRef.set({
newCityRef.id, // 👈 Writes the document ID
// ...
});
As others have commented, you don't need to store the ID in the document. You can also add it to your data when you read the documents, with:
spaceDocs.docs.forEach((spaceDoc) => {
const spaceID = spaceDoc.id
const spaceData = spaceDoc.data()
console.log(spaceID)
spaces.value.push({ id: spaceID, ...spaceData })
})
With this change your spaces contains both the document ID and the data of each document.
A DocumentSnapshot has id property that you are looking for. If you add something within the document, then you'll need to access the data first with doc.data().field_name.
Working on another problem, I came across the solution. Quite simple actually:
When declaring the new Object, you can add ...doc.data() to add all the properties of the DocumentData to the newly created Object, after initialising it with an id. This works for me anyways. Case closed.
onSnapshot(profilesCollectionRef, (querySnapshot) => {
const profilesHolder = [];
querySnapshot.forEach((doc) => {
const profile = {
id: doc.id,
...doc.data(),
}
profilesHolder.push(profile);
console.log(profile);
});
profiles.value = profilesHolder;
});
onSnapshot(doc(db, "profiles", userId), (doc) => {
const newProfile = {
id: doc.id,
...doc.data(),
}
myProfile.value = newProfile as Profile;
});

Get field from subcollection's parent document

I am creating a message application with Firestore
At the moment i have created a function that gets triggered when a user writes a message in a messageRoom. Each messageRoom document has a subcollection called messages which contains the messages of the room. The messageRoom document itself contains data related to the messageRoom itself e.g. users who are part of the messageRoom. The messageRoom contains a field called members which holds info about the user who are a part of the messageRoom
I want to get access to the data in the messageRoom document and not the messages subcollection. At the moment the following code does not work. An error tells me that the members field is undefined. What have i done wrong?
exports.sendNotification = functions
.firestore
.document('messageRooms/{messageRoomId}/messages/{messageId}')
.onCreate(
async (snapshot) => {
/*
Does something with the documents in the subcollection
*/
//Get the DocumentReference of the parent doc
const parentDocId = snapshot.ref.parent.id;
admin.firestore().collection('messageRooms').doc(parentDocId).get('members').get().then(doc => {
if (doc.exists) {
console.log("Document data:", doc.data());
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
return; //return added to prevent crash
}).catch(error => {
console.log("Error getting document:", error);
});
}
);
I think the problems is here:
//Get the DocumentReference of the parent doc
const parentDocId = snapshot.ref.parent.id;
snapshot is DocumentSnapshot, so ref property give us DocumentReference. Property parent of document reference is CollectionReference (Google Doc). So you have to find not parent but "grandparent" :) I may say. Maybe try:
const parentDocId = snapshot.ref.parent.parent.id;
However I would not use .id property here just to get reference in .doc in next line. parent property already provides reference (doc). I don't think that argument in get is changing anything, so I would use .data().messages to get to the field. In the end I would use:
const parentDocRef = snapshot.ref.parent.parent;
parentDocRef.get().then(doc => { console.log(doc.data().members)})
I hope it will help!

Check if field exists, delete it and create a new one

I am trying to write a NodeJS program that on a given document, it checks if a field exists from a given list of fields - lets say the key we are looking for is key1. If the field exists it is deleted and a new field is added with the field incremented - key2 with some value.
// Get the `FieldValue` object
let FieldValue = require('firebase-admin').firestore.FieldValue;
// Create a document reference
let cityRef = db.collection('cities').doc('BJ');
// Remove the 'capital' field from the document
let removeCapital = cityRef.update({
capital: FieldValue.delete()
});
From the documentation I found the way to delete a field but I am not sure how to check if the key exists so the program knows what key to create after the deletion.
For the program the key might be any sequence of letters followed by a sequence of numbers - key1, key2, key3 etc, so I need a way to know which of those exists in order to correctly delete and then increment the new one
To know the list of fields for a document you need to fetch it with the get() method, see https://firebase.google.com/docs/firestore/query-data/get-data and https://firebase.google.com/docs/reference/js/firebase.firestore.DocumentReference#get
For example:
let cityRef = db.collection('cities').doc('BJ');
cityRef.get().then(function(doc) {
if (doc.exists) {
console.log("Document data:", doc.data());
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
}).catch(function(error) {
console.log("Error getting document:", error);
});
In the code above, doc is a DocumentSnapshot and if you call the data() method it will return "all fields in the document as an Object."
You just have to loop over the Object returned by the data() method to get the value X of the key(X) field, then increment it and write a new field key(X+1) with, for example, the update() method.
Note that, depending on your exact requirement, you may have to use a Transaction.

Mongoose.js conditional populate

I'm working with some old data where some of the schema has a "mixed" type.
Basically sometimes a value will be a referenced ObjectID, but other times it'll be some text (super poor design).
I unable to correctly populate this data because of the times a non-ObjectID appears.
So, for my actual question: Is it possible to create a populate (on a collection) that is conditional; I need to be able to tell the populate to skip those other values.
Yes, you can do that check the middleware function on the Mongoose API reference
http://mongoosejs.com/docs/middleware.html
What you need to do is before you populate those data, you validate the data if is is Object ID or not, if it is Object ID, you call next() to pass the next function, else you just return, this will skip it
Example
xSchema.pre('validate', function(next){
var x = this;
var checkXType = typeof x.id;
if (checkXType === String) {
return;
} else {
next();
}
});

Resources