Returning documents in Cloud Firestore - javascript

I'm absolutely new to Firestore and for some reason I can't get it right.
I'm trying to retrieve documents from it that I've just manually entered. Using node.js I make a call for a database snapshot like in the documentation:
db.collection('categories').get()
.then((docs) => {
res.json(docs)
})
But instead of an array of docs or something like this, I get this as a response:
_size is right, I have two documents in the collection - but I can't find them anywhere. What am I missing here?

Your docs variable is of type QuerySnapshot. Look at the methods available on that object. If you just want the raw documents out of it, use the docs property on it to get an array of QueryDocumentSnapshot objects. Each of those will have a method called data() on them to get the raw document data.

Related

Remove all fields associated linked to a document that I removed on another collection

For the context I'm using firebase/nodeJS on my application.
I'm deleting an user of a collection called workers. To do so I update the field deleted : true on the worker document.
I'm not deleting the document, because I want to keep their infos if needed.
But when I delete a worker, I must remove him from all the properties is working on (they have a field called workers, which is an array of all the workers associated.)
Hence, I must browse all the properties of the collection associated, check if the worker is on it and remove him.
Is there a way to do so ?
I saw this page : https://firebase.google.com/docs/firestore/manage-data/add-data#node.js_11
But don't know how to use theses ressources to do so.
So I finally did it alone.
First, I had to fetch all the properties where the work is working on, to do this I used array-contains.
let docsProperties = await db.collection('user').doc(id).collection("properties")
.where("workers", "array-contains", workerId).get();
Then I wanted to browse all theses properties and remove the worker. Thought about doing a ForEach first. But the problem is I wanted to use an await .update() with arrayRemove and async methods aren't usable inside ForEach.
So I used promises.
let promises = [];
docsProperties.forEach(doc => {
promises.push(doc.ref.update({
workers: admin.firestore.FieldValue.arrayRemove(workerId)
})); });
return Promise.all(promises);
arrayRemove is a method from the firebase documentation to remove all the occurences of an element on a following array field.
So I browsed all properties, added all promises and executed them!
worked perfectly, if anyone got questions don't hesitate.

Retrieve id of model created in $afterInsert hook in Objection ORM

Is it possible to retrieve (at least) the id of created model in $afterInsert hook in Objection ORM?
async $afterInsert(queryContext)
I guess and hope it could be available somehow from queryContext?
The goal is to be able to log this id when created in an allowed graph through insertGraph method.
You can access the returned data at this:
async $afterInsert(queryContext) {
await super.$afterInsert(queryContext);
console.log(this.id)
}

Using firebase .update within a .where (instead of .doc)

