Sequelize bulkCreate controller not inserting into associated table - javascript

I am trying to insert multiple records into 2 associated tables/models using sequelize bulkCreate. I can only get data to insert into the parent table. I am using MVC framework.
I have each model in a separate file, with associations defined therein.
The function to carry out the bulkCreate is in a separate controller file.
I call the request using a route file.
Here are the models and the model index
Occurrence.js
module.exports = (sequelize, Sequelize) => {
const Occurrence = sequelize.define("occurrence", {
recordedBy : {
type : Sequelize.STRING,
allowNull : true
},
scientificName : {
type : Sequelize.STRING,
allowNull : true
}
});
Occurrence.associate = (models) => {
Occurrence.hasMany(models.materialSample, {
foreignKey: 'occurrenceTableID'
})
}
return Occurrence;
};
MaterialSample.js
module.exports = (sequelize, Sequelize) => {
const MaterialSample = sequelize.define("materialSample", {
materialSampleID : {
type : Sequelize.STRING,
allowNull : true
},
materialSampleType : {
type : Sequelize.STRING,
allowNull : true
}
numberCollected : {
type : Sequelize.INTEGER,
allowNull : true
}
});
MaterialSample.associate = (models) => {
MaterialSample.belongsTo(models.occurrence, {
foreignKey: 'occurrenceTableID'
})
}
return MaterialSample;
};
index.js
const dbConfig = require("../config/db.config.js");
const Sequelize = require("sequelize");
const { response } = require("express");
const sequelize = new Sequelize(dbConfig.DB, dbConfig.USER, dbConfig.PASSWORD, {
host: dbConfig.HOST,
dialect: dbConfig.dialect,
operatorsAliases: false,
pool: {
max: dbConfig.pool.max,
min: dbConfig.pool.min,
acquire: dbConfig.pool.acquire,
idle: dbConfig.pool.idle
}
});
const db = {};
db.Sequelize = Sequelize;
db.sequelize = sequelize;
db.project = require("./Project.js")(sequelize, Sequelize);
db.event = require("./Event.js")(sequelize, Sequelize);
db.occurrence = require("./Occurrence.js")(sequelize, Sequelize);
db.materialSample = require("./MaterialSample.js")(sequelize, Sequelize);
db.preservedSpecimen = require("./PreservedSpecimen.js")(sequelize, Sequelize);
db.transfer = require("./Transfer.js")(sequelize, Sequelize);
db.germplasmViabilityTest = require("./GermplasmViabilityTest.js")(sequelize, Sequelize);
db.viabilityTracking = require("./ViabilityTracking.js")(sequelize, Sequelize);
//set up table associations
Object.keys(db).forEach((modelName) => {
if ('associate' in db[modelName]){
//call the associate function and pass reference to all other models
db[modelName].associate(db)
}
})
module.exports = db;
Here is the controller file with the bulkCreate function (insertData.js)
const db = require("../models");
const MaterialSample = db.materialSample;
const Occurrence = db.occurrence;
async function insertData() {
//create some data
const data = [
{
recordedBy: "Botanist One",
MaterialSamples:
{
materialSampleType: "tissue",
numberCollected: 2
}
},
{
recordedBy: "Botanist Two",
MaterialSamples:
{
materialSampleType: "seed",
numberCollected: 72
}
}
]
//use bulkCreate with include to insert the data
const result = await Occurrence.bulkCreate(data, {
include : [
{
model: MaterialSample
}
]
})
}
module.exports = {
insertData
}
and the route
const express = require("express");
const router = express.Router();
const insertDatasController= require("../controllers/insertData")
let routes = (app) => {
//views
//index
app.get('/', (req, res) => {
res.render("index");
});
//POST /api/materialSamples
router.post("/materialSample", insertDataController.insertData)
app.use("/api", router);
};
module.exports = routes;
Both records are inserted into the occurrence table, but nothing gets inserted into the materialSample table.
I will also note that when I send POST the request via PostMan, it seems to hang in "Sending request..."

this fixed it. it was the capitalization of the table name in the controller function
async function insertData(req, res) {
//create some data
const data = [{
recordedBy: "Botanist One",
materialSamples: {
materialSampleType: "tissue",
numberCollected: 2
}
},
{
recordedBy: "Botanist Two",
materialSamples: {
materialSampleType: "seed",
numberCollected: 72
}
}
]
//use bulkCreate with include to insert the data
const result = await Occurrence.bulkCreate(data, {
include: [{
model: MaterialSample
}]
})
.then(() => {
res.send()
})
.catch((err) => {
console.log(err);
})
}

