How to get relationship model in Keystone.js - javascript

So I try to get 'Pronouns' model from 'Sentence' model from 'TestGenerator' model. And I have id's, but no model.
TestGenerator Model
var TestGenerator = new keystone.List('TestGenerator', {
map: { name: 'title' },
autokey: { path: 'slug', from: 'title', unique: true }
});
TestGenerator.add({
title: { type: String, required: true },
sentences: { type: Types.Relationship, ref: 'Sentence', many: true },
});
TestGenerator.register();
var Sentence = new keystone.List('Sentence', {
map: { name: 'title' },
autokey: { path: 'slug', from: 'title', unique: true }
});
Sentence Model
Sentence.add({
title: { type: String, required: true },
pronouns: { type: Types.Relationship, ref: 'Pronoun', many: true },
verbs: { type: Types.Relationship, ref: 'Verb', many: true },
});
Sentence.relationship({ ref: 'TestGenerator', path: 'sentences' });
Sentence.register();
Ponoun model
var Pronoun = new keystone.List('Pronoun', {
map: { name: 'english' },
autokey: { path: 'slug', from: 'english', unique: true },
});
Pronoun.add({
english: { type: String },
russian: { type: String }
});
Pronoun.relationship({ ref: 'Sentence', path: 'pronouns' });
Pronoun.register();
And my controller
view.on('init', function (next) {
var q = keystone.list('TestGenerator').model.findOne({
slug: locals.filters.test_genetation,
}).populate('sentences');
q.exec(function (err, result) {
locals.testGenerator = result;
locals.current_sentence = result.sentences[0];
locals.data.englishSentence = locals.current_sentence.pronouns;
console.log(locals.data.englishSentence);
next(err);
});
});
And "locals.data.englishSentence" return
["5739bd696ef7d78d16e9e0e5","573ac7645e3d7d7210f1c4be"]
So how I can fix it? Don't understand.

This is related to mongoose, not Keystone actually. Mongoose doesn't provide deep population, you'll need a separate plugin like e.g. mongoose-deep-populate

Have you tried explicitly including pronouns in the populate() call?
var q = keystone.list('TestGenerator').model.findOne({
slug: locals.filters.test_genetation,
})
.populate('sentences sentences.pronouns')
.exec(function (err, result) {
...
});

Related

Limit Depth on Recursive GraphQL Schema Query using graphql-sequelize resolver (Node.js, express-graphql)

