How to push data into a nested array in firestore? - javascript

How can i do this?
I have a data stucture for each user in a user collection, I have created my own schema for the user object stored in the collection.
I want to have a favoriteMovies collection initalised into this object so that i can add movie objects inside.
Heres an example of what im trying to do :
async function signUp(email, password){
const {user} = await auth.createUserWithEmailAndPassword(email, password)
// initialise the schema for the data
const userInitialised = await db.collection('users').doc(user.uid).set(userSchemaObject(user.uid))
return userInitialised;
}
Then the schema so far:
export default function userSchemaObject(uuid){
return {
uuid,
favmovies:[]
}
}

I'm asking how to push data to a nested array
From your question and comment, your favmovies field should be of type Array and does not seem to be "nested". Therefore you should simply use arrayUnion() in order to push data to this Array.
The following code demonstrates how to create a doc with an empty Array field, then, later, how to push a value.
const db = firebase.firestore();
const newDocRef = db.collection('test').doc();
newDocRef
.set({
uuid: 'uuid_value',
favmovies: [],
})
.then(() => {
newDocRef.update({
favmovies: firebase.firestore.FieldValue.arrayUnion('movie1'),
});
});
Note that you can pass several elements to the arrayUnion() method.
newDocRef.update({
favmovies: firebase.firestore.FieldValue.arrayUnion(
'movie1',
'movie2'
),
});

Related

MongoDB updateOne not able to update document when referring an ID as filter

I'm trying to update a property value of a single document by sending a request to my NextJs API via fetch.
// Update items in state when the pending time in queue has passed, set allowed: true
items.map((item) => {
const itemDate = new Date(item.added)
const timeDiff = currentDate.getTime() - itemDate.getTime()
const dateDiff = timeDiff / (1000 * 3600 * 24)
if (dateDiff >= 7) {
const updateItem = async () => {
try {
// Data to update
const updatedItem = {
_id: item._id,
name: item.name,
price: item.price,
allowed: true
}
console.log(updatedItem)
await fetch ('/api/impulses', {
method: 'PATCH',
body: JSON.stringify(updatedItem)
})
} catch (error) {
alert('Failed to update items status')
}
}
updateItem()
}
})
API receives the data object as a whole and I am able to parse every piece of data I need for updating the MongoDb document from the req.body. However, when trying to use the item's _id (which is generated by MongoDb and values as _id: ObjectId('xx1234567890xx')) to filter the document I need to update, it seems to treat the ID differently.
I've tried to mess around with the format, forcing the object that gets sent to the API to include just the things I want to be updating (and the _id, of course) but still...
const jsonData = JSON.parse(req.body)
const { _id } = jsonData
// Fields to update
const { name, price, allowed } = jsonData
const data = {
name,
price,
allowed
}
const filter = {
_id: _id
}
const update = {
$set: data
}
const options = {
upsert: false
}
console.log("_id: ", filter) // returns { _id: 'the-correct-id-of-this-document' }
And finally, updating thedb.collection and returning responses:
await db.collection('impulses').updateOne(filter, update, options)
return res.json({
message: 'Impulse updated successfully',
success: true
})
So, as the _id clearly matches the document's id, I cannot figure out why it doesn't get updated? To confirm that this isn't an issue with database user privileges, if I set upsert: true in options, this creates a new document with the same _id and the updated data the request included...
Only difference to the other documents created through a submit form is that the id is in the following format: _id: 'xx1234567890xx' - so, comparing that to an ID with the ObjectId on front, it doesn't cause a conflict but I really don't get this behavior... After noticing this, I've also tried to include the 'ObjectId' in the ID reference in various ways, but it seemed like initiating a new ObjectId did exactly that - generate a new object ID which no longer referenced the original document.
Any ideas?
You compare an ObjectId object from _id with a string, this does not work.
Create proper filter object, e.g.
const filter = { _id: ObjectId(_id) }
or the other way around:
const filter = { $expr: {$eq: [{$toString: "$_id"}, _id] } }
but this will prevent to use the index on _id, so the first solution should be preferred.

Javascript Access an an Object in an Array

I'm fetching data from MongoDB, and the response is coming through fine, however it appears to be wrapped in array when it comes out of the User.find() function.
For example, one response is:
[{"_id":"62fe3c888e2776ef3c1a010f","username":"Drago D Trial","password":"U2FsdGVkX1867hs26KL0KitTGhWnP9tdVX6AcmI5pWE=","fullname":"Drago DaTrial","firstname":"","surname":"","email":"drago#hotmail.com","position":"QA Tester","userImage":"","locationCity":"","country":"","role":"","company":"","emailAuthorised":true,"professionalBio":"","positionRecentTitle":"","positionRecentCompany":"","companyAuthorised":"","isAdmin":false,"createdAt":"2022-08-18T13:20:08.045Z","updatedAt":"2022-08-18T13:21:02.619Z","__v":0}]
I'm accessing this through an api like this:
router.get('/inviteToJoinTeam/:token/:email', async (req, res) => {
try {
//verify the token against DB
const userToken = (req.params.token)
const indivEmailAdd = (req.params.email)
// creating user auth
try{
const userDetails = await User.find({email: indivEmailAdd})
const indivIDAdd = await userDetails (!want to access the data here and just get ID)
res.send(indivIDAdd)
}catch{
console.log('failure')
}
} catch (e) {
res.send('This isnt working');
}
});
How would you access this and just get the _id field out?
If there is only one item in the array then - simply get the id property of the first item intthe returned array
const indivIDAdd = await userDetails[0]['_id'];
or using dot notation
const indivIDAdd = await userDetails[0]._id;
if there are multiple results then map over the results and get the id from each
const ids = await userDetails.map(user => user._id);
just use response[0]._id
Ps: Response is the array coming from the database
Try projection for the same it should work
const userDetails = await User.find({ email: indivEmailAdd }, { _id : 1 })
it will return array of ObjectId. if you need to get only one object then use findOne instead of find.
According to me you have 2 solutions :
Option 1 use findOne instead of find :
const userDetails = await User.findOne({email: indivEmailAdd});
Option 2 access array / object with basic js:
const usersDetails = await User.find({email: indivEmailAdd});
const userDetails = usersDetails.at(0)._id; // or
const userDetails = usersDetails[0]['_id'];