Related

Insert data from javascript file into mongodb using graphql

I am new to using Graphql and MongoDB. I am trying to insert data from an existing javascript file where the data has been defined. I was trying to use a mutation in order to achieve this but I have no clue what I'm really doing. Any help would be nice.
const dotenv = require('dotenv');
dotenv.config();
const { ApolloServer, gql } = require('apollo-server');
const { MongoClient } = require('mongodb');
const items = require('./itemsListData');
const typeDefs = gql`
type Query {
items:[Item!]!
}
type Item{
id:ID!,
name:String!,
aisle:String!,
bay:String!,
price:Float!,
xVal:Int!,
yVal:Int!
}
type Mutation {
createItem(name: String!, aisle: String!): Item!
}
`;
console.log(items)
const resolvers = {
Query: {
items:() => items,
},
Item:{
id: ( { _id, id }) => _id || id,
},
Mutation: {
createItem: async(_, { name }, { db }) => {
// name:String!, bays:[Bay!]!, xStartVal:Int!, xEndVal:Int!, yStartVal:Int!, yEndVal:Int!
const newItem = {
items
}
// insert Item object into database
const result = await db.collection('Items').insert(newItem);
console.log("This is the result " + result);
return result.ops[0]; // first item in array is the item we just added
}
}
};
const start = async () => {
const client = new MongoClient("mongodb+srv://admin:admin#quickkartcluster.o0bsfej.mongodb.net/test", { useNewUrlParser: true, useUnifiedTopology: true });
await client.connect();
const db = client.db("QuickKart");
const context = {
db,
}
const server = new ApolloServer({
typeDefs,
resolvers,
context,
introspection: true
});
// The `listen` method launches a web server.
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
}
start();
here is my javascript data file
https://pastebin.com/wvGANBgR

Why could the findOrCreate plugin create several documents at once?

I'm currently working on a MERN application with following/follower function for the users. I decided to create separate schemas for following and follower relationships detached from user schema.
Follower schema
const mongoose = require('mongoose');
const findOrCreate = require('mongoose-findorcreate');
const ObjectId = mongoose.Schema.Types.ObjectId;
const followerSchema = mongoose.Schema({
userId: {
type: ObjectId,
ref: 'User'
},
follower: {
type: [ObjectId],
ref: 'User'
}
});
followerSchema.plugin(findOrCreate);
const Follower = mongoose.model('Follower', followerSchema);
module.exports = { followerSchema, Follower };
Following schema
const mongoose = require('mongoose');
const findOrCreate = require('mongoose-findorcreate');
const ObjectId = mongoose.Schema.Types.ObjectId;
const followingSchema = mongoose.Schema({
userId: {
type: ObjectId,
ref: 'User'
},
following: {
type: [ObjectId],
ref: 'User'
}
});
followingSchema.plugin(findOrCreate);
const Following = mongoose.model('Following', followingSchema);
module.exports = { followingSchema, Following };
The problem however starts in my service where followings are created as supposed to. However, for followers mongoose create 6-8 documents at once with userIds that don't even exist in my db.
Here's the code of the followerService (it's the first function)
const { Follower } = require('../models/followerModel');
const { Following } = require('../models/followingModel');
const { User } = require('../models/userModel');
const mongoose = require('mongoose');
exports.changeFollowStatus = async (user, userId) => {
console.log({ userId: userId, user: user._id });
const newFollowing = await Following.findOrCreate({ userId: user._id }, (err, following, created) => {
console.log({following: following});
if (!err && !isFollowing(following, userId)) {
following.following.push(mongoose.Types.ObjectId(userId));
following.save();
User.findByIdAndUpdate(mongoose.Types.ObjectId(userId), {$inc: {follower: 1}});
} else {
const followingIndex = following.following.indexOf(mongoose.Types.ObjectId(userId));
following.following.splice(followingIndex, 1);
following.save();
User.findByIdAndUpdate(mongoose.Types.ObjectId(userId), { $inc: { follower: -1 } });
}
});
const newFollower = await Follower.findOrCreate({ userId: mongoose.Types.ObjectId(userId) }, (err, follower, created) => {
console.log({follower: follower});
if (!err && !isFollower(follower, user._id)) {
follower.follower.push(user._id);
follower.save();
User.findByIdAndUpdate(user._id, { $inc: { following: 1 } });
} else {
const followerIndex = follower.follower.indexOf(user._id);
follower.follower.splice(followerIndex, 1);
follower.save();
User.findByIdAndUpdate(user._id, { $inc: { following: -1 } });
}
});
};
exports.showFollowings = async (userId) => {
const followings = await Following.findOrCreate({ userId: mongoose.Types.ObjectId(userId) });
return followings.following;
};
exports.showFollowers = async (userId) => {
const followers = await Follower.findOrCreate({ userId: mongoose.Types.ObjectId(userId) });
return followers.follower;
};
const isFollowing = (newFollowing, userId) => {
return newFollowing.following.includes(mongoose.Types.ObjectId(userId));
};
const isFollower = (newFollower, userId) => {
return newFollower.follower.includes(userId);
}
Now, my following adding code and follower adding code look almost identical, but for some reason for followers, mongoose creates many more documents. The strange thing is that there is a follower document with the correct userId, but many other with random userIds get created which doesn't happen with followings which works as supposed to.
I also checked whether I pass the correct values and everything seems to be fine. But just for reference, here's the controller function from which I pass the values.
exports.changeFollowingStatus = async (req, res, next) => {
passport.authenticate('jwt', async (err, user, info) => {
if (err) {
console.error({ authError: err });
};
if (info !== undefined) {
console.error({ authError: info.message });
res.status(403).send(info.message);
} else {
console.log({params: req.params});
const userToFollow = req.params.id;
await FollowerService.changeFollowStatus(user, userToFollow);
res.status(200).send({ message: 'success' })
}
})(req, res, next);
};
Could anyone help me with this bug or at least navigate me towards the right direction? I can't seem to find solution to my problem. Thank you all in advance!

