I was wondering how I can solve a many to many solutions with migrations like at this diagram:
DB relation diagram
u_id_user is a FK from the User table
u_g_id is unique and auto-increment
g_id_group is a FK from Group
Code example:
class UserGroupsSchema extends Schema {
up () {
this.create('user_groups', (table) => {
table.increments()
table.integer('u_id_user')
table.integer("g_id_group")
table.timestamps()
})
}
An other problem is that if I run my migrations it creates the tables with type MyISAM and I cant build relations with this type.. so how can I change the default type to InnoDB?
From the adonis.js documentation you need to define your models like this:
const Model = use('Model')
class User extends Model {
groups () {
return this.belongsToMany('App/Models/Group')
}
}
module.exports = User
After that you can query the date like this:
const user = await User.find(1)
const groups = await user
.groups()
.wherePivot('user_id', '=', user.id)
.fetch()
I haven't tested the code but this should put you on the right track
Related
I'm struggling with queries over many-to-many relation using typeorm.
I have two entities: users and groups, both having many-to-many relations to each other.
User entity has this:
#ManyToMany(() => Group, group => group.users)
#JoinTable()
groups: Group[];
Group entity has this:
#ManyToMany(() => User, user => user.groups)
users: User[];
I want to get all groups of a specified user, that have a certain value. In my case, I want all groups for user X that have ishidden = false. So I tried diffentent things, but none work...
const groups = this.groupRepository.find({
relations: ['users'],
where: {
ishidden: false,
users: {
id: user.id
}
}
});
I was really hoping that it would work this way, but it just throws Cannot query across many-to-many for property users. Other approaches with querybuilder also didnt work :-(
I can't be that hard the query a related table, can it? I'm coming from the C# world, so maybe I'm just doing it wrong ;-)
Recently faced a similar problem(nestjs + ts + typeorm). Try something like this:
import { Connection } from 'typeorm';
// some code
constructor(private connection: Connection) {}
// some code
await this.connection
.getRepository(Group) // group entity
.createQueryBuilder('groups') // table name
.leftJoin('groups.users', 'users') // join group table and user table
.where('ishidden = false AND users.id = :userId ', {
userId: userId,
})
.getMany();
Okay so, I'm displaying a friends table with Sequelize in Nodejs, Everything goes to plan but I run into a problem. For the friends table i store the user ids and then access the user data with those ids then display it onto ejs. I want to access another table using the current tables data.
Heres the code to access the Friends table, I made it a function so i can access it from ejs
let getFriends = async (id) => {
const project = await database.Friends.findAndCountAll({ where: { fid: id } });
return project.rows
}
you can access another table using include.
new code will be
let getFriends = async (id) => {
const project = await database.Friends.findAndCountAll({ where: { fid: id },
include:[{
model:model.User,as:'user'}]
});
return project;
}
TL;DR: Is there a safe way to dynamically define a mongoose discriminator at runtime?
I have an app with a MongoDB collection where users have some control over the underlying schema.
I could add one or two fixed, required fields and just use mongoose.Mixed for the remainder that users can change, but I'd like to make use of Mongoose's validation and discriminators if I can.
So, what I've got is a second collection Grid where the users can define the shape they'd like their data to take, and in my main model Record, I've added a function to dynamically generate a discriminator from the definition in the second collection.
The code for my Record model looks like this:
const mongoose = require("mongoose")
const recordSchema = new mongoose.Schema({
fields: {
type: Array,
required: true
}
}, {
discriminatorKey: "grid"
})
const Record = mongoose.model("Record", recordSchema)
module.exports = grid => {
// Generate a mongoose-compatible schema from the grid's field definitions
const schema = grid.fields.map(field => {
if(field.type === "string") return { [field.name]: String }
if(field.type === "number") return { [field.name]: Number }
if(field.type === "checkbox") return { [field.name]: Boolean }
return { [field.name]: mongoose.Mixed }
})
return Record.discriminator(grid._id, new mongoose.Schema(schema))
}
This is inside an Express app, and I use the model in my middleware handlers something like this:
async (req, res) => {
const grid = await Grid.findById(req.params.id)
const Record = await GenerateRecordModel(grid)
const records = await Record.find({})
res.json({
...grid,
records
})
}
This works great on the first request, but after that I get an error Discriminator with name “ ” already exists.
I guess this is because only one discriminator with its name per model can exist.
I could give every discriminator a unique name whenever the function is called:
return Record.discriminator(uuidv4(), new mongoose.Schema(schema), grid._id)
But I imagine that this isn't a good idea because discriminators seem to persist beyond the lifetime of the request, so am I laying the groundwork for a memory leak?
I can see two ways forward:
COMPLICATED? Define all discriminators when the app boots up, rather than just when a HTTP request comes in, and write piles of extra logic to handle the user creating, updating or deleting the definitions over in the Grid collection.
SIMPLER? Abandon using discriminators, just use mongoose.Mixed so anything goes as far as mongoose is concerned, and write any validation myself.
Any ideas?
I am creating two models, the first one is User and the second one is Portfolio. When a user is creating a Portfolio (where a user can only have one Portfolio), I want it to have reference to the user who is creating it, and every time that user's data is fetched, I want it to also fetching their portfolio data if any.
I am trying to use hasOne to create portfolio_id inside User tables, with the skeleton generated using sequelize init command, but it is not working. I cannot find a column the name protfolio_id if I don't put it inside the user migration file. Is that how it is supposed to be?
How should I design the models? Should I include the portfolio_id in User tables and include user_id in Portfolio table, or is there a best way to do it?
And which associations method should I use, hasOne or belongsTo?
First of all make sure that you are calling Model.associate for each model. This will run queries for all the relationships.
You can define the relationships in the associate method as follows:
// user.js (User Model definition)
module.exports = (sequelize, dataTypes) => {
const { STRING } = dataTypes
const User = sequelize.define("user", {
username: { type: STRING }
})
User.associate = models => {
User.hasOne(models.Portfolio, { foreignKey: "userId" }) // If only one portfolio per user
User.hasMany(models.Portfolio) // if many portfolios per user
}
return User
}
// portfolio.js (Portfolio Model definition)
module.exports = (sequelize, dataTypes) => {
const { STRING } = dataTypes
const Portfolio = sequelize.define("portfolio", {
portfolioName: { type: STRING }
})
Portfolio.associate = models => {
Portfolio.belongsTo(models.User, { foreignKey: "userId" })
}
return Portfolio
}
hasOne stores the foreignKey in the target model. So this relationship will add a foreign key userId to the Portfolio model.
belongsTo stores the key in the current model and references the primary key of the target model. In this case the Portfolio.belongsTo will add userId in the Portfolio model which will reference the primary key of User model.
Notice how both these relationships do the same thing, they add userId to the Portfolio model. Its better to define this in both models for your last use case:
I want it to have reference to the user who is creating it, and every time that user's data is fetched, I want it to also fetching their portfolio data if any.
Accessing related models:
In sequelize fetching a related model together with the main model is called Eager Loading. Read more about it here.
Now for your use case if you want to fetch the portfolio if any, while fetching user, do the following:
var userWithPortfolio = await User.findAll({include: [models.Portfolio]};
// Or you may also use include: {all: true} to include all related models.
var userWithPortfolio = await User.findAll({include: {all: true}};
/*
Output:
userWithPortfolio = {
username: "xyz",
portfolio: {
portfolioName: "xyz"
}
}
*/
I have a sequelize model called Activity, that belongs to another model called User.
I want to be able to generate a dynamic property on the Activity model, so I did the following:
class Activity extends Sequelize.Model {
toJSON() {
return {
...this.dataValues,
my_property: 'its value', // Dynamic property
};
}
}
This works quite fine when I query the Activity itself (or get them all), but the property is not there when I query a User with its activities e.g.
const user = await User.findByPk('some-pk-here', { include: [Activity] });
Can this behavior be changed, and if yes then how?