I have 2 Models, User and Post. I want to be able to get User information when querying a post, and be able to get all of a User's posts when querying a user.
they have an association as follows:
User.hasMany(Post, {
foreignKey: 'user',
as: 'posts'
});
Post.belongsTo(User, {
foreignKey: 'id',
sourceKey: 'user',
as: 'userObject'
})
Post.addScope('defaultScope', {
include: [{ model: User, as: 'userObject' }],
}, { override: true })
User.addScope('defaultScope', {
include: [{ model: Post, as: 'posts' }],
}, { override: true })
Here are my Models
User.js
module.exports.userType = new GQL.GraphQLObjectType({
name: 'User',
fields: () => {
const { postType } = require('../Post/Post');
return {
id: {
type: new GQL.GraphQLNonNull(GQL.GraphQLString),
description: 'user unique id'
},
ci_username: {
type: new GQL.GraphQLNonNull(GQL.GraphQLString),
unique: true,
description: 'case INSENSITIVE username of the user'
},
username: {
type: new GQL.GraphQLNonNull(GQL.GraphQLString),
description: 'case SENSITIVE username of the user'
},
password: {
type: new GQL.GraphQLNonNull(GQL.GraphQLString),
description: 'password for the user'
},
first_name: {
type: new GQL.GraphQLNonNull(GQL.GraphQLString),
description: 'first name of user'
},
last_name: {
type: GQL.GraphQLString,
description: 'last name of user (optional)'
},
profile_picture: {
type: GQL.GraphQLString,
description: 'profile picture for the user'
},
posts: {
type: GQL.GraphQLList(postType),
description: 'list of users posts'
}
}
},
})
/** define User model for the database */
module.exports.User = db.define('user', {
id: {
type: Sequelize.UUID,
primaryKey: true,
unique: true,
},
ci_username: {
type: Sequelize.STRING,
unique: true,
},
username: Sequelize.STRING,
password: Sequelize.STRING,
first_name: Sequelize.STRING,
last_name: Sequelize.STRING,
profile_picture: Sequelize.STRING,
}, {
// Tells sequelize not to query the "CreatedAt" or "UpdatedAt" Columns
timestamps: false
})
Post.js
module.exports.postType = new GQL.GraphQLObjectType({
name: 'Post',
fields: () => {
const { userType } = require('../User/User');
return {
id: {
type: new GQL.GraphQLNonNull(GQL.GraphQLString),
description: 'post unique id'
},
name: {
type: new GQL.GraphQLNonNull(GQL.GraphQLString),
description: 'name of the post'
},
user: {
type: userType,
description: 'user object of who created the post'
},
created_at: {
type: new GQL.GraphQLNonNull(GQL.GraphQLString),
description: 'the datetime the post was created',
}
}
},
})
/** define User model for the database */
module.exports.Post = db.define('post', {
id: {
type: DataTypes.UUID,
primaryKey: true,
unique: true,
},
name: DataTypes.STRING,
user: {
type: DataTypes.STRING,
references: {
model: 'users',
key: 'id'
}
},
created_at: {
type: DataTypes.TIME,
defaultValue: DataTypes.NOW
}
}, {
// Tells sequelize not to query the "CreatedAt" or "UpdatedAt" Columns
timestamps: false
})
Here are my Queries:
allUsers.js
const allUsers = {
type: new GQL.GraphQLList(userType),
args: {
username: {
description: 'username of the user',
type: GQL.GraphQLString,
},
// An arg with the key limit will automatically be converted to a limit on the target
limit: {
type: GQL.GraphQLInt,
default: 10
},
// An arg with the key order will automatically be converted to a order on the target
order: {
type: GQL.GraphQLString
}
},
// use graphql-sequelize resolver with the User model from database
resolve: resolver(User)
}
allPosts.js
const allPosts = {
type: new GQL.GraphQLList(postType),
args: {
username: {
description: 'username of the user',
type: GQL.GraphQLString,
},
// An arg with the key limit will automatically be converted to a limit on the target
limit: {
type: GQL.GraphQLInt,
default: 10
},
// An arg with the key order will automatically be converted to a order on the target
order: {
type: GQL.GraphQLString
}
},
// use graphql-sequelize resolver with the Post model from database
resolve: resolver(Post)
}
I'm currently getting a Maximum call stack size exceeded. I assume because the resolver in the queries are recursively getting details on posts and users infinitely.
Does anyone know of any way to put a depth limitation on the resolver? Or is it just not possible to have a recursive query like this?
You would have to remove the default scope from the included model as shown here like this:
Post.addScope('defaultScope', {
include: [{ model: User.scope(null), as: 'userObject' }],
}, { override: true })
User.addScope('defaultScope', {
include: [{ model: Post.scope(null), as: 'posts' }],
}, { override: true })
To support additional depth, you'd need to implement resolvers for the fields in question, for example:
function resolve (user) {
if (user.posts) {
return user.posts
}
return user.getPosts()
}

Populate in nested schema Mongoose

