usage of classMethods vs instanceMethods in sequilizejs? - node.js

I am new to sequilizejs and basically am trying to refactor code that i've written in the controller and came across classMethods and instanceMethods. I see instance methods defined like so:
/lib/model/db/users.js
module.exports = function(sequelize, DataTypes) {
var instance_methods = get_instance_methods(sequelize);
var User = sequelize.define("User", {
email : {
type : DataTypes.STRING,
allowNull : false
},
}, {
classMethods: class_methods,
instanceMethods : instance_methods,
});
return User;
};
function get_instance_methods(sequelize) {
return {
is_my_password : function( password ) {
return sequelize.models.User.hashify_password( password ) === this.password;
},
};
function get_class_methods(sequelize) {
return {
hashify_password : function( password ) {
return crypto
.createHash('md5')
.update(
password + config.get('crypto_secret'),
(config.get('crypto_hash_encoding') || 'binary')
)
.digest('hex');
},
};
My understanding of the above is that classMethods are generic functions defined for the whole model and instanceMethods are basically a reference to a given row in a table/model, am i right in assuming this ? this would be my primary question.
Also i don't see any reference of classMethods and instanceMethods in the docs HERE. I only found this previous answer HERE. That provides a somewhat comprehensive understanding of the difference between instanceMethods and classMethods.
Basically i'am just trying to confirm weather my understanding matches the intended usage for class vs instance methods and also links to the official docs for the same would be highly appreciated.

The official way to add both static and instance methods is using classes like this:
class User extends Model {
static classLevelMethod() {
return 'foo';
}
instanceLevelMethod() {
return 'bar';
}
getFullname() {
return [this.firstname, this.lastname].join(' ');
}
}
User.init({
firstname: Sequelize.TEXT,
lastname: Sequelize.TEXT
}, { sequelize });
See Models as classes

Your understand is correct. In short: classes can have instances. Models are classes. So, Models can have instances. When working with an instance method, you will notice the this — which is the context, which refers to that particular class/model instance.
Hence, if you have a User model that has:
an instance method called is_my_password
a class model called hashify_password
User.hashify_password('123') will return the hashed version of 123. The User instance is not needed here. hashify_password is general function attached to the User model (class).
Now, if you'd like to call is_my_password() you do need a User instance:
User.findOne({...}).then(function (user) {
if (user.is_my_password('123')) {
// ^ Here we call `is_my_password` as a method of the user instance.
...
}
}).catch(console.error)
In general, when you have functions that do not need the particular model instance data, you will define them as class methods. They are static methods.
And when the function works with the instance data, you define it as instance method to make it easier and nicer to call.

Related

How to define a manytomany relationship on the same table in objectionjs?

I have an article table, where an article can cite multiple articles. Each article can also have a single author.
Article model :
class Article extends DefaultModel {
static get tableName() {
return "articles";
}
static get relationMappings() {
return {
author: {
relation: DefaultModel.BelongsToOneRelation,
modelClass: "Author",
join: {
from: "articles.authorId",
to: "authors.id"
}
},
mentions: {
relation: DefaultModel.ManyToManyRelation,
modelClass: "Article",
join: {
from: "articles.id",
through: {
from: "mentions.citingArticle",
to: "mentions.citedArticle"
},
to: "articles.id"
}
}
};
}
}
Mention model :
class Mention extends DefaultModel {
static get tableName() {
return "mentions";
}
static get idColumn() {
return ["citingArticle", "citedArticle"];
}
}
What I'm trying to do, is insertGraph, the main article + the articles that are mentioned in it, here is what I have :
async function insertData(fullArticle) {
const articleTx = await transaction(Article.knex(), async tx => {
const references = fullArticle.references
.map(articleObj => {
return {
...articleObj.article,
author: articleObj.author
};
});
const article = await Article.query(tx).insertGraph({
...fullArticle.article,
author: fullArticle.author,
mentions: references[0]
});
return article;
});
console.log(articleTx);
}
I know this is only inserting the main row + the first row, but I ran into multiple problems :
The mention model has a composite key, when I try to insert using the code above I get a (node:5944) UnhandledPromiseRejectionWarning: error: column "citedArticle" of relation "mentions" does not exist.
I also got the same error when I try to add "#id" to the main article mentions relation, and "#ref" to the cited article, and insert them in 2 different objects.
A third problem I ran into here, was a unique constraint on both the citing and the cited article authors. What if both of them were written by the same author? I have a unique string column in the authors table that isn't the id, when I tried to insert I got duplicate key value violates unique constraint "author_isn". I'm not sure how I can update the row to reference the existing author id if that happens.

Node.js Testing with Mongoose. unique gets ignored

I'm having a little trouble with an integration test for my mongoose application. The problem is, that my unique setting gets constantly ignored. The Schema looks more or less like this (so no fancy stuff in there)
const RealmSchema:Schema = new mongoose.Schema({
Title : {
type : String,
required : true,
unique : true
},
SchemaVersion : {
type : String,
default : SchemaVersion,
enum: [ SchemaVersion ]
}
}, {
timestamps : {
createdAt : "Created",
updatedAt : "Updated"
}
});
It looks like basically all the rules set in the schema are beeing ignored. I can pass in a Number/Boolean where string was required. The only thing that is working is fields that have not been declared in the schema won't be saved to the db
First probable cause:
I have the feeling, that it might have to do with the way I test. I have multiple integration tests. After each one my database gets dropped (so I have the same condition for every test and precondition the database in that test).
Is is possible that the reason is my indices beeing droped with the database and not beeing reinitiated when the next text creates database and collection again? And if this is the case, how could I make sure that after every test I get an empty database that still respects all my schema settings?
Second probable cause:
I'm using TypeScript in this project. Maybe there is something wrong in defining the Schema and the Model. This is what i do.
1. Create the Schema (code from above)
2. Create an Interface for the model (where IRealmM extends the Interface for the use in mongoose)
import { SpecificAttributeSelect } from "../classes/class.specificAttribute.Select";
import { SpecificAttributeText } from "../classes/class.specificAttribute.Text";
import { Document } from "mongoose";
interface IRealm{
Title : String;
Attributes : (SpecificAttributeSelect | SpecificAttributeText)[];
}
interface IRealmM extends IRealm, Document {
}
export { IRealm, IRealmM }
3. Create the model
import { RealmSchema } from '../schemas/schema.Realm';
import { Model } from 'mongoose';
import { IRealmM } from '../interfaces/interface.realm';
// Apply Authentication Plugin and create Model
const RealmModel:Model<IRealmM> = mongoose.model('realm', RealmSchema);
// Export the Model
export { RealmModel }
Unique options is not a validator. Check out this link from Mongoose docs.
OK i finally figured it out. The key issue is described here
Mongoose Unique index not working!
Solstice333 states in his answer that ensureIndex is deprecated (a warning I have been getting for some time now, I thought it was still working though)
After adding .createIndexes() to the model leaving me with the following code it works (at least as far as I'm not testing. More on that after the code)
// Apply Authentication Plugin and create Model
const RealmModel:Model<IRealmM> = mongoose.model('realm', RealmSchema);
RealmModel.createIndexes();
Now the problem with this will be that the indexes are beeing set when you're connection is first established, but not if you drop the database in your process (which at least for me occurs after every integration test)
So in my tests the resetDatabase function will look like this to make sure all the indexes are set
const resetDatabase = done => {
if(mongoose.connection.readyState === 1){
mongoose.connection.db.dropDatabase( async () => {
await resetIndexes(mongoose.models);
done();
});
} else {
mongoose.connection.once('open', () => {
mongoose.connection.db.dropDatabase( async () => {
await resetIndexes(mongoose.models);
done();
});
});
}
};
const resetIndexes = async (Models:Object) => {
let indexesReset: any[] = [];
for(let key in Models){
indexesReset.push(Models[key].createIndexes());
}
Promise.all(indexesReset).then( () => {
return true;
});
}

SequelizeEagerLoadingError when relationship between models has already been defined

I have an exports file that includes all the sequelize-models and then defines the relationship among the models. It looks something like:
// Snippet from the global init file
for (let modelFile of modelFileList) {
// ... Some code ...
// Require the file
appliedModels[modelName] = require(`../${MODEL_DIR}/${modelFile}`).call(null, _mysql);
}
//Define the relationship between the sql models
_defineRelationship(appliedModels);
function _defineRelationship(models) {
models._planAllocationModel.belongsTo(models._subscriptionModel, {
foreignKey: 'subscription_id',
targetKey: 'subscription_id'
});
}
But when I try to include the model like:
_subscriptionModel.findAll({
where: {
start_date: {
_lte: today // Get all subscriptions where start_date <= today
}
},
limit,
include: [
{
model: _planAllocationModel
}
]
});
There is an error thrown by sequelize: SequelizeEagerLoadingError: tbl_plan_allocation is not associated to tbl_subscription_info! What could be the reason for this? I have already initialized the relationshipt between the 2 models.
I was able to solve the problem. The relationship was defined as belongsTo which had to be changed to hasOne because of the type of join applied in the findAll query.

How does the NodeInterface and nodeField work in relayjs and what does they mean

how does the nodeInterface refetch and do object identification,
and what does the type represent and what does the obj represent, what is id here
and what does instanceof mean
const { nodeInterface, nodeField } = nodeDefinitions(
(globalId) => {
const { type, id } = fromGlobalId(globalId);
console.log('NodeDefinitions (globalId), id:', id);
console.log('NodeDefinitions (globalId), type:', type);
if (type === 'teacher') {
return teacher.findOne({ _id: id }).exec();
} else if (type === 'college') {
return college.findOne({ _id: id }).exec();
} else if (type === 'student') {
return student.findOne({ _id: id }).exec();
}
return null;
},
(obj) => {
if( obj instanceof Teacher) {
return teacherType
}
// other types too
return null;
});
nodeDefinitions returns the Node interface that objects can implement, and returns the node root field to include on the query type. To implement this, it takes a function to resolve an ID to an object, and to determine the type of a given object.
In GraphQL, data are represented by a tree, and the nodes of the tree are different types of data.
nodeInterface, nodeField are used to Serialize and Deserialize nodes. for example: if you want to add a book to the book collection, the new book and its id must be provided. Relay will then convert the book object into a new BookType node and add it into the tree.
I have created a demo on how to upload images using React + Relay + GraphQL.
Github repo: https://github.com/bfwg/relay-gallery
Live demo: http://fanjin.computer
InstanceOf in this case is checking the class type of the object with the given passed in globalID. See instanceof on Mozilla.
Regarding nodeInterface The simple answer, and what finally let me understand it, is that the nodeInterface is how the the app stays in contact with the object.Remember what you are doing with GraphQL when you create your schema. You are creating GraphQL class 'Types'. So nodeInterface, is checking your objects GraphQL Type. If you have no schema then you cant have types. The 'id' property is the idea on an instance of your type, or in this case, an instance of 'teacher', 'college' or 'student' GraphQLObjectType. So in a line, relay is saying, give me the objects type and id and I will keep track of it for you. Let me know if you need further explanation.

Using existing validators inside custom validator in Sequelize

I am using Sequelize on my Node.JS project and I would like to know if there's a way to use existing validator inside user-defined one.
For example:
var User = sequelize.define('User', {
foo: {
type: Sequelize.STRING,
validate: {
newValidator: function (value) {
//something like
if (value.length == 10) {
return this.foo.isUrl && this.foo.contains('bar');
} else {
return this,foo.isEmail;
}
}
}
}
});
Is it possible to somehow refer existing validators?
If you are on a pretty new version (not sure when the sequelize validator was exposed to users ) you could do Sequelize.DAOValidator.Validator.isUrl() etc. If that doesn't work for you, try importing validator into your own project var Validator = require('validator'). Since validator is already a sequelize dependency, there should be no need to add it to your package

Resources