How do you unit test JOIN queries in Sequelize? - node.js

I am using Sequelize to access a database from NodeJS. The database has a few tables with relationships, and I have a query that joins tables i.e. something like this
const result = await modelA.findAll({
...
include: [
{
model: modelB,
...
}
]
})
I have been struggling to find a good way to test this.
I have looked at
sequelize-mock
sequelize-test-helpers
None of which mention anything about testing associated Sequelize models.
I have tried to create mock models with sequelize-mock, and re-define the relationships between the models, but that also did not work so I am not sure if it is supported (by did not work I mean the above query I made did not actually do a join with the two provided mock models, despite the attempted association definition between them).
Alternatively, I could just mock the return value of findAll(, but I am aware that that return object is Sequelize implementation specific so it would be ideal if I could avoid constructing that object myself. Also I feel like that defeats the purpose of having the above mocking libraries as well.
Any one know a good way to approach this?

Related

Can we create a joint table in TypeORM for tables from different databases?

I am trying to create a joint table for two tables, that are from different PostgreSQL databases. Working with TypeORM, I have a problem defining the #ManyToMany(() => 'TableFromAnotherDb') in TypeScript. I've created an interface that has the needed property for the joint table, but having the interface in mind - it's unuseful when it's assigned in the ManyToMany part, because it refers to a type, and I am trying to use it as a value.
Also, does having two simultaneous database connections is necessary here? Because I am trying to mask the interface for the table needed from the second database.
Any recommendation for avoiding this problem while keeping my typescript compiler happy?
I highly doubt that TypeORM allows for linking between databases like this. The problem is that most of the relations auto-generate SQL queries that pull in the various data base tables it needs to operate on. If you have two databases, one SQL queries can't get all the info it needs. So your application needs to be the glue that binds them together.
I think the best you can get is store the ID in the entity, and then manually query each connection.
#Entity()
class Thing {
#Column()
otherThingId: number
}
// usage
const thing = await ThingRepository.find(123)
const otherThing = await OtherThingRepository.find(thing.otherThingId)

Sequelize: what's the point of models?

I'm using Sequelize as my ORM, and just wondering what the point of having a model is.
It looks like the main thing that matters, is the table definitions in your migrations, and models are just a static snapshot of what your tables look like. When you perform a migration, nothing changes in your models. It doesn't get updated, nor created/deleted based on your migration.
You have to manually keep your models up to date it looks like.
So is there any point in having models, or making the effort to keep them updated?
The models are the definition of your database schema so that it can map into the ORM that Sequelize provides. For me this is the most important feature of Sequelize, not the migrations.
Migrations are used for changing the database schema.
Models are used to map the database schema to your code.
Using Models gives you lots of built in helper methods, associations let you build references between tables to generate complex JOINs, etc.

Difference between HasOne and BelongsTo in Sequelize ORM

I am developing a sails.js app with sequelize ORM. I am a little confused as to when BelongsTo and HasOne need to be used.
The documentation states that :
BelongsTo associations are associations where the foreign key for the
one-to-one relation exists on the source model.
HasOne associations are associations where the foreign key for the
one-to-one relation exists on the target model.
Is there any other difference apart from the the place where these are specified? Does the behavior still continue to be the same in either cases?
This is more universal problem.
The main difference is in semantic. you have to decide what is the relationship (Some silly example):
Man has only one right arm. Right arm belongs to one man.
Saying it inversely looks a little weird:
Right arm has a man. A man belongs to right arm.
You can have man without right arm. But alone right arm is useless.
In sequelize if RightArm and Man are models, it may looks like:
Man.hasOne(RightArm); // ManId in RigthArm
RightArm.belongsTo(Man); // ManId in RigthArm
And as you notice there is also difference in db table structure:
BelongsTo will add the foreignKey on the source where hasOne will add on the target (Sequelize creates new column 'ManId' in table 'RightArm' , but doesn't create 'RightArmId' column in 'Man' table).
I don't see any more differences.
I agree with Krzysztof Sztompka about the difference between:
Man.hasOne(RightArm);
RightArm.belongsTo(Man);
I'd like to answer Yangjun Wang's question:
So in this case, should I use either Man.hasOne(RightArm); or
RightArm.belongsTo(Man);? Or use them both?
It is true that the Man.hasOne(RightArm); relation and the RightArm.belongsTo(Man); one do the same thing - each of these relations will add the foreign key manId to the RightArm table.
From the perspective of the physical database layer, these methods do the same thing, and it makes no difference for our database which exact method we will use.
So, what's the difference? The main difference lays on the ORM's layer (in our case it is Sequalize ORM, but the logic below applies to Laravel's Eloquent ORM or even to Ruby's Active Record ORM).
Using the Man.hasOne(RightArm); relation, we will be able to populate the man's RightArm using the Man model. If this is enough for our application, we can stop with it and do not add the RightArm.belongsTo(Man); relation to the RightArm model.
But what if we need to get the RightArm's owner? We won't be able to do this using the RightArm model without defining the RightArm.belongsTo(Man); relation on the RightArm model.
One more example will be the User and the Phone models. Defining the User.hasOne(Phone) relation, we will be able to populate our User's Phone. Without defining the Phone.belongsTo(User) relation, we won't be able to populate our Phone's owner (e.g. our User). If we define the Phone.belongsTo(User) relation, we will be able to get our Phone's owner.
So, here we have the main difference: if we want to be able to populate data from both models, we need to define the relations (hasOne and belongsTo) on both of them. If it is enough for us to get only, for example, User's Phone, but not Phone's User, we can define only User.hasOne(Phone) relation on the User model.
The logic above applies to all the ORMs that have hasOne and belongsTo relations.
I hope this clarifies your understanding.
I know this is a 4-years late answer, but I've been thinking of it, searching the docs, and googling since yesterday. And couldn't find an answer that convinced me about what was happening. Today I've got to a conclusion: the difference is not just a matter of semantics, definitely!
Let's suppose you have the following statement (from the docs):
Project.hasMany(Task);
It creates, in Project model, some utility methods on the instances of Project, like: addTask, setTask etc. So you could do something like:
const project = await Project.create({...});
// Here, addTask exists in project instance as a
// consequence of Project.hasMany(Task); statement
project.addTasks([task1, task2]);
Also, in the database, a foreign key in tasks relation would've been created, pointing to projects relation.
Now if, instead of Project.hasMany(Task);, I had stated only:
Task.belongsTo(Project);
Then, similarly, in the database, foreign keys in tasks relation would've been created, pointing to projects relation. But there wouldn't be any addTasks method on project instances though. But, by doing Task.belongsTo(Project);, Sequelize would create a different set of methods, but only on task instances this time. After doing that, you could associate a task to a project using, for example:
const proj = await Project.findByPk(...);
const task1 = await Task.create({...});
...
// Here, setProject exists in task instance as a
// consequence of Task.belongsTo(Project); statement
task1.setProject(proj);
The docs defines as source, the model that owns the method used to create the association. So, in:
Project.hasMany(Task);: In this statement, Project is the source model. Task is, in turn, the target model.
Task.belongsTo(Project);: In this statement, Task is the source model. Project is, in turn, the target model.
The thing is that, when creating associations using hasOne, hasMany, belongsTo, and belongsToMany, the instances utility methods are created only on the source model. In summary: if you want to have the utility methods created both in Project and Task instances, you must use the two statements for describing the same the association. In the database itself, both will have the same redundant effect (creating a foreign key on tasks relation pointing to projects relation's primary key):
// All the instances of Project model will have utility methods
Project.hasMany(Task);
// All the instances of Task model will have utility methods
Task.belongsTo(Project);
const project = await Project.create(...);
const task1 = await Task.create(...);
const task2 = await Task.create(...);
...
// as a consequence of Project.hasMany(Task), this can be done:
project.addTask(task1);
...
// as a consequence of Task.belongsTo(Project), this can be done:
task2.setProject(project);
BTW, after writing this answer, I realized that this is the same thing that Vladsyslav Turak is explaining in his answer, but I decided to keep my answer here because it adds some important practical information involving the utility methods stuff.
One-to-One belongTo or hasOne
Using the right arm example, and Sequelize's own documentation. The question we must ask is, can a man survive without a right arm? Or can a right arm survive without a man? To determine where we want our foreign key to exist is to answer this question. Let's take a more practical example.
Let's say you have a community website. Your users are all represented by a singular Profile model (or User model). But in a community you will also have administrators and moderators, both with their own sets of rights, and maybe even a different kind of profile. Instead of adding admin/mod specific fields to the User model, it might be best to create a separate model to represent an admin/mod.
Here's what basic user model looks like (ignoring constraints and validations):
class User extends Model {
static associate(models) {}
}
User.init(
{
username: DataTypes.STRING(25),
password: DataTypes.STRING(50)
}
)
Now here's a model that represents an admin or mod, which is intended to extend the user model:
class Staff extends Model {
static associate(models) {}
{
Staff.init(
{
permissions: DataTypes.ARRAY(DataTypes.STRING),
roleType: DataTypes.STRING(20),
}
)
So we ask our selves, can a user exist without admin/mod? Can an admin/mod exist without a user? A user doesn't have to be staff to use your services, but an admin/mod still needs a username and password in order to login. You could add those fields to the Staff model, but the truth is, it would be repeating information and make things harder to keep track of.
At the heart, an admin/mod would have the same attributes as a normal user, just with special abilities. If you intend otherwise, I'd still maintain a BaseUser model to organize and keep what each model has in common together. An admin/mod account would still have a username and password, and likely an email as well. Otherwise, you'd end up having two users with the same info, and in a community that can be confusing, and difficult to manage.
It is determined that a user does not need a Staff object associated with it to exist, so we shouldn't put the foreign key on the user profile. This still doesn't quite answer our question though. Remember, hasOne() puts the FK on the target model, while belongsTo() places the FK on the source. So we could say that Staff.belongsTo(User) or User.hasOne(Staff) that meets the requirement of the FK has to exist on the Staff model.
Whether you put a belongsTo() on the Staff model, or a hasOne() on the User model is a matter of semantics, and doesn't really matter. Either will associate the Staff model with the User model, allowing you to perform the User.getStaff() method. If you want to be able to get user account from a Staff instance, you could add a reference column without creating an actual association like so on our Staff model (this doesn't add constraints or associations, merely as it implies, a reference):
user: {
type: DataTypes.INTEGER,
references: {
model: User,
key: 'userId'
}
}
I hope this helps.

Wrapping multiple similar mongoose schemas into a single schema

I have a server which stores records representing Objects, and which uses Mongoose to manage these records. I want to be able to query/update/etc. all objects with a simple API (i.e. a single endpoint). Different types of Objects have some identical attributes, and some different attributes, so a single, static Object schema won't do. Instead, I still want to have a single schema, but I want to be able to change it slightly by adding/deleting fields when I create each new Object, with the fields which are/aren't present depending on the type of the Object. I don't want a mixed schema, because I want error validation for each type of Object. I want a single schema (as opposed to a different schema for each type of Object) so that I can just do
Object = mongoose.model('Object', ObjectSchema);
Object.findOne({objectType: "type1"}, function(err, model) {
...
});
So basically, I want field validation, while still maintaining some flexibility for attributes, and a single point to query/update/etc. my Object records. If I change the schema with each new Object, recompile it into a model, and create a new instance of that model, will all the instances of the different models (compiled from different modified versions of the same schema) still be queryable as above?
Obviously, I'm new to Mongoose. I just talked a lot about the schema here, and I honestly don't know whether I should have used the word "model" in place of "schema" in some places. I just don't know how I can accomplish all of this. Let me know if I make no sense.
We are successfully using the mongoose model inheritance and discriminator functionality for a very similar scenario. See here for an example:
http://www.laplacesdemon.com/2014/02/19/model-inheritance-node-js-mongoose/
You might also be able to use this plugin:
https://www.npmjs.com/package/mongoose-schema-extend

Mongoose: Schema vs Model?

When looking at tutorials there is often a delineation between a schema and a model, particularly when dealing with mongoose/mongodb.
This makes porting over to postgresql somewhat confusing, as 'models' don't seem to exist under that system. What is the difference the two approaches?
For example, what would be a postgres/sql ORM equivalent of this line?
(mongoose and express.js):
var userSchema = schema.define('local', {
username: String,
password: String,
});
module.exports = mongoose.model('User', userSchema);
In mongoose, a schema represents the structure of a particular document, either completely or just a portion of the document. It's a way to express expected properties and values as well as constraints and indexes. A model defines a programming interface for interacting with the database (read, insert, update, etc). So a schema answers "what will the data in this collection look like?" and a model provides functionality like "Are there any records matching this query?" or "Add a new document to the collection".
In straight RDBMS, the schema is implemented by DDL statements (create table, alter table, etc), whereas there's no direct concept of a model, just SQL statements that can do highly flexible queries (select statements) as well as basic insert, update, delete operations.
Another way to think of it is the nature of SQL allows you to define a "model" for each query by selecting only particular fields as well as joining records from related tables together.
In other ORM systems like Ruby on Rails, the schema is defined via ActiveRecord mechanisms and the model is the extra methods your Model subclass adds that define additional business logic.
A schema is fundamentally describing the data construct of a
document (in MongoDB collection). This schema defines the name of each item of data, and the type of data, whether it is a string, number, date, Boolean, and so on.
A model is a compiled version of the schema. One instance of the model will map to one document in the database.
It is the model that handles the reading, creating, updating, and deleting of documents.
A document in a Mongoose collection is a single instance of a model. So it makes sense that if we're going to work with our data then it will be through the model.
A single instance of a model (like a User instance in var User = mongoose.model('User', userSchema);) maps directly to a single document in the database.
With this 1:1 relationship, it is the model that handles all document interaction - creating, reading, saving, and deleting. This makes the model a very powerful tool.
Taken from "Mongoose for Application Development", by Simon Holmes, 2013
I imagine models as classes created from a schema (maybe I am mistaken).
MongoDB stores everything in BSON , which is a binary format. A simple Hello World BSON document might look like this internally:
\x16\x00\x00\x00\x02hello\x00\x06\x00\x00\x00world\x00\x00. A computer can deal with all that mumbo-jumbo, but that's hard to read for humans. We want something we can easily understand, which is why developers have created the concept of a database model. A model is a representation of a database record as a nice object in your programming language of choice. In this case, our models will be JavaScript objects. Models can serve as simple objects that store database values, but they often have things like data validation, extra methods, and more. As you’ll see, Mongoose has a lot
of those features.
Taken from "Express in Action", by Evan Hahn, 2016
In Short:
A Mongoose model is a wrapper on the Mongoose schema. A Mongoose schema defines the structure of the document, default values, validators, etc., whereas a Mongoose model provides an interface to the database for creating, querying, updating, deleting records, etc.
Reference: Introduction to Mongoose for MongoDB - FCC

Resources