I am trying to create a classic Articles/Categories association with mongoose.
Everything works fine, but since I am trying to expose the query results as JSON, I get a Converting circular structure to JSON error.
I know the issue is related in cross referencing models, but I don't know how to solve this.
Here are my model schemas.
var ArticleSchema = new Schema({
created: {
type: Date,
default: Date.now
},
title: {
type: String,
default: '',
trim: true
},
content: {
type: String,
default: '',
trim: true
},
user: {
type: Schema.ObjectId,
ref: 'User'
},
categories: [{
type: Schema.ObjectId,
ref: 'Category'
}]
});
ArticleSchema.statics.load = function(id, cb) {
this.findOne({
_id: id
}).populate('user', 'name username').populate('categories', 'title').exec(cb);
};
/**
* Category Schema
*/
var CategorySchema = new Schema({
created: {
type: Date,
default: Date.now
},
title: {
type: String,
default: '',
trim: true
},
user: {
type: Schema.ObjectId,
ref: 'User'
}
},
{
toObject: { virtuals: true },
toJSON: { virtuals: true }
});
/**
* Virtual Schema
*/
var articles = CategorySchema.virtual('articles');
articles.get(function () {
return Article.find({categories : { $eq: this }});
});
Seems to be like you have circular references, this mean that the json you create is calling it self in somere.. Im not sure if this line could be the problem:
articles.get(function () {
return Article.find({categories : { $eq: this }});
});
why dont you try with a hardcore value in $eq ??
Related
I am trying to write the category model for MySQL using Sequelize taking reference from this code of mongoose model in node(shared below) . I have gone through some articles but going through some confusion. Please help.
const mongoose = require("mongoose");
const categorySchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
trim: true,
},
createdBy: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
},
},
);
module.exports = mongoose.model("Category", categorySchema);
Here I've tried my code in using Sequelize for MySql but got stuck in createdBy block. Need Help!!!
module.exports = (sequelize, Sequelize) => {
const Category = sequelize.define("categories", {
name:{
type: Sequelize.STRING,
required: true,
trim: true,
},
createdBy:{
}
});
return Category;
};
The createdBy value from Mongo will be an ObjectID, which you can save as a string in MySQL, so use a DataTypes.STRING type in the definition, then set it on insert/update/etc from your Mongo lookup. The Sequelize variable in your example should be DataTypes as well.
module.exports = (sequelize, DataTypes) => {
const Category = sequelize.define('categories', {
name: {
type: DataTypes.STRING,
required: true,
trim: true,
},
createdBy: {
type: DataTypes.STRING,
// any other settings
},
});
return Category;
};
// get the value from mongoose
const createdBy = ...;
// set it on the create for your model to insert.
await Category.create({
name: 'Some Name',
createdBy,
});
I have the following table in mysql.
Users
CREATE TABLE users(
id INTEGER AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) UNIQUE NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
password VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
and Posts
CREATE TABLE posts(
id INTEGER AUTO_INCREMENT PRIMARY KEY,
created_by INTEGER NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
post_title VARCHAR(100) NOT NULL UNIQUE,
post_body VARCHAR(255) NOT NULL,
slug VARCHAR(100) NOT NULL UNIQUE,
FOREIGN KEY (created_by) REFERENCES users(id)
);
I am using nodejs and the mysql npm package. How can I make graphQL schema for the models?
I searched a lot but failed to find any solution to this. Mostly people are using sequelize for this purpose. Is it better than mysql package?
Yes You can use sequelize orm for connecting graphql to Mysql database
Refference :- http://docs.sequelizejs.com/manual/installation/getting-started
Sample Schema and resolvers are given
Schema.js
const typeDefinitions = `
type Author {
authorId: Int
firstName: String
lastName: String
posts: [Post]
}
type Post {
postId: Int
title: String
text: String
views: Int
author: Author
}
input postInput{
title: String
text: String
views: Int
}
type Query {
author(firstName: String, lastName: String): [Author]
posts(postId: Int, title: String, text: String, views: Int): [Post]
}
type Mutation {
createAuthor(firstName: String, lastName: String, posts:[postInput]): Author
updateAuthor(authorId: Int, firstName: String, lastName: String, posts:[postInput]): String
}
schema {
query: Query
mutation:Mutation
}
`;
export default [typeDefinitions];
connectors.js
import rp from 'request-promise';
var Sequelize = require('sequelize');
var db = new Sequelize('test', 'postgres', 'postgres', {
host: '192.168.1.168',
dialect: 'postgres',
pool: {
max: 5,
min: 0,
idle: 10000
}
});
const AuthorModel = db.define('author', {
authorId: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true, field: "author_id" },
firstName: { type: Sequelize.STRING, field: "first_name" },
lastName: { type: Sequelize.STRING, field: "last_name" },
},{
freezeTableName: false,
timestamps: false,
underscored: false,
tableName: "author"
});
const PostModel = db.define('post', {
postId: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true, field: "post_id" },
text: { type: Sequelize.STRING },
title: { type: Sequelize.STRING },
views: { type: Sequelize.INTEGER },
},{
freezeTableName: false,
timestamps: false,
underscored: false,
tableName: "post"
});
AuthorModel.hasMany(PostModel, {
foreignKey: 'author_id'
});
PostModel.belongsTo(AuthorModel, {
foreignKey: 'author_id'
});
const Author = db.models.author;
const Post = db.models.post;
export { Author, Post };
resolver.js
import { Author } from './connectors';
import { Post } from './connectors';
const resolvers = {
Query: {
author(_, args) {
return Author.findAll({ where: args });
},
posts(_, args) {
return Post.findAll({ where: args });
}
},
Mutation: {
createAuthor(_, args) {
console.log(args)
return Author.create(args, {
include: [{
model: Post,
}]
});
},
updateAuthor(_, args) {
var updateProfile = { title: "name here" };
console.log(args.authorId)
var filter = {
where: {
authorId: args.authorId
},
include: [
{ model: Post }
]
};
Author.findOne(filter).then(function (product) {
Author.update(args, { where: { authorId: args.authorId } }).then(function (result) {
product.posts[0].updateAttributes(args.posts[0]).then(function (result) {
//return result;
})
});
})
return "updated";
},
},
Author: {
posts(author) {
return author.getPosts();
},
},
Post: {
author(post) {
return post.getAuthor();
},
},
};
export default resolvers;
You can try a new open source tool called SwitchQL (github.com/SwitchQL/SwitchQL). I've been working on the project for a while.
You pass it your connection string and it returns everything you need to run a graphql server on top of an existing database. It also returns Apollo compliant client mutation and queries.
We only support Postgres at the moment, but if you'd like to look into helping us support MySQL let me know!
I am using GraphQL js.I want to implement One-to-many association in it.I have two types user and Office.One user has many offices.
userType:
var graphql = require('graphql');
const userType = new graphql.GraphQLObjectType({
name: 'user',
fields :()=>{
var officeType=require('./officeSchema');
return {
_id: {
type: graphql.GraphQLID
},
name: {
type: graphql.GraphQLString
},
age: {
type: graphql.GraphQLString
},
office:{
type:officeType
}
};
}
});
module.exports=userType;
officeSchema:
const officeType = new graphql.GraphQLObjectType({
name: 'office',
fields:()=> {
var userType = require('./userSchema');
return {
_id: {
type: graphql.GraphQLID
},
room: {
type: graphql.GraphQLString
},
location: {
type: graphql.GraphQLString
},
users: {
type: new graphql.GraphQLList(userType),
resolve: (obj,{_id}) => {
fetch('http://0.0.0.0:8082/office/user/'+obj._id, {
method: "GET",
headers: {
'Content-Type': 'application/json'
}
})
.then(function(res) {return res});
}
}
};
}
});
Now the mutation code is as follows:
const Adduser = {
type: userType,
args: {
name: {
type: graphql.GraphQLString
},
age: {
type: graphql.GraphQLString
}
},
resolve: (obj, {
input
}) => {
}
};
const Addoffice = {
type: OfficeType,
args: {
room: {
type: graphql.GraphQLString
},
location: {
type: graphql.GraphQLString
},
users: {
type: new graphql.GraphQLList(userInputType)
}
},
resolve: (obj, {
input
}) => {
}
};
const Rootmutation = new graphql.GraphQLObjectType({
name: 'Rootmutation',
fields: {
Adduser: Adduser,
Addoffice: Addoffice
}
});
This code is throwing error as
Rootmutation.Addoffice(users:) argument type must be Input Type but got: [user].
I want to add the actual fields in database as well as associated tables' fields but couldn't figure out the problem.
Updated:
1-Added GraphQLInputObjectType:
const officeInputType = new graphql.GraphQLInputObjectType({
name: 'officeinput',
fields: () => {
return {
room: {
type: graphql.GraphQLString
},
location: {
type: graphql.GraphQLString
}
}
}
});
const userInputType = new graphql.GraphQLInputObjectType({
name: 'userinput',
fields: () => {
return {
name: {
type: graphql.GraphQLString
},
age: {
type: graphql.GraphQLString
}
}
}
});
2-Added userinputtype instead of usertype in AddOffice.
Now the error is
Rootmutation.Addoffice(user:) argument type must be Input Type but got: userinput.
The problem is that you provided userType as one of the argument types for the Addoffice mutation. userType cannot be an argument type. Instead, you must use an input type.
There are two object types: output and input types. Your userType and officeType are output types. You need to create an input type using GraphQLInputObjectType [docs]. It will likely have very similar fields. You can use that as a type on your argument field.
const userInputType = new graphql.GraphQLInputObjectType({
name: 'UserInput',
fields () => {
return {
_id: {
type: graphql.GraphQLID
},
// ...
};
}
});
I have 2 models Project model and Task model defined in sequelize as shown below
import { INTEGER, STRING, DATE } from 'sequelize';
import sequelize from '../sequelize';
import Task from './task.model'
const ProjectModel = sequelize.define('project', {
project_id: {
type: INTEGER,
primaryKey: true,
autoIncrement: true
},
phabricator_project_id: {
type: STRING,
allowNull: false
},
name: {
type: STRING
},
description: {
type: STRING
},
start_date: {
type: STRING,
},
end_date: {
type: STRING
}
},
{
timestamps: false
}
);
export default ProjectModel;
and the task model
import { INTEGER, STRING, DATE } from 'sequelize';
import sequelize from '../sequelize';
const TaskModel = sequelize.define('task', {
task_id: {
type: INTEGER,
primaryKey: true,
autoIncrement: true
},
title: {
type: STRING
},
status: {
type: STRING
},
priority: {
type: STRING
},
description: {
type: STRING
},
tool_project_id: {
type: STRING
},
date_modified: {
type: STRING
}
},
{
timestamps: false
}
);
export default TaskModel;
What I want to achieve is to create a relation between tool_project_id in TaskModel and phabricator_project_id in ProjectModel (they are same values only diff column names are given) and write a query for a GET request which outputs the data in form shown below
{ {project1Details,TaskDetails-->{task1, task2, task3},
{project2Details,TaskDetails-->{task4, task5, task6},
{project3Details,TaskDetails-->{task7, task8, task9},
{project4Details,TaskDetails-->{task10, task11, task12} }
All the database design has been done accordingly and another file is called to create all these databases. This is written in typescript and I tried this as a GET method
listByProjects(req, res) {
TaskModel.belongsTo(ProjectModel, { as: 'task' , foreignKey: 'tool_project_id'});
ProjectModel.findAll({
include:[{model:TaskModel}],
where:{status:'open'}
}).then(function(projects) {
res.json(projects);
});
}
Here in this method I define the relation and try to list all 'open' tasks and send them back as response but I am getting the error
Unhandled rejection Error: task is not associated to project!
ANY HELP TO THIS PROBLEM WOULD BE WONDERFULL
The answer to this question is that when creating the table we should create the relation and then create the table such as
Create the relation also the name of the key should be same so as to create relation.
TaskModel.belongsTo(ProjectModel, {foreignKey: 'project_id' });
ProjectModel.hasMany(TaskModel, { foreignKey: 'project_id' });
Then create the table project and then tasks
ProjectModel.sync({ force: false }).then(function () {
console.log('Project table created');
TaskModel.sync({ force: false }).then(function () {
console.log('Task table created');
});
});
then in the API method, you are invoking just include the model which you want to provide to get the required data.
ProjectModel.findAll({
include: [{
model: TimeSheetModel,
where: {
status: "ACTIVE"
},
}],
}).then(function (projects) {
const responseData = {
'status': 1,
'message': 'List successfull.',
'projects': projects,
};
res.json(responseData);
}).catch(error => {
const responseData = {
'status': 1,
'message': error.message,
'projects': [],
};
res.json(responseData);
})
This uses nodemon and sequilize to manage node and relations of the table respectively
I got this SimpleSchema for a collection in my meteor-app
Collection.attachSchema(new SimpleSchema({
title: { type: String },
slug: { type: String, unique: true },
language: { type: String, defaultValue: "en" },
'category.element': { type: String, optional: true }
}));
And I try to insert this JSON-data, but I get insert failed: Error: Category must be an object at getErrorObject
{
"_id" : "25uAB4TfeSfwAFRgv",
"title" : "Test 123",
"slug" : "test_123",
"language" : "en",
"category" : [
{
"element" : "Anything"
}
]
}
What is wrong with my JSON-data? Or what's wrong with my SimpleSchema. I can change both of them to match the best way.
You need to first declare the object, like,
Collection.attachSchema(new SimpleSchema({
...,
....,
category: {type: [Object], optional: true}
}));
After that you can extend/define object field(s) like,
Collection.attachSchema(new SimpleSchema({
....,
....,
category: {type: [Object]},
'category.$.element': {type: String}
}));
use '$' if its a Array Object ([Object]), If only object then then do not use '$'.
If you do not sure about Object Structure, use another parameter blackbox:true
like,
category: {type: [Object], blackbox: true}
The simplest solution is to define category as an array of objects in your schema:
Collection.attachSchema(new SimpleSchema({
title: { type: String },
slug: { type: String, unique: true },
language: { type: String, defaultValue: "en" },
category: { type: [Object], optional: true }
}));
This will get you unstuck.
If you want to be more specific about the contents of category then you can define a sub-schema for category. For example:
CategorySchema = new SimpleSchema({
element: { type: String }
});
Collection.attachSchema(new SimpleSchema({
title: { type: String },
slug: { type: String, unique: true },
language: { type: String, defaultValue: "en" },
category: { type: [CategorySchema], optional: true }
}));