I am confused how to apply BelongsToMany with Bookshelf.
Say, there is a Movie that BelongsToMany Genres, e.g.
"The Artist" has the genres "Comedy, Drama"
I have setup a join table called join_movies_genres that has FK movie_id and genre_id.
I try to fetch genres from a Movie with and without a through(...) definition. I get however undefined targets, similar to:
relatedData:
{ type: 'belongsToMany',
target:
{ [Function]
NotFoundError: [Function: ErrorCtor],
NoRowsUpdatedError: [Function: ErrorCtor],
NoRowsDeletedError: [Function: ErrorCtor] },
targetTableName: 'genres',
targetIdAttribute: 'id',
joinTableName: 'join_movies_genres',
foreignKey: { debug: true },
otherKey: undefined,
parentId: 1,
parentTableName: 'movies',
parentIdAttribute: 'id',
parentFk: 1,
throughTarget:
{ [Function]
NotFoundError: [Function: ErrorCtor],
NoRowsUpdatedError: [Function: ErrorCtor],
NoRowsDeletedError: [Function: ErrorCtor] },
throughTableName: 'join_movies_genres',
throughIdAttribute: 'id',
throughForeignKey: { debug: true } }
So, how would I approach setting up this relation? How can I enable a debug output?
The current state of the Model is:
var Movie = bookshelf.Model.extend({
tableName: 'movies',
genres: function() {
// return this.belongsToMany(Genre, 'join_movies_genres', 'movie_id', 'genre_id', {debug: true});
// return this.belongsToMany(Genre).through(JoinMovieGenre, 'movie_id', 'genre_id');
return this.belongsToMany(Genre, 'join_movies_genres', 'movie_id', 'genre_id').through(JoinMovieGenre, {debug: true});
}
});
var Genre = bookshelf.Model.extend({
tableName: 'genres'
});
new Movie({title: 'The Artist'}).fetch({debug: true}).then(function(m) {
console.log(m.toJSON());
console.log(m.genres())
})
A sandbox of this code is at https://github.com/mulderp/bookshelf-demo/tree/cli_migrations
Does this work?
var Movie = bookshelf.Model.extend({
tableName: 'movies',
genres: function() {
return this.belongsToMany(Genre, 'join_movies_genres', 'movie_id', 'genre_id');
}
});
var Genre = bookshelf.Model.extend({
tableName: 'genres'
});
new Movie({title: 'The Artist'}).fetch({withRelated:['genres']}).then(function(m) {
console.log(m.toJSON());
});
Related
Below is my code to update values in my MongoDB using mongoose. It updates only the first item in the array but not the second one.
I want this code to update both the items.
router.post('/updateattendance', (req, res) => {
let attendaceColl = [
{roll_number: 9915,date: '2019-05-21',was_present: true},
{roll_number: 9904,date: '2019-05-21',was_present: true}
];
async.eachSeries(attendaceColl, (stdnt, done) => {
UserSubjectDetails.findOneAndUpdate(
{
roll_number: stdnt.roll_number
},
{
$push: {
'subject_details.attendance.doc' : [{
date: stdnt.date,
was_present: stdnt.was_present
}]
}
}, function(err, rsesult) {
console.log('did')
}, done);
}, function(err, res){
console.log(res)
});
});
In GraphiQL at http://localhost:8080/graphiql, I'm using this query:
{
instant_message(fromID: "1"){
fromID
toID
msgText
}
}
I'm getting this response:
{
"data": {
"instant_message": {
"fromID": null,
"toID": null,
"msgText": null
}
},
"errors": [
{
"message": "Resolve function for \"instant_message.fromID\" returned undefined",
"locations": [
{
"line": 3,
"column": 5
}
]
},
{
"message": "Resolve function for \"instant_message.toID\" returned undefined",
"locations": [
{
"line": 4,
"column": 5
}
]
},
{
"message": "Resolve function for \"instant_message.msgText\" returned undefined",
"locations": [
{
"line": 5,
"column": 5
}
]
}
]
}
I tried to set up my system according to the examples found here:
https://medium.com/apollo-stack/tutorial-building-a-graphql-server-cddaa023c035#.s7vjgjkb7
Looking at that article, it doesn't seem to be necessary to set up individual resolvers for string fields, but I must be missing something.
What is the correct way to update my resolvers so as to return results from string fields? Example code would be greatly appreciated!
Thanks very much in advance to all for any thoughts or info.
CONNECTORS
import Sequelize from 'sequelize';
//SQL CONNECTORS
const db = new Sequelize(Meteor.settings.postgres.current_dev_system.dbname, Meteor.settings.postgres.current_dev_system.dbuser, Meteor.settings.postgres.current_dev_system.dbpsd, {
host: 'localhost',
dialect: 'postgres',
});
db
.authenticate()
.then(function(err) {
console.log('Connection to Sequelize has been established successfully.');
})
.catch(function (err) {
console.log('Unable to connect to the Sequelize database:', err);
});
const IMModel = db.define('IM', {
id: {type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true},
fromID: {type: Sequelize.STRING},
toID: {type: Sequelize.STRING},
msgText: {type: Sequelize.STRING}
});
IMModel.sync({force: true}).then(function () {
// Table created
return IMModel.create({
fromID: '1',
toID: '2',
msgText: 'msg set up via IMModel.create'
});
});
const IM = db.models.IM;
export {db, IM };
SCHEMA
const typeDefinitions = [`
type instant_message {
id: Int
fromID: String
toID: String
msgText: String
}
type Query {
instant_message(fromID: String, toID: String, msgText: String): instant_message
}
type RootMutation {
createInstant_message(
fromID: String!
toID: String!
msgText: String!
): instant_message
}
schema {
query: Query,
mutation: RootMutation
}
`];
export default typeDefinitions;
RESOLVERS
import * as connectors from './db-connectors';
import { Kind } from 'graphql/language';
const b = 100;
const resolvers = {
Query: {
instant_message(_, args) {
const a = 100;
return connectors.IM.find({ where: args });
}
},
RootMutation: {
createInstant_message: (__, args) => { return connectors.IM.create(args); },
},
};
export default resolvers;
When you define your GraphQLObjectTypes you need to provide a resolver for each of their fields.
You defined your instant_message with multiple fields but did not provide resolvers for each of these fields.
More over you defined the types of those field with regular typescript fields while you need to define it with GraphQL types (GraphQLInt, GraphQLString, GrapQLFloat etc..)
So defining your type should look something like this:
let instant_message = new GraphQLObjectType({
id: {
type: GraphQLInt,
resolve: (instantMsg)=> {return instantMsg.id}
}
fromID: {
type: GraphQLString,
resolve: (instantMsg)=> {return instantMsg.fromID}
}
toID: {
type: GraphQLString,
resolve: (instantMsg)=> {return instantMsg.toID}
}
msgText: {
type: GraphQLString,
resolve: (instantMsg)=> {return instantMsg.msgText}
}
})
In addition, you will need to define your Query as follows:
let Query = new GraphQLObjectType({
name: "query",
description: "...",
fields: () => ({
instant_messages: {
type: new GraphQLList(instant_message),
args: {
id: {type: GraphQLInt}
},
resolve: (root, args) => {
connectors.IM.find({ where: args })
}
}
})
})
The issue is that the query does not expect an array,
Please fix it:
type Query {
instant_message(fromID: String, toID: String, msgText: String): [instant_message]
}
Then you should make sure the resolver returns Array of objects, if it doesnt work then the resolver is not returning an Array.
I have created some users and i have data in my users collection.I wan to display this data in a reactive table https://github.com/aslagle/reactive-table
This is my helper code
settings: function () {
return {
collection: Meteor.users.find(),
rowsPerPage: 10,
showFilter: true,
fields: ['_id', 'profile.schoolname', 'profile.schoollocation']
};
},
and this is my html
{{> reactiveTable settings=settings}}
When i run the code,only the fields i have defined show but not the data.
What could be the problem?.
I solved it this way.
In the helper
settings: function () {
return {
collection: 'ma',
rowsPerPage: 5,
showFilter: true,
showNavigation: 'auto',
fields: [
{ key: '_id', label: 'Id' },
{ key: 'profile.schoolname', label: 'Name' },
{ key: 'profile.schooltelephonenumber', label: 'Telephone' },
{ key: 'profile.schoollocation', label: 'Location' }
],
useFontAwesome: true,
group: 'client'
};
},
and in my publish
ReactiveTable.publish("ma",
function () {
return Meteor.users;
}
);
the html
{{> reactiveTable settings=settings}
I have two model:
var Book = bookshelf.Model.extend({
tableName: 'books',
chapters: function(){
var chapters = require('./chapter').Chapter;
return this.hasMany(chapters, 'bookId');
}
});
module.exports = {
Book: Book
};
var Chapter = bookshelf.Model.extend({
tableName: 'chapters',
hasTimestamps: ['dateCreated', 'dateUpdated'],
//relationship
book: function(){
var book = require('./user').Book;
return this.belongsTo(book, 'bookId');
}
});
module.exports = {
Chapter: Chapter
}
I do this in one of the controller:
new Book.Book()
.fetch({withRelated:['chapters']})
.then(function(books){
resolve(books);
}).catch(function(err){
reject(err);
});
The "chapters": [] because the debug log give me this, the bindings is undefined:
{ method: 'select',
options: {},
bindings: [ 1 ],
sql: 'select `books`.* from `books` limit ?' }
{ method: 'select',
options: {},
bindings: [ undefined ],
sql: 'select `chapters`.* from `chapters` where `chapters`.`bookId` in (?)' }
Bookshelf assumes that you have a column called 'id' in each table which uniquely identifies each row. If you are using a primary key which is not called 'id' you need to specify what it is called to be able to use the model in a relation. This is done by the 'idAttribute' member e.g.
var ModelA = bookshelf.Model.extend({
tableName: "ModelA",
idAttribute: "modelA_id"
...
}
});
http://bookshelfjs.org/#Model-instance-idAttribute
Solved it with idAttribute: 'cardId' specified.
I've been trying to make sure that the load event in the Rally.ui.grid.Grid is firing since I have a problem because my Grid is not filtering. I tried calling the methods myStore.setFilter() and myStore.load(), these two are firing, but I can't be sure the Grid is working properly since the first time, when it all loads, it does the filtering right, but then when I change the dropdown or combobox it doesn't.
This is how I load myStore:
this.myStore=Ext.create("Rally.data.wsapi.Store",{
model:"Task",
autoLoad:true,
filters: myFilters,
listeners:{
load:function(myStore,myData,success){
if(!this.myGrid) //IT CREATES THE GRID FOR THE FIRST TIME
{
this._loadGrid(myStore)
console.log('Grid Created!');
// this.myStore.setFilter();
// this.myStore.load();
}
else
{
this.myStore.setFilter();
//this.myStore.load();
console.log('Grid reloaded!');
console.log(myFilters);
}
},
scope:this
},
fetch:["FormattedID","State","Iteration", "Release"]
}
)
}
And this is how I load myGrid:
_loadGrid:function(myStoryStore){
this.myGrid = Ext.create("Rally.ui.grid.Grid",{
store:myStoryStore,
columnCfgs:["FormattedID","State","Iteration", "Release"],
listeners: {
load: function(myGridy){
console.log('myGrid did load!');
},
scope:this
}
});
this.add(this.myGrid);
}
Here is an example by David Thomas from his videos on building Rally apps that uses reconfigure method to which a store is passed: _myGrid.reconfigure(myStore)
Ext.define('CustomApp', {
extend: 'Rally.app.App',
componentCls: 'app',
launch: function() {
var relComboBox = Ext.create('Rally.ui.combobox.ReleaseComboBox',{
listeners:{
ready: function(combobox){
//console.log('loaded release name', combobox.getRecord().get('Name')); //getRecord() returns currently selected item
var releaseRef = combobox.getRecord().get('_ref');
this._loadStories(releaseRef);
//console.log('what is this', this);
},
select: function(combobox){
var releaseRef = combobox.getRecord().get('_ref');
this._loadStories(releaseRef);
},
scope: this
}
});
this.add(relComboBox);
},
_loadStories: function(releaseRef){
console.log('loading stories for ', releaseRef);
var myStore = Ext.create('Rally.data.WsapiDataStore',{
model: 'User Story',
autoLoad:true,
fetch: ['Name','ScheduleState','FormattedID'],
filters:[
{
property : 'Release',
operator : '=',
value : releaseRef
}
],
listeners: {
load: function(store,records,success){
console.log("loaded %i records", records.length);
this._updateGrid(myStore);
},
scope:this
}
});
},
_createGrid: function(myStore){
console.log("load grid", myStore);
this._myGrid = Ext.create('Ext.grid.Panel', {
title: 'Stories by Release',
store: myStore,
columns: [
{text: 'ID', dataIndex: 'FormattedID', flex: 1},
{text: 'Story Name', dataIndex: 'Name', flex: 2},
{text: 'Schedule State', dataIndex: 'ScheduleState', flex: 2}
],
height: 400
});
this.add(this._myGrid);
},
_updateGrid: function(myStore){
if(this._myGrid === undefined){
this._createGrid(myStore);
}
else{
this._myGrid.reconfigure(myStore);
}
}
});