Mongoose keep duplicate elements

I have a function that create guild entry for DiscordJS, but when the script start and also if the function is called multiple times, it create around 400 duplicate documents, it create by ID and the ID is unique, so it's not normal
My schema structure only have a ID type String and unique is true
client.createGuild = async guild => {
const exist = await Guild.findOne({ id: guild.id });
if(!exist) {
await Guild.create({ id: guild.id }); // new Guild().save() keep duplicate too
}
}
It look like the if statement doesn't exist
const Schema = mongoose.Schema;
const FooSchema = new Schema({
id: { type: String, index: true, unique: true }
});
const Foo = mongoose.model('Foo', FooSchema);
Foo.createIndexes();
If collection already exists. Create index manually to the collection via atlas or cmd.
You can combine getData and createData functions to one. Here is the example:
const mongoose = require('mongoose');
async function getData(Guild, guild) {
if (!mongoose.connection.readyState) await mongoose.connect('MONGO_URL'); // In case you haven't connect to database
const data = await Guild.findOne({ id: guild.id }); // get data from database
if (!data) {
return new Guild({
id: guild.id,
}); // If no data exists for the guild, return new model
}
return data; // If the data already exists, return that
}
Now if you want to get data from mongodb you just call the function. It automatically create and save a new one if there is not.
Comment if you still have any problem or you have got what you need.
Make sure to call the function with await or it won't return the data.

Firebase: how to receive/add info to separate collection via JS

yeap, if read a title you can think that it is simple action via collection - but no -> in you we have a button "Add collection": not clear how to add this collection via code or how to receive these created collections.
Please take a look at the structure of BD in the attachment:
.
I can receive ID of a document, but not clear how to receive collection(s)/data of these collections. here i just receive the main ID, not "1" collection and his data:
const listCollections = [];
await db
.collection(table)
.get()
.then((snapshot) => {
snapshot.docs.forEach((doc) => {
debugger
const dataCollectionObject = doc.data();
dataCollectionObject.id = doc.id;
listCollections.push(dataCollectionObject);
});
});
When you receive a document, you need another query to inspect its sub-collections.
Also, I suggest not mixing async/await with then.
A basic approach
const rootSnapshot = await db.collection(table).get();
const promises = rootSnapshot.docs.map(rootDoc => rootDoc.ref.collection("1").get());
const childrenDoc = await Promise.all(promises);
Using collection group queries
see the doc
const childrenDoc = await db.collectionGroup("1").get();
If you do not know the sub-collection name
Bad luck! With Firestore you are required to know the name of your collections. You could for exemple store it in another data member:
document {
someField: ...
collectionIds: ["1", "2"] // store the sub-collection ids
"1" // a sub-collection
"2" // another sub-collection
}
Then when you retrieve such a document, inspect its data and loop on collectionIds to query deeper.

Get fields from nested DocumentReference item in Firestore

I'm in the process of writing an API using firebase functions: api is written in javascript.
In my firestore db, I have a user document that contains some nested fields. For example, my user doc has a field that looks roughly like this:
profile
education
education1 --> degree: doc ref, date: timestamp, school: doc ref
education2 --> degree: doc ref, date: timestamp, school: doc ref
I cannot for the life of me access the degree object and get the properties out of it. Each user could have multiple education entries (for example, people who hold multiple degrees). I can't step into those education# maps and access the fields inside the document they are referring to.
You didn't give a lot of details on your exact data model: which collection, which document, etc...
However, since in your comment above you say that "profile is a map, education is a map that lives inside of profile, and education items are also maps that live inside of education" the following should do the trick
var docRef = firestore.collection('collectionId').doc('docID');
docRef
.get()
.then(doc => {
if (doc.exists) {
const educationObj = doc.data().profile.education;
const promises = [];
Object.keys(educationObj).forEach(key => {
promises.push(firestore.doc(educationObj[key].degree.path).get());
});
return Promise.all(promises);
} else {
// doc.data() will be undefined in this case
console.log('No such document!');
throw new Error('no doc');
}
})
.then(results => {
results.forEach(r => {
console.log(r.data());
});
})
.catch(error => {
console.log('Error getting document:', error);
});
The degree property contains a DocumentReference, therefore you need use the path property in order to get the corresponding document.

Categories