I have this User model:
const userSchema = new Schema({
_id: {
type: Schema.Types.ObjectId,
required: true
},
name: {
type: String,
required: true
},
email: {
type: String,
unique: true,
required: true
},
notification: {
experiment_id: {
type: Schema.Types.ObjectId,
ref: "Experiment",
required: false
},
seen: {
type: Boolean,
required: true,
default: false
}
}
});
And this Experiment model:
const experimentSchema = new Schema(
{
_id: {
type: Schema.Types.ObjectId,
required: true
},
name: {
type: String,
required: true
},
description: {
type: String,
required: true,
default: "No Description"
},
author_id: {
type: Schema.Types.ObjectId,
ref: "User",
required: true
}
);
I am trying to populate from User the experiment_id in notification.
And from this populate, I would like to populate the author_id as well.
I have seen some code like I have done below but I didn't succeed.
I am trying this:
User.find(
{
_id: req.params.currentUserId
},
"notification"
)
.populate({ path: "experiment_id", populate: { path: "author_id" } })
.exec((err, notif) => {
});
I fixed it by adding notification.experiment_id in the path
User.find(
{
_id: req.params.currentUserId
},
"notification"
)
.populate({ path: "notification.experiment_id", populate: { path: "author_id" } })
.exec((err, notif) => {
});

Mongoose create method ignores variable, but not hard coded data

Can someone lead me in the right direction? I'ven been stuck on this a few days. I'm using Express and Mongoose. Here is my controller,
module.exports.addPoll = function(req,res) {
var optionsArray = [];
var row = [];
req.body.option1.forEach( function(value) {
optionsArray.push('{"v":"' + value + '"},{"v":0}');
});
var beg = '{"c":[';
var end = ']}';
var whole = beg.concat(optionsArray, end);
Pol.create({
cols: [{label: "options", type: "string"}, {label: "quantity", type:
"number"}],
rows: whole,
title: req.body.heading
}, function(err, poll) {
if (err) {
sendJSONresponse(res, 400, err);
} else {
sendJSONresponse(res, 201, poll);
console.log(whole);
}
});
}
When i create the document the rows property is empty. It ignores the variable that has the data and looks like this.
{"__v":0,"rows":{"c":[]},"title":"What is tomorrow","_id":"59826915c7a0186940e8431c","cols":[{"label":"options","type":"string"},{"label":"quantity","type":"number"}]}
Here is what it looks like when it logs and if replace the variable withi this, it works perfectly fine. I don't get it.
[{"c":[{"v":"Monday"},{"v":"0"},{"v":"Tuesday"},{"v":"0"}]}]
Here is my schema.
var rowsSchema = new mongoose.Schema({
c: [{
v: {
type: String,
required: true
},
_id:false
},
{
v:{
type:Number,
required: true
},
_id:false
}],
_id: false
})
var pollSchema = new mongoose.Schema({
cols: [{
label: {
type: String,
required: true
},
type: {
type: String,
required: true
},
_id: false
},
{
label: {
type: String,
required: true
},
type: {
type: String,
required: true
},
_id:false
}],
rows: rowsSchema,
title: {type: String, required:true, _id:false}
})

Mongoose Schema for geoJson coordinates

I tried to create a schema for geojson but have had some problems with syntax for coordinates.
Here's my current code:
var DataSchema = new Schema({
properties: {
title: { type: String, required: true },
description: { type: String, required: true },
date: { type:Date, default:Date.now }
},
geometry: {
coordinates: []
}
});
I tried using [] (empty array), it creates '' and [Number,Number] but it doesn't work.
My question is: how do I have to construct my schema so as result I will get
coordinates: [ 3.43434343, 5.543434343 ]
without quotation marks, is this possible?
Express Route
app.post('/mountain_rescue', function (req, res){
new rescueData({properties:{title: req.body.title, description: req.body.description},geometry:{
coordinates:req.body.coordinates}}).save(function (e, result) {
console.log(result);
});
res.redirect('/mountain_rescue');
});
View
<div id="AddingPanel">
<form method="post" action="mountain_rescue" >
Title:<input type="text" name="title">
Description:<textarea type="text" name="description"></textarea>
Coordinates:<input type="text" name="coordinates">
<button type="submit">Add</button>
</form>
A GeoJSON field has to be included a geometry type as a string. So a GeoJSON field must be defined like the following;
geometry: { type: { type: String }, coordinates: [Number] }
or if you want to define a default value you might use the below line;
geometry: { type: { type: String, default:'Point' }, coordinates: [Number] }
Good luck..
Like this;
var DataSchema = new Schema({
properties: {
title: { type: String, required: true },
description: { type: String, required: true },
date: { type:Date, default:Date.now }
},
geometry: {
coordinates: { type: [Number], index: '2dsphere'}
}
});
Here is your update route handler, it converts coordinates string to number array;
app.post('/mountain_rescue', function (req, res) {
new rescueData({
properties: {
title: req.body.title, description: req.body.description
},
geometry: {
coordinates:req.body.coordinates.split(',').map(Number)
}
}).save(function (e, result) {
console.log(result);
});
res.redirect('/mountain_rescue');
});
try this:
var DataSchema = new Schema({
properties: {
title: { type: String, required: true },
description: { type: String, required: true },
date: { type:Date, default:Date.now }
},
geometry: {
coordinates: {type: Array, required: true}
}
});

Passing id between router and collection, Meteor

I am trying to use Session.set() in my router and Session.get() with autoValue in my schema, but Session.get() is undefined within my schema scope. How can I pass the value of the current id (as it is appended to the end of my URL) to autoValue in my schema?
//collections/singleDetails.js
singleDetails = new Meteor.Collection('singleDetails');
singleDetails.attachSchema(new SimpleSchema({
location: {
type: String,
label: 'location',
max: 200,
optional: true
},
number: {
type: Number,
label: 'number of attendees',
max: 2000,
optional: true
},
occasion: {
type: String,
label: 'occasion for speech'
},
speakerId: {
type: String,
autoValue: function(){
Session.get('pathId');
}
}
}));
And here is the router
//client/views/singleDetails.js
Router.route('singleDetails', {
path: '/speakers/singleDetails/:_id',
template: 'singleDetails',
data: function(){
return Speakers.findOne({_id: this.params._id});
},
onBeforeAction: function(){
console.log(this.params._id);
Session.set('pathId', this.params._id);
this.next();
}
});
Template.singleDetails.helpers({
singleDetails: function(){
return singleDetails.find({});
}
});

Categories

Resources