I am new to firebase and am struggling a little bit.
Currently, I am trying to update an array within a user, within a document. However, I cannot match the user to current user using the unique ID, as each users unique ID is their username, and it may have changed since creation.
I figured the best way to match the documents user to the current user would be to use a .where().get() and then use an "update()" to update the array.
Now, this is where I am getting stuck. In the firebase documents, their example of using .update is attached to a .doc
var washingtonRef = db.collection("cities").doc("DC");
//Atomically add a new region to the "regions" array field.
washingtonRef.update({
regions: firebase.firestore.FieldValue.arrayUnion("greater_virginia")
});
However, as I am using a .where, I assume I have to use references and snapshots. But, I am not quite sure how references work in this scenario and, with that, how to update properly.
Here is the code I have after a while of messing round, but no matter my variations i cannot figure it out. (essentially, I want to add a new project (in this case called "new project" to the users array of postedProjects.)
db.collection('users').where('user_id', '==', this.userInfo.user_id)
.get()
.then(function(querySnapshot) {
querySnapshot.forEach(function(doc) {
doc.data().update({
postedProjects: firebase.firestore.FieldValue.arrayUnion("new project")
})
})
})
This gives me an error of ".update() is not a function".
Is anyone able to help me with my solution to show me how references should properly be used in this scenario?
You're almost there. You can't update the data of DocumentSnapshot though, since that is the in-memory representation of the document data. Instead you need to get the DocumentReference and call update on that.
doc.ref.update({
postedProjects: firebase.firestore.FieldValue.arrayUnion("new project")
})
You need a DocumentReference in order to update() a document. Nothing else will work.
In your code, doc is a QueryDocumentSnapshot type object. If you want the DocumentReference object that refers to the document from that snapshot, use its ref property.
doc.ref.update({
postedProjects: firebase.firestore.FieldValue.arrayUnion("new project")
})

Why do we need a new object in bookshelf.js fetch() method?

I'm new to Node and using bookshelf.js as the ORM for my current project. The documentation of bookshelf.js contains this snippet under fetch() method:
// select * from `books` where `ISBN-13` = '9780440180296'
new Book({'ISBN-13': '9780440180296'})
.fetch()
.then(function(model) {
// outputs 'Slaughterhouse Five'
console.log(model.get('title'));
});
http://bookshelfjs.org/#Model-fetch
What confuses me is why do we need to create a new object here if we are querying for an existing record? Does the way bookshelf.js works requires a new object to be created for every returned result?
This is confusing. At the moment there is only one type of object that represents both "model" and "query builder".
fetch prefills the WHERE clause with the set attributes on the model.
There are plans underway to change this. Discussion here.
Instead of writing your code as above, I'd recommend doing something like this:
// select * from `books` where `ISBN-13` = '9780440180296'
Book.where({'ISBN-13': '9780440180296'})
.fetch()
.then(function(model) {
// outputs 'Slaughterhouse Five'
console.log(model.get('title'));
});
(I realize you've just copied this straight from the docs.)
If you wish to update a model in place, prefer to use the new Model#refresh:
Book.forge({id: 5}).refresh().then(function (book) {//...

Nodejs + mongodb : How to query $ref fields?

I'am using MongoDB with a nodejs REST service which exposes my data stored inside. I have a question about how to interrogate my data which uses $ref.
Here is a sample of an Object which contains a reference to another object (detail) in anther collection :
{
"_id" : ObjectId("5962c7b53b6a02100a000085"),
"Title" : "test",
"detail" : {
"$ref" : "ObjDetail",
"$id" : ObjectId("5270c7b11f6a02100a000001")
},
"foo" : bar
}
Actually, using Node.js and mongodb module, I do the following :
db.collection("Obj").findOne({"_id" : new ObjectID("5962c7b53b6a02100a000085"},
function(err, item) {
db.collection(item.$ref).findOne({"_id" : item.$id}, function(err,subItem){
...
});
});
In fact I make 2 queries, and get 2 objects. It's a kind of "lazy loading" (not exactly but almost)
My question is simple : is it possible to retrieve the whole object graph in one query ?
Thank you
No, you can't.
To resolve DBRefs, your application must perform additional queries to return the referenced documents. Many drivers have helper methods that form the query for the DBRef automatically. The drivers do not automatically resolve DBRefs into documents.
From the MongoDB docs http://docs.mongodb.org/manual/reference/database-references/.
Is it possible to fetch parent object along with it's $ref using single MongoDB query?
No, it's not possible.
Mongo have no inner support for refs, so it up to your application to populate them (see Brett's answer).
But is it possible to fetch parent object with all its ref's with a single node.js command?
Yes, it's possible. You can do it with Mongoose. It has build-in ref's population support. You'll need to change your data model a little bit to make it work, but it's pretty much what you're looking for. Of course, to do so Mongoose will make the same two MongoDB queries that you did.
Answer of Vladimir is not still valid as the db.dereference method was deleted from MongoDB Nodejs API:
https://www.mongodb.com/blog/post/introducing-nodejs-mongodb-20-driver
The db instance object has been simplified. We've removed the following methods:
db.dereference due to db references being deprecated in the server
No, very few drivers for MongoDb include special support for a DBRef. There are two reasons:
MongoDb doesn't have any special commands to make retrieval of referenced documents possible. So, drivers that do add support are artificially populating the resulting objects.
The more, "bare metal" the API, the less it makes sense. In fact, as. MongoDb collections are schema-less, if the NodeJs driver brought back the primary document with all references realized, if the code then saved the document without breaking the references, it would result in an embedded subdocument. Of course, that would be a mess.
Unless your field values vary, I wouldn't bother with a DBRef type and would instead just store the ObjectId directly. As you can see, a DBRef really offers no benefit except to require lots of duplicate disk space for each reference, as a richer object must stored along with its type information. Either way, you should consider the potentially unnecessary overhead of storing a string containing the referenced collection's documents.
Many developers and MongoDb, Inc. have added an object document mapping layer on top of the existing base drivers. One popular option for MongoDb and Nodejs is Mongoose. As the MongoDb server has no real awareness of referenced documents, the responsibility of the references moves to the client. As it's more common to consistently reference a particular collection from a given document, Mongoose makes it possible to define the reference as a Schema. Mongoose is not schema-less.
If you accept having and using a Schema is useful, then Mongoose is definitely worth looking at. It can efficiently fetch a batch of related documents (from a single collection) from a set of documents. It always is using the native driver, but it generally does operations extremely efficiently and takes some of the drudgery out of more complex application architectures.
I would strongly suggest you have a look at the populate method (here) to see what it's capable of doing.
Demo /* Demo would be a Mongoose Model that you've defined */
.findById(theObjectId)
.populate('detail')
.exec(function (err, doc) {
if (err) return handleError(err);
// do something with the single doc that was returned
})
If instead of findById, which always returns a single document, find were used, with populate, all returned documents' details property will be populated automatically. It's smart too that it would request the same referenced documents multiple times.
If you don't use Mongoose, I'd suggest you consider a caching layer to avoid doing client side reference joins when possible and use the $in query operator to batch as much as possible.
I reach the desired result with next example:
collection.find({}, function (err, cursor) {
cursor.toArray(function (err, docs) {
var count = docs.length - 1;
for (i in docs) {
(function (docs, i) {
db.dereference(docs[i].ref, function(err, doc) {
docs[i].ref = doc;
if (i == count) {
(function (docs) {
console.log(docs);
})(docs);
}
});
})(docs, i)
}
});
});
Not sure that it solution is best of the best, but It is simplest solution that i found.

Categories

Resources