How do I update a foreign key (belongsTo) in Sequelize

I have the following model:
'use strict';
const {Model} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Key extends Model {
static associate(models) {
Key.belongsTo(models.User, {
foreignKey: 'userId',
onDelete: 'CASCADE'
});
}
};
Key.init({
keyType: DataTypes.STRING,
key: DataTypes.JSON
}, {
sequelize,
modelName: 'Key',
});
return Key;
};
I then try to create a row, after receiving userId, keyType and key:
...
const Key = KeyModel(sequelize, Sequelize);
const createKey = async (userid, keyType, key) => {
const result = await Key.create({userId, keyType, key});
return result;
}
The row gets created successfully in the DB, and i get back an ID (the createdAt and updatedAt are updated as well), but the userId is null.
How should I pass it to the create method so the value gets to the DB? Am I missing something in the model?
PS: the DB is MySQL 8.
I think you should change your code like below.
'use strict';
const {Model} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Key extends Model {
static associate(models) {
Key.belongsTo(models.User, {
foreignKey: 'keyId',
targetKey: 'userId'
onDelete: 'CASCADE'
});
}
};
Key.init({
keyId: DataTypes.STRING,
keyType: DataTypes.STRING,
key: DataTypes.JSON
}, {
sequelize,
modelName: 'Key',
});
return Key;
};
const Key = KeyModel(sequelize, Sequelize);
const createKey = async (userId, keyType, key) => {
const result = await Key.create({keyId: userId, keyType, key});
return result;
}

Mongoose find by a subdocument's value

I have 2 schemas
const schema = Schema({
headLine: {
type: String,
required: false
},
availableDays: [{
type: Schema.Types.ObjectId,
ref: AvailableDay
}]
}, {collection: 'providers', timestamps: true});
module.exports = mongoose.model("Provider", schema);
const schema = Schema({
day: {
type: String,
enum: ['Mondays','Tuesdays','Wednesdays','Thursdays','Fridays','Saturdays','Sundays']
},
timeFrom: String,
timeTo: String
}, {collection: 'availableDays', timestamps: true});
module.exports = mongoose.model("AvailableDay", schema);
Then in a route I call to a repository like this
router.get('/', async (req, res) => {
const match = {};
const sort = {};
const options = {};
// Arrange sort
if(req.query.sortBy){
const sortArray = JSON.parse(req.query.sortBy);
sortArray.map(e => sort[e[0]] = e[1] && e[1] === 'desc' ? -1 : 1);
options['sort'] = sort
}
// Get the pagination: limit how many, skip where it starts
if(req.query.limit) {
options['limit'] = parseInt(req.query.limit);
}
if(req.query.skip) {
options['skip'] = parseInt(req.query.skip);
}
const docs = await ProviderRepository.findBy(match, {}, options);
res.status(200).json(docs)
});
So what I need here is to filter providers for an AvailableDay monday and return the docs and count the total docs for pagination. I'm doing something like this without success
const findBy = async (params, projection = "", options = {}, callback) => {
const data = () => {
Provider.find(params, projection, options)
.populate([{path: 'user', match: {gender: 'F'}}]).exec((error, e) => {
if (error) {
console.log('error:', error)
return {error: error}; // returns error in json
}
return e.filter(i => i.user);
});
};
const total = await Provider.countDocuments(params).exec();
return {data(), total}
}
Thanks in advance
Use mongoose-aggregate-paginate-v2 and update your schema. If you use that package then you have to convert your queries from populate to aggregate style.
STEP 1: Update schema. Sample Schema:
const mongoose = require('mongoose');
const mongoosePaginate = require('mongoose-aggregate-paginate-v2');
const Schema = mongoose.Schema;
let definition = {
headLine: {
type: String,
required: false
},
availableDays: [{
type: Schema.Types.ObjectId,
ref: AvailableDay
}]
};
let options = {
collection: 'providers'
};
let providerSchema = new Schema(definition, options);
providerSchema.plugin(mongoosePaginate);
module.exports = mongoose.model('providers', providerSchema);
STEP 2: Update controller. Sample code in controller:
router.get('/', async (req, res) => {
const match = {}
const sort = {
// Fill it based on your sort logic.
}
const paginateOptions = {
page: req.query.page, // Page number like: 1, 2, 3...
limit: req.query.limit // Limit like: 10, 15, 20...
};
ProviderRepository
.findBy(match, {}, sort, paginateOptions)
.then(() => {
res.status(200).json(docs)
})
.catch(() => {
res.status(HTTP_ERROR_CODE).json({ "error": "Your error message" })
})
});
STEP 3: Update manager. Sample code in manager:
const findBy = (match, projection, sort, paginateOptions) => {
if (!paginateOptions) {
paginateOptions = {
pagination: false
};
}
let providerAggregate = providerSchema.aggregate([
{
$lookup: {
from: "availableDays",
let: { days: "$availableDays" },
pipeline: [
{
$match: {
$expr: {
$in: ["$$availableDays", "$day"]
}
}
}
],
as: "availableDays"
}
},
{
$lookup: {
from: "users", // I dont know the collection name
let: { user_id: "$user" }
pipeline: [
{
$match: {
"gender": 'F',
$expr: {
$eq: ["$_id", "$$user_id"]
}
}
}
],
as: "users"
}
}
{ $sort: sort }
]);
return providerSchema
.aggregatePaginate(providerAggregate, paginateOptions)
.then(res => {
return res;
})
.catch(err => {
throw err;
});
};

GraphQL server with Deno

