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

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) {//...

Related

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")
})

Load includes on existing model

I'm trying to load includes on an existing model in sequelize. In express we pre check the models to see if they exist in the middleware.
So once we're in the actual "controller" we want to run some includes on that existing model that is passed in.
req.models.item.incude([
{model: Post, as: 'posts'}
])
Is there any way to accomplish this?
EDIT:
I know we can do something like this.
return req.models.item.getThing()
.then(function (thing) {
req.models.item.thing = thing;
return req.models.item;
});
But:
My expansions for includes are a dynamic property that come via url parameters, so they are not know ahead of time.
It I return the above you will not see the "thing" in the response. I need it nicely built as part of the original instance.
Something like a .with('thing', 'other.thing'); notation would be nice. Or in the case of sequelize .with({include: ...}); or .include([{model: ...}]);
If the variable req.models.item is already an Instance but without its other related instances ("includes"), then you could include them using something like the following code:
Item.findAll({
where: req.models.item.where(),
include: [{
model: SomeAssociateModel,
}]
})
.then(function(itemWithAssoc) {
// itemWithAssoc is an Instance for the same DB record as item, but with its associations
});
See here for some documentation. See here for a script demo'ing this.
Update: Given the instance, how do I just get the associated models?
To do this just use the automatically generated "getAssociation" getter functions, e.g.:
function find_associations_of_instance(instance) {
return instance.getDetails();
}
I've updated the script to include this as an example. For more information on these functions, see the SequelizeJS docs.

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.

avoiding persistence on Collection in Meteor

I've just started to look into using Meteor for an upcoming project, and have a question about data persistence. It sounds like you have two options: First, you can declare a "name" when instantiating a new Collection which will create a database collection which will be saved upon alteration.
Chatrooms = new Meteor.Collection("chatrooms");
The other option is to create an anonymous collection, which won't be saved.
Chatrooms = new Meteor.Collection();
But what do I do if I want to populate a Collection from the database, but not save it upon alteration on the client-side? For instance, I might want to create a collection of user Movies that will be displayed in a grid -- each having their own absolute positioning based upon the sorting and filtering applied to the collection. Upon changes to the collection, the associated views (or templates) will be re-rendered to reflect those changes. But I don't necessarily want these absolute positions to be stored in a database...
Any ideas?
I'm not very clear about your question. But perhaps, you can bind the absolute position into the collection data? They are just normal javascript objects. And the collection data will only be changed through insert/update/remove function call.
I ended up doing something like this:
movies: function() {
var movies = Movies.find().fetch();
_.each(movies, function(movie, index){
movie.left = index * 2;
movie.top = index * 2;
});
return movies;
},
Basically, 'fetch()' allows me to deal with pure JSON objects, making it easier to iterate through them and alter them without performing 'update' commands.

Implementing Backbone.Subset.js in Backbone.js to filter Models from a parent Collection

In this stackoverflow post i read about filtering backbone collections and using subsets.
One answer (by sled) recommends using backbone.subset.js (usage example).
I could not find any further resources on backbone.subset.js and I failed implementing it into my project.
It seems like backbone.subset.js is the perfect solution for what i'm trying to achieve.
(Having one "parent" collection that holds all models at all times, and depending on user input filtering the relevant models from the parent collection into a backbone.subset collection.)
My "parent" collection, holding all tasks:
var TasksAll = Backbone.Collection.extend({
url: '/tasks', // the REST url to retrieve collection data
model: Task // the models of which the collection consists of
});
var allTasks = new TasksAll();
Now i want to create a subset collection for e.g. tasks where task.status = 0:
var TasksTrash = new Backbone.Subset({
superset: allTasks,
filter: function(Task) {
return Task.isTrash();
}
});
var trashTasks = new TasksTrash();
Whereas inside the Task model, the method "isTrash" returns true if:
this.get('status') == 0
a) Are there any more resources on backbone.subset.js?
b) How do I implement above scenario?
c) Can I pass 'superset' and 'filter' options as params to the Backbone.Subset init function?
d) I looked into the backbone.subset.js code, when I 'reset' my parent Collection my subset Collections should be updated straight away, right?
PS: I'm fairly new to Backbone. Thanks for your help.
Looking at the source for backbone-subset, it looks as though there is a pre-initialization hook which you could utilize in order to make the 'sieve' or filter available as an option or argument:
https://github.com/masylum/Backbone.Subset/blob/master/backbone.subset.js#L50
As for providing parent as an argument, there is an outstanding patch to add that exact functionality:
https://github.com/masylum/Backbone.Subset/pull/5
With it, you can pass in parent as an option, if it is not an option the library will fall back to looking for it on the object Prototype

Categories

Resources