Related
I am trying to update a row in a given table using the following Sequelize query:
const handleEditProfile = async (req, res) => {
let medicalconditions = req.body.medicalconditions;
let allergies = req.body.allergies;
let bloodtype = req.body.bloodtype;
let weight = req.body.weight;
let height = req.body.height;
let userId = req.body.userid;
Profile.bulkCreate(
[{userId: userId, medicalconditions: medicalconditions, allergies: allergies, bloodtype: bloodtype, weight: weight, height: height}],
{updateOnDuplicate: ['medicalconditions', 'allergies', 'bloodtype', 'weight', 'height']}
)
.then(data => {
res.send(data);
})
.catch(err => {
res.status(400).send(err);
})
};
This is what I get in the Postgres DB:
id | medicalconditions | allergies | bloodtype | weight | height | createdAt | updatedAt | userId
----+-----------------------------+--------------------+-----------+---------+------------+----------------------------+----------------------------+--------
74 | type 2 diabetes | penicillin | AB | 114 lbs | | 2023-02-13 14:56:21.789-06 | 2023-02-13 14:56:21.789-06 | 40
90 | hyperthyroidism | none | AB | 120 lbs | 5'4" | 2023-02-17 18:08:44.503-06 | 2023-02-17 18:08:44.503-06 | 40
As you can see, I looked up the "row" by the userId. I want to update the columns mentioned in "updateOnDuplicate" where userId = 40. However, instead of updating the row with the id = 74 and userId = 40, it creates a new row with the id = 90 and userId = 40. How can I fix this without mentioning the primary id?
const User = sequelize.define('user', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
uid: {
type: DataTypes.TEXT,
allowNull: false,
unique: true
},
firstName: {
type: DataTypes.TEXT,
allowNull: false
},
lastName: {
type: DataTypes.TEXT,
allowNull: false
},
DOB: {
type: DataTypes.TEXT,
allowNull: false
}
});
const Profile = sequelize.define('profile', {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true
},
medicalconditions: {
type: DataTypes.TEXT
},
allergies: {
type: DataTypes.TEXT
},
bloodtype: {
type: DataTypes.TEXT
},
weight: {
type: DataTypes.TEXT
},
height: {
type: DataTypes.TEXT
}
});
User.hasOne(Profile);
Profile.belongsTo(User);
If you want Profile records to be unique not only by PK but by userId as well you need an unique index/constraint in Profile table on userId. That way if you don't indicate PK in objects that about to be inserted PostgreSQL will look at fields values that correspond to any unique constraint to decide whether a record with such values already exists or doesn't.
Another benefit of having the unique index on userid that you can't create two records with the same userId even if you use an individual create calls.
I have a mariadb database and connect to it with their connector through nodejs. I send a select query. When I log the returned values i get this:
[
{
id: 1,
heading: 'Test Heading',
url: 'test-heading',
content: '#This is Test Content!',
author: 'Max Mustermann'
},
meta: [
ColumnDef {
_parse: [StringParser],
collation: [Collation],
columnLength: 11,
columnType: 3,
flags: 16899,
scale: 0,
type: 'LONG'
},
ColumnDef {
_parse: [StringParser],
collation: [Collation],
columnLength: 1020,
columnType: 253,
flags: 4097,
scale: 0,
type: 'VAR_STRING'
},
ColumnDef {
_parse: [StringParser],
collation: [Collation],
columnLength: 1020,
columnType: 253,
flags: 20485,
scale: 0,
type: 'VAR_STRING'
},
ColumnDef {
_parse: [StringParser],
collation: [Collation],
columnLength: 4294967295,
columnType: 252,
flags: 4113,
scale: 0,
type: 'BLOB'
},
ColumnDef {
_parse: [StringParser],
collation: [Collation],
columnLength: 200,
columnType: 253,
flags: 0,
scale: 0,
type: 'VAR_STRING'
}
]
]
I interpret this as a list of objects which are the found entries in the database. But why can there be a key value pair right behind that (the meta key) without needing curly braces around it? When I try to store this exact structure to a variable by hand I get an error. The documentation says the return type is a JSON object. Can someone please explain what is going on here? Thanks (Guesses are welcome as well)
Here is my Code:
const config = require("./config.js");
const mariadb = require("mariadb");
const pool = mariadb.createPool({
host: config.db_host,
user: config.db_user,
password: config.db_password,
connectionLimit: 5,
database: config.db_name
});
pool.getConnection().then(conn => {
console.log("Connected to MariaDB Database");
conn.query("select * from BlogEntries where url=(?);"["test-heading"]).then((rows)=>{
console.log(rows)
conn.end();
}).catch((err)=>{
console.log(err);
conn.end();
})
}).catch(err => {
console.log("Failed to connect to MariaDB Database:",err);
})
config.js exports the ports etc (that works) otherwise really nothing special here. My Database looks like this (url is unique and id is the primary key):
MariaDB [Paulemeister]> select * from BlogEntries;
+----+-----------------+--------------+------------------------+----------------+
| id | heading | url | content | author |
+----+-----------------+--------------+------------------------+----------------+
| 1 | Test Heading | test-heading | #This is Test Content! | Max Mustermann |
| 2 | Minimal Heading | bare-minimum | | NULL |
+----+-----------------+--------------+------------------------+----------------+
According to the MariaDB documentation this is the data key/value pairs you are looking for:
{
id: 1,
heading: 'Test Heading',
url: 'test-heading',
content: '#This is Test Content!',
author: 'Max Mustermann'
},
The meta section is reference to the database column data types.
Data rows are rows: [ {val: 1}, meta: ... ] where the first element of each row array is the data, the second is the meta information. Perhaps this will be of assistance: https://mariadb.com/kb/en/connector-nodejs-callback-api/#result-set-array
It appears that the nodejs library has a node event structure for retrieving the data. An example from the url referenced above:
connection.query("SELECT * FROM mysql.user")
.on("error", err => {
console.log(err); //if error
})
.on("fields", meta => {
console.log(meta); // [ ... ]
})
.on("data", row => {
console.log(row);
})
.on("end", () => {
//ended
});
use the .on('data', row => {... to put all your data rows in an array and work with them from there.
What do you mean by this?
When I try to store this exact structure to a variable by hand I get an error.
I want to retrieve users post by users logged in Id with following
conditions. As currently I am getting user-post as null.
1: If user posted a post. same user should be able to view his post
(if same user is not friend with anyone i.e ACCEPTED status).
2: If user posted a post, other users with whom he is friend should be
able to view post.
Basically I have used following database design,
1: Users id, first Name, last Name
2: Post id, post
3: UserPost
id,
postId, (referencing Post table),
userId (referencing User table)
4: Friendship id, userId, (referencing User table) friendUserId,
(referencing User table) friendshipStatus (e.g values ACCEPTED,
REJECTED)
Following is associations and query to retrieve the records schema,
1: User association
const Users = sequelize.define(
'Users',
{
id: {
type: DataTypes.BIGINT,
allowNull: false,
primaryKey: true,
autoIncrement: true,
field: 'id'
},
firstName: {
type: DataTypes.STRING(20),
allowNull: true,
field: 'firstName'
},
lastName: {
type: DataTypes.STRING(20),
allowNull: true,
field: 'lastName'
},
{
tableName: 'users'
}
);
Users.associate = models => {
Users.hasMany(models.Friendships, { foreignKey: 'userId', as: 'friendship'});
Users.hasMany(models.UserPost, { foreignKey: 'userId', as: 'user-post'
});
}
2: Friendship association
const Friendships = sequelize.define(
'Friendships',
{
id: {
type: DataTypes.BIGINT,
allowNull: false,
primaryKey: true,
autoIncrement: true,
field: 'id'
},
userId: {
type: DataTypes.BIGINT,
allowNull: false,
references: {
model: 'users',
key: 'id'
},
field: 'userId'
},
friendUserId: {
type: DataTypes.BIGINT,
allowNull: false,
references: {
model: 'users',
key: 'id'
},
field: 'friendUserId'
},
friendshipStartDate: {
type: DataTypes.DATE,
allowNull: true,
field: 'friendshipStartDate'
},
friendshipStatusId: {
type: DataTypes.STRING(20),
allowNull: true,
field: 'friendshipStatusId'
},
},
{
tableName: 'friendships'
}
);
Friendships.associate = models => {
Friendships.belongsTo(models.UserPost, { foreignKey: 'userId', as: 'user-post' });
Friendships.belongsTo(models.Users, { foreignKey: 'id', as: 'user' });
};
3: Post association
const Post = sequelize.define(
'Post',
{
id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true,
field: 'id'
},
post: {
type: DataTypes.STRING(1000),
allowNull: false,
// defaultValue: '0',
field: 'Post'
},
postStatusId: {
type: DataTypes.INTEGER(10),
allowNull: true,
field: 'PostStatusId'
},
createdBy: {
type: DataTypes.INTEGER(10),
allowNull: true,
field: 'CreatedBy'
},
updatedBy: {
type: DataTypes.INTEGER(10),
allowNull: true,
field: 'ModifiedBy'
}
},
{
tableName: 'posts',
updatedAt: false,
createdAt: false
}
);
Post.associate = models => {
Post.belongsTo(models.Friendships, { foreignKey:'userId', as: 'friendship' });
Post.belongsTo(models.UserPost, { foreignKey: 'postId', as: 'userspost' });
Post.belongsTo(models.Users, { foreignKey: 'id', as: 'user' });
};
4: UserPost association
const UserPost = sequelize.define(
'UserPost',
{
id: {
type: DataTypes.INTEGER(11),
allowNull: false,
primaryKey: true,
autoIncrement: true,
field: 'id'
},
userId: {
type: DataTypes.STRING(1000),
allowNull: false,
references: {
model: 'user',
key: 'id'
},
field: 'userId'
},
postId: {
type: DataTypes.INTEGER(10),
allowNull: true,
references: {
model: 'posts',
key: 'id'
},
field: 'postId'
}
},
{
tableName: 'user_posts',
updatedAt: false,
createdAt: false
}
);
UserPost.associate = models => {
UserPost.hasMany(models.Post, { sourceKey:'postId', foreignKey: 'id', as: 'post' });
UserPost.hasMany(models.Friendships, { foreignKey:'userId', as: 'friend'
});
};
Sequlize query
const result = await Users.findAll({
attributes: ['id', 'firstName', 'lastName'],
subQuery: false,
include: [
{
model: Friendships,
as: 'friendship',
attributes: ['userId', 'friendUserId'],
where:
{
[Op.or]: {
userId: userId,
friendUserId: userId
},
[Op.and]: {
friendshipStatusId: 'ACCEPT'
}
},
include: [{
model: UserPost,
as: 'user-post',
attributes: ['userId', 'postId'],
subQuery: false,
include: [
{
model: Post,
as: 'post',
attributes:['id','post'],
}
],
}]
}
] })
But in this query I am not getting users post records since there is
one entry in database of user post. following is actual result for
above query.
Response
[
{
"id": 5,
"firstName": "Abc",
"lastName": "Xyz",
"friendship": [
{
"userId": 5,
"friendUserId": 6,
"user-post": null
}
]
}
]
do anyone having solution for this?
Just to make sure we're on the same page, from features perspective you want users to be able to be friends with each others and posts created by a user can only be seen by that user and his friend.
From the relations perspective by UserPost you want to get all the users who have access to a post.
If this is correct then the way to go about it is much simpler.
First you don't need a many-to-many relation between user and post(UserPost) since you don't need many users to belong to a post unless a post can have multiple creators.
From raw database tables perspective we want(minimal):
User:
+-----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+-------------+------+-----+---------+----------------+
| id | int | NO | PRI | NULL | auto_increment |
| firstName | varchar(20) | YES | | NULL | |
| lastName | varchar(20) | YES | | NULL | |
| createdAt | datetime | NO | | NULL | |
| updatedAt | datetime | NO | | NULL | |
+-----------+-------------+------+-----+---------+----------------+
Friendship:
+-----------+-------------------------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+-------------------------------------+------+-----+---------+----------------+
| id | int | NO | PRI | NULL | auto_increment |
| status | enum('PENDING','ACCEPTED','DENIED') | NO | | NULL | |
| createdAt | datetime | NO | | NULL | |
| updatedAt | datetime | NO | | NULL | |
| user_1 | int | YES | MUL | NULL | |
| user_2 | int | YES | MUL | NULL | |
+-----------+-------------------------------------+------+-----+---------+----------------+
Post:
+-----------+--------------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+--------------------------+------+-----+---------+----------------+
| id | int | NO | PRI | NULL | auto_increment |
| content | varchar(1000) | NO | | NULL | |
| status | enum('ACTIVE','DELETED') | NO | | NULL | |
| createdAt | datetime | NO | | NULL | |
| updatedAt | datetime | NO | | NULL | |
| UserId | int | YES | MUL | NULL | |
+-----------+--------------------------+------+-----+---------+----------------+
Which in sequelize can be done with:
const User = sequelize.define('User',{
firstName: {
type: DataTypes.STRING(20),
allowNull: true,
},
lastName: {
type: DataTypes.STRING(20),
allowNull: true,
}
})
User.associate = models => {
User.hasMany(models.Friendship, { foreignKey: "user_1" });
User.hasMany(models.Friendship, { foreignKey: "user_2" });
};
const Post = sequelize.define('Post',{
content: {
type: DataTypes.STRING(1000),
allowNull: false,
},
status: {
type: DataTypes.ENUM("ACTIVE", "DELETED"),
allowNull: false,
default: "ACTIVE"
}
}
);
Post.associate = models => {
Post.belongsTo(models.User);
};
const Friendship = sequelize.define('Friendship', {
status: {
type: DataTypes.ENUM("PENDING", "ACCEPTED", "DENIED"),
allowNull: false,
default: "PENDING"
}
});
Usage:
const models = require("./models")
const { Op } = require("sequelize");
const { User, Friendship, Post } = models;
const requestPost = async (user_id,post_id) => {
let user = await User.findOne({ where: { id: user_id } })
let post = await Post.findOne({ where: { id: post_id }, include: [User] })
let isFriend = await Friendship.findOne({
where: {
[Op.or]: [
{
user_1: user.id,
user_2: post.User.id,
status: "ACCEPTED"
},
{
user_1: post.User.id,
user_2: user.id,
status: "ACCEPTED"
}
]
}
})
if (isFriend) return post;
}
let run = async () => {
await models.sequelize.sync({ force: true });
let user1 = await User.create({
firstName: "dummy1",
lastName: "dummy1"
})
let user2 = await User.create({
firstName: "dummy2",
lastName: "dummy2"
})
let user3 = await User.create({
firstName: "dummy3",
lastName: "dummy3"
})
let user1_user2_friendship_accepted = await Friendship.create({
user_1: user1.id,
user_2: user2.id,
status: "ACCEPTED"
})
let user1_user3_friendship_pending = await Friendship.create({
user_1: user1.id,
user_2: user3.id,
})
let user1_post = await Post.create({
UserId: user1.id,
content: "Dummy-content"
})
// Example 1:
// user2 wants to access user1_post:
let getPost1 = await requestPost(user2.id, user1_post.id) // returns post
// Example 2:
// user3 wants to access user1_post:
let getPost2 = await requestPost(user3.id, user1_post.id) // returns undefined
console.log("\n\n\n\n")
if (getPost1) console.log("Returned getPost1")
if (getPost2) console.log("Returned getPost2")
}
run()
I have 3 models. User, Item and Location.
User.hasMany(Item)
Location.hasMany(Item)
Item.belongsTo(User)
Item.belongsTo(Location)
this runs, but does not create the location foreign key nor location row in locations table.
models.User.create(
{
username: 'ddavids',
email: 'hello#david.com',
password: '12345678',
items: [
{
itemName: 'Good Book',
orderDate: '2020-01-20',
locations: { locationName: 'floor' }
},
{
itemName: 'Bad Book',
orderDate: '2020-01-21',
locations: { locationName: 'shelf' }
}
]
},
{
include: [{ model: models.Item, include: [models.Location] }]
}
)
This creates the items and locations correctly but obviously not under a user.
models.Location.create(
{
locationName: 'floor',
items: [
{
itemName: 'Good Book',
orderDate: '2020-01-20',
locations: { locationName: 'floor' }
},
{
itemName: 'Bad Book',
orderDate: '2020-01-21',
locations: { locationName: 'shelf' }
}
]
},
{ include: [models.Item] }
)
What I can't figure out is if my relations are the wrong way to go about this or if its a limitation of create and I should move on or what.
My end goal will be something along the lines of.
User.hasMany(Order)
Order.belongsTo(User)
Order.hasMany(Item)
Item.belongsTo(Order)
Location.hasMany(Item)
Item.belongsTo(location)
Supplier.hasMany(Item)
Item.belongsTo(Supplier)
I am currently using create just to create some fake data for when I make changes. So if there is a better way to seed the database that would be my end goal.
Because of the association of Location and Item models is one-to-many. So you should change the
locations: { locationName: 'floor' } to location: { locationName: 'floor' }. The location of Item should not be plural. It means each item belongs to one location.
Here is the working example:
index.ts:
import { sequelize } from '../../db';
import { Model, DataTypes } from 'sequelize';
class User extends Model {}
User.init(
{
username: DataTypes.STRING,
email: DataTypes.STRING,
password: DataTypes.STRING,
},
{ sequelize, modelName: 'user' },
);
class Location extends Model {}
Location.init(
{
locationName: DataTypes.STRING,
},
{ sequelize, modelName: 'location' },
);
class Item extends Model {}
Item.init(
{
itemName: DataTypes.STRING,
orderDate: DataTypes.STRING,
},
{ sequelize, modelName: 'item' },
);
User.hasMany(Item);
Location.hasMany(Item);
Item.belongsTo(User);
Item.belongsTo(Location);
(async function test() {
try {
await sequelize.sync({ force: true });
await User.create(
{
username: 'ddavids',
email: 'hello#david.com',
password: '12345678',
items: [
{
itemName: 'Good Book',
orderDate: '2020-01-20',
location: { locationName: 'floor' },
},
{
itemName: 'Bad Book',
orderDate: '2020-01-21',
location: { locationName: 'shelf' },
},
],
},
{
include: [{ model: Item, include: [Location] }],
},
);
} catch (error) {
console.log(error);
} finally {
await sequelize.close();
}
})();
After execution above code, check the data records in the database:
node-sequelize-examples=# select * from "user";
id | username | email | password
----+----------+-----------------+----------
1 | ddavids | hello#david.com | 12345678
(1 row)
node-sequelize-examples=# select * from "location";
id | locationName
----+--------------
1 | floor
2 | shelf
(2 rows)
node-sequelize-examples=# select * from "item";
id | itemName | orderDate | userId | locationId
----+-----------+------------+--------+------------
1 | Good Book | 2020-01-20 | 1 | 1
2 | Bad Book | 2020-01-21 | 1 | 2
(2 rows)
Data records inserted as expected.
sequelize version: "sequelize": "^5.21.3",
Source code: https://github.com/mrdulin/node-sequelize-examples/tree/master/src/examples/stackoverflow/59937776
This might be a silly question but why on earth am I getting an "Unexpected token" error for the following code snippet? Bear in mind it is a mongoose model.
Error message
SyntaxError: D:/Coding/Species Project/backend/models/species.js: Unexpected token (15:2)
13 | },
14 | organism: {
> 15 | ...shared,
| ^
16 | enum: ["Plant", "Animal", "Other"],
17 | },
18 | taxonomy: {
Mongoose model
const shared = {
type: String,
required: true,
}
const SpeciesSchema = new Schema({
name: {
common: shared,
scientific: shared,
},
organism: {
...shared,
enum: ["Plant", "Animal", "Other"],
},
...,
}
Spread operator is used as parameters from array.
You might want to use this, instead
const SpeciesSchema = new Schema({
name: {
common: shared,
scientific: shared,
},
organism: Object.assign({}, shared, enum: ["Plant", "Animal", "Other"]},
...,
}