It works just once for the below code
import {
graphql,
GraphQLSchema,
GraphQLObjectType,
GraphQLString,
buildSchema,
} from "https://cdn.pika.dev/graphql/^15.0.0";
import { serve } from "https://deno.land/std#0.50.0/http/server.ts";
var schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: "RootQueryType",
fields: {
hello: {
type: GraphQLString,
resolve() {
return "world";
},
},
},
}),
});
var query = "{ hello }";
graphql(schema, query).then((result) => {
console.log(result);
});
How to keep it listening, just like express
Something like this
var express = require('express');
var graphqlHTTP = require('express-graphql');
var { buildSchema } = require('graphql');
// Construct a schema, using GraphQL schema language
var schema = buildSchema(`
type Query {
hello: String
}
`);
// The root provides a resolver function for each API endpoint
var root = {
hello: () => {
return 'Hello world!';
},
};
var app = express();
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
}));
app.listen(4000);
console.log('Running a GraphQL API server at http://localhost:4000/graphql');
import {
graphql,
buildSchema,
} from "https://cdn.pika.dev/graphql/^15.0.0";
import {Application, Router} from "https://deno.land/x/oak/mod.ts";
var schema = buildSchema(`
type Query {
hello: String
}
`);
var resolver = {hello: () => 'Hello world!'}
const executeSchema = async (query:any) => {
const result = await graphql(schema, query, resolver);
return result;
}
var router = new Router();
router.post("/graph", async ({request, response}) => {
if(request.hasBody) {
const body = await request.body();
const result = await executeSchema(body.value);
response.body = result;
} else {
response.body = "Query Unknown";
}
})
let app = new Application();
app.use(router.routes());
app.use(router.allowedMethods());
console.log("Server running");
app.listen({port: 5000})
You can now use https://deno.land/x/deno_graphql to achieve this goal.
It provides everything needed out-of-the-box and works with multiple Deno frameworks (oak, abc, attain, etc).
This is how you code looks like (with oak for example):
import { Application, Context, Router } from "https://deno.land/x/oak/mod.ts";
import {
gql,
graphqlHttp,
makeExecutableSchema,
} from "https://deno.land/x/deno_graphql/oak.ts";
const typeDefs = gql`
type Query {
hello: String
}
`;
const resolvers = {
Query: {
hello: () => "Hello world!",
},
};
const context = (context: Context) => ({
request: context.request,
});
const schema = makeExecutableSchema({ typeDefs, resolvers });
const app = new Application();
const router = new Router();
router.post("/graphql", graphqlHttp({ schema, context }));
app.use(router.routes());
await app.listen({ port: 4000 });
PS : i'm the author of the package, so you can ask me anything.
Hope this helps!
Here is an example using oak working with your GraphQL code.
First let's say you have a repository graphRepository.ts with your graph schema:
import {
graphql,
GraphQLSchema,
GraphQLObjectType,
GraphQLString
} from "https://cdn.pika.dev/graphql/^15.0.0";
var schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: "RootQueryType",
fields: {
hello: {
type: GraphQLString,
resolve() {
return "world";
},
},
},
}),
});
export async function querySchema(query: any) {
return await graphql(schema, query)
.then(async (result) => {
return result;
});
}
Now start your app.ts listener with the routes, and use the following URL to call the endpoint:
http://localhost:8000/graph/query/hello
import { Application, Router } from "https://deno.land/x/oak/mod.ts";
import { querySchema } from "./graphRepository.ts";
const router = new Router();
router
.get("/graph/query/:value", async (context) => {
const queryValue: any = context.params.value;
const query = `{ ${queryValue}}`
const result = await querySchema(query);
console.log(result)
context.response.body = result;
})
const app = new Application();
app.use(router.routes());
app.use(router.allowedMethods());
await app.listen({ port: 8000 });
here is a code example using oak and middleware.
You also can enjoy the playground GUI like an apollo one.
import { Application } from "https://deno.land/x/oak/mod.ts";
import { applyGraphQL, gql } from "https://deno.land/x/oak_graphql/mod.ts";
const app = new Application();
app.use(async (ctx, next) => {
await next();
const rt = ctx.response.headers.get("X-Response-Time");
console.log(`${ctx.request.method} ${ctx.request.url} - ${rt}`);
});
app.use(async (ctx, next) => {
const start = Date.now();
await next();
const ms = Date.now() - start;
ctx.response.headers.set("X-Response-Time", `${ms}ms`);
});
const types = gql`
type User {
firstName: String
lastName: String
}
input UserInput {
firstName: String
lastName: String
}
type ResolveType {
done: Boolean
}
type Query {
getUser(id: String): User
}
type Mutation {
setUser(input: UserInput!): ResolveType!
}
`;
const resolvers = {
Query: {
getUser: (parent: any, {id}: any, context: any, info: any) => {
console.log("id", id, context);
return {
firstName: "wooseok",
lastName: "lee",
};
},
},
Mutation: {
setUser: (parent: any, {firstName, lastName}: any, context: any, info: any) => {
console.log("input:", firstName, lastName);
return {
done: true,
};
},
},
};
const GraphQLService = applyGraphQL({
typeDefs: types,
resolvers: resolvers
})
app.use(GraphQLService.routes(), GraphQLService.allowedMethods());
console.log("Server start at http://localhost:8080");
await app.listen({ port: 8080 });
I have created gql for making GraphQL servers that aren't tied to a web framework. All of the responses above show Oak integration but you don't really have to use it to have a GraphQL server. You can go with std/http instead:
import { serve } from 'https://deno.land/std#0.90.0/http/server.ts'
import { GraphQLHTTP } from 'https://deno.land/x/gql/mod.ts'
import { makeExecutableSchema } from 'https://deno.land/x/graphql_tools/mod.ts'
import { gql } from 'https://deno.land/x/graphql_tag/mod.ts'
const typeDefs = gql`
type Query {
hello: String
}
`
const resolvers = {
Query: {
hello: () => `Hello World!`
}
}
const schema = makeExecutableSchema({ resolvers, typeDefs })
const s = serve({ port: 3000 })
for await (const req of s) {
req.url.startsWith('/graphql')
? await GraphQLHTTP({
schema,
graphiql: true
})(req)
: req.respond({
status: 404
})
}

Categories

Resources