Sequelize Polymorphic HasMany/HasOne through Join Table - postgresql

I have a Customer table, an Address table and a join table named CustomerAddress.
The customer needs a Shipping Address, a Billing Address, a Default Address, and all of the customer's addresses need to be accessible in a field addresses, this is because Address table is polymorphic.
I would like to do this without having a target key or foreign/target key in the Customer table or Address table, and rely on the CustomerAddress table to specify which address is which, this is what I have for the join table:
//CustomerAddress
schema = {
address_id: {
type: Sequelize.INTEGER,
unique: 'customeraddress_address'
},
// Type of address eg. shipping, default, billing
address: {
type: Sequelize.STRING,
unique: 'customeraddress_address'
},
customer_id: {
type: Sequelize.INTEGER,
unique: 'customeraddress_address',
references: null
}
}
I'm not able to figure out how the associations need to be set up to accomplish this. Any help is greatly appreciated.

The way to do this is actually through a belongsToMany association where you imply that it's a belongsTo. This way it uses the join table be default. The difference being that it returns an array instead of an object, which defeats the point. A compromise could be that when overriding the instanceMethod toJSON, you morph the array into an object, or make a custom setter/getter that does this when the association is fetched.

Related

One way one to many relation

I have a Recipe and a Tag model. Currently, the recipe contains an array of id's belonging to Tag:
#Entity()
export class Recipe extends BaseEntity {
#PrimaryGeneratedColumn('uuid')
public id!: string;
#Column({ type: 'varchar' })
public title!: string;
#Column({ type: 'varchar' })
public description!: string;
#Column({ type: 'simple-array', nullable: true })
public tags: string[];
}
#Entity()
export class Tag extends BaseEntity {
#PrimaryGeneratedColumn('uuid')
public id!: string;
#Column({ type: 'varchar' })
public name!: string;
}
However, I am currently not making use of the relational capabilities of TypeORM. I was wondering though, how would i go about doing this? Since the relation only works one way, i.e. the one Recipe having many Tags.
I could be wrong, but I believe by default, you must declare both ways--even if you only intend to use a single direction of the relationship.
For example, you need to declare that a Recipe has many Tags you also have to set up the Tag to Recipe relationship even if you aren't going to use it.
Given your example, you'll need to set up a one:many and a many:one relationship.
Since Recipe will "own" the tags, it will have the one:many:
// recipe.entity.ts
#OneToMany(() => Tag, (tag) => tag.recipe)
tags: Tag[];
Then the inverse will look like this:
// tag.entity.ts
#ManyToOne(() => Recipe, (recipe) => recipe.tags)
#JoinColumn({
name: 'recipeId',
})
recipe: Recipe;
If you're considering having many recipes own the same tag, you may need to consider using a many:many relationship
EDIT
I suppose you could technically store an array of id's in a column to represent tags for any given recipe. The question here is, what happens if you decide you need further info on any given tag?
IMO, (and it's just that so take all of this with a grain of salt). You are bending your recipe table to also store relationship info.
I have found it to be more helpful to keep my "buckets" (tables) as specific as possible. That'd leave us with:
recipes | tags | recipes_tags
-----------------------------
That way my recipes table just has recipes & that's it. "Just give me all recipes...". Tags is the same, "just show me all tags"
The two things are completely different entities. By setting up a ManyToMany relationship, we're telling TypeORM that these two columns are related--without "muddying" either of their underlying data.
You can add/remove columns on the pivot table should you decide you want more info about the relationship. At that point, you'd still be working with the relationship, not a tag or recipe so your data would still be nice & lean!
Another example from one of my own use cases...
I have an Activity and a Resource any given resource can have one or more Activities. (activities = tags/ resources = recipes)
// activity.entity.ts
...
#PrimaryGeneratedColumn('uuid')
id: string;
#Column()
name: string;
...
#ManyToMany((type) => Resource, (resource) => resource.activities)
resources: Resource[];
// resource.entity.ts
#PrimaryGeneratedColumn('uuid')
id: string;
#Column()
name: string;
...
#JoinTable()
#ManyToMany((type) => Activity, (activity) => activity.resources)
activities: Activity[];
The above generates a resources_activities_activities table.
Within that table is:
resourceId | activityId
------------------------
I could add additional columns here as well. createdBy or status or something else that is specific to the relationship. Each entry in this table has a relationship back to the activity and the resource--which is great!
I realize we've gone outside the scope of your original question, but I think this is a pretty small step outside, for a potential big win later on.
When I make a request to get a resource: example.com/resources/123 I get something like this back:
"id": "123"
...
"activities": [
{
"id": "f79ce066-75ba-43bb-bf17-9e60efa65e25",
"name": "Foo",
"description": "This is what Foo is.",
"createdAt": "xxxx-xx-xxxxx:xx:xx.xxx",
"updatedAt": "xxxx-xx-xxxxx:xx:xx.xxx"
}
]
...
Likewise, any time I get an activity, I also get back any resources that are related to it. In my front-end I can then easily do something like resource.activities.

TypeORM - postgresSQL / saving data in the DB

So, i'm new into this typeORM thing, and actually also new into postgresSQL DB, and there's something i couldn't undertand about typeORM and making relations between tables.
My Question: So, i have two entities, User and Post. When you create a post, we store the user ( creator of the post ) in the DB using #JoinColumn, and when i go to users table, i can see the name of that field (username), but, inside User entity, we have an array of Posts, but, that field doesn't appear in the postgres DB, so, when i create a relation, #ManyToOne and #OneToMany, what data stores in the DB and which don't ? Besides that, when i fetch stuff, i can fetch the array, but, does that array is store in the DB or what ? I'm kinda confused with this, so, now let me show you the code
User entity
import {
Entity as TOEntity,
Column,
Index,
BeforeInsert,
OneToMany
} from "typeorm";
import bcrypt from "bcrypt";
import { IsEmail, Length } from "class-validator";
import { Exclude } from "class-transformer";
import Entity from "./Entity";
import Post from "./Post";
#TOEntity("users")
export default class User extends Entity {
constructor(user: Partial<User>) {
super();
Object.assign(this, user);
}
#Index()
#IsEmail(undefined, { message: "Must be a valid email address" })
#Length(5, 255, { message: "Email is empty" })
#Column({ unique: true })
email: string;
#Index()
#Length(3, 200, { message: "Must be at leat 3 characters long" })
#Column({ unique: true })
username: string;
#Exclude()
#Length(6, 200, { message: "Must be at leat 3 characters long" })
#Column()
password: string;
#OneToMany(() => Post, post => post.user)
posts: Post[];
#BeforeInsert()
async hashedPassword() {
this.password = await bcrypt.hash(this.password, 6);
}
}
Post entity
import {
Entity as TOEntity,
Column,
Index,
BeforeInsert,
ManyToOne,
JoinColumn,
OneToMany
} from "typeorm";
import Entity from "./Entity";
import User from "./User";
import { makeid, slugify } from "../util/helpers";
import Sub from "./Sub";
import Comment from "./Comment";
#TOEntity("posts")
export default class Post extends Entity {
constructor(post: Partial<Post>) {
super();
Object.assign(this, post);
}
#Index()
#Column()
identifier: string; // 7 Character Id
#Column()
title: string;
#Index()
#Column()
slug: string;
#Column({ nullable: true, type: "text" })
body: string;
#Column()
subName: string;
#ManyToOne(() => User, user => user.posts)
#JoinColumn({ name: "username", referencedColumnName: "username" })
user: User;
#ManyToOne(() => Sub, sub => sub.posts)
#JoinColumn({ name: "subName", referencedColumnName: "name" })
sub: Sub;
#OneToMany(() => Comment, comment => comment.post)
comments: Comment[];
#BeforeInsert()
makeIdAndSlug() {
this.identifier = makeid(7);
this.slug = slugify(this.title);
}
}
How the User entity looks as a table in the DB
So, as you can see, there's no field with name posts ( which is weird, because as i already said, if i can fetch that, where is that data if i can't see it in the DB )
Now, let me show you Post entity
What i want to understand: So, we have the relationship between tables, know, i tried to search stuff in order to understand that, but i couldn't find anything, so, if you can help me with this mess, i would really aprecciate that, so, thanks for your time !
Let's take this section and try to understand piece by piece:
#ManyToOne(() => User, user => user.posts)
#JoinColumn({ name: "username", referencedColumnName: "username" })
user: User;
1. #ManyToOne(() => User, user => user.posts):
#ManyToOne: This annotation tells typeORM that Post entity is going to have a many to one relationship. From the postgres DB point of view, this means that posts table is going to have a new column (foreign key) which points to a record in some other table.
() => User: This is called definition of the target relationship. This helps typeORM to understand that the target of the relationship is User entity. For postgres DB, this means the foreign key in posts table is going to reference a row in users database
user => user.posts: This is called the inverse relationship. This tells typeORM that the related property for the relationship in User entity is posts. From the postgres DB point of view, this has no meaning. As long as it has the foreign key reference, it can keep the relationship between the two tables.
2. #JoinColumn({ name: "username", referencedColumnName: "username" }):
#JoinColumn: In this scenario, this annotation helps typeORM to understand the name of the foreign key column in posts table and the name of the referenced column in users table
name: "username": This is the name of the column in posts table which is going to uniquely identify a record in users table
referencedColumnName: "username": This is the name of the column in users table which is going to be referenced by the foreign key username in posts table.
inside User entity, we have an array of Posts, but, that field doesn't appear in the postgres DB
The array of Posts is there for the typeORM to return you an array of linked posts. It is not needed by postgres DB to contain the relationship.
when i create a relation, #ManyToOne and #OneToMany, what data stores in the DB and which don't
Whatever property you decorated using #Column will be there in the table as it is. And for the relationships, only the foreign key will be saved. As an example, when you save a Post entity, it will save only the relevant columns in that entity + username foreign key.
when i fetch stuff, i can fetch the array, but, does that array is store in the DB or what ?
When you query User entity, typeorm uses the annotations to join users table with posts table and return you the posts with the user you searched. But in database, it saves users and posts data in their respective tables and uses username foreign key to keep the relationship between them.
I hope this helps you to understand what happens. Cheers 🍻 !!!

Which of these 2 MongoDB design choices is recommended?

I have 2 models: Supplier and Supplier type. The client should be able to retrieve all Suppliers belonging to a particular Supplier Type:
Supplier:
name: {
type: String,
required: true,
minlength: 2,
maxlength: 255,
},
...
supplier_type: { type: ObjectId, ref: 'SupplierType' },
}
SupplierType:
name: {
type: String,
required: true,
minlength: 2,
maxlength: 50,
},
suppliers: [{ type: ObjectId, ref: 'Supplier' }],
};
Design 1:
A field on Supplier contains a reference to the assigned Supplier Type object.
An array on the Supplier Type object contains object references to all the Suppliers that have the Supplier Type.
The client queries the relevant Supplier Type document, let's say Vegetables and contained in the response, among other fields, will be a list of Suppliers.
With this approach, each time a new Supplier is saved, at least one other DB operation would be needed to update the Suppliers array on the Supplier Type object.
Design 2:
Remove the Suppliers reference array from Supplier Type object
A field on Supplier contains a reference to the assigned Supplier Type object, as in design 1.
The Client, this time, queries the Supplier document with a parameter specifying the Supplier Type i.e. GET /suppliers?supplier-type=Vegetables
Which design makes the most sense/would be the recommended approach in MongoDB?
I see no reason to split these two objects into separate collections. Embed the SupplierType inside the Supplier object. If a Supplier can be of more than one SupplyType you can extend that object into an array.
Now when you get any Supplier you automatically get their SupplierType without the need for a join and/or a second query.
Want to find all the Supplier docs by SupplyType? Query on the SupplyType field and a single cursor will return all the relevant suppliers.
Apply indexes to either field if the number of items is large to improve performance.

Generated SQL query with "WHERE (1 <> 1)" condition

I'm trying to query a many-to-many relationship using the Gorm ORM for Go.
I have two structs: User & Address.
type User struct {
// gorm.Model
UUID string `gorm:"type:uuid;primary_key;auto_increment:false"`
Firstname string
// ...
Addresses []Address `gorm:"many2many:useraddresses"`
}
// Address represents the Postgres SQL address model
type Address struct {
UUID string `gorm:"type:uuid;primary_key;auto_increment:false"`
Line1 string
// ...
}
I took inspiration from the many-to-many example shown here in the documentation, (except I used a slice of users []User instead of a single user).
var u []User
var a []Address
If I query just using the users as a Model, all users are returned (sends sql query SELECT * FROM "users"):
db.Model(&u).Find(&u)
However, if I include related Addresses, surgeons are returned, but no Addresses:
db.Model(&u).Related(&a, "Addresses").Find(&u)
This creates another sql query that precedes the first:
SELECT "addresses".*
FROM "addresses" INNER JOIN "useraddresses" ON "useraddresses"."address_uuid" = "addresses"."uuid"
WHERE (1 <> 1)
Of course, the where false condition prevents any addresses from being returned.
Can anyone shed light on how I can include the addresses using the db.Model method of Gorm?

Mongodb changing the unique key

I have made the users email the unique key for my entire users database:
var usersSchema = new Schema({
_id: String, // Users Unique Email address
name: String, // Users name
phone: String, // Users phone number
country: String, // Country
type: String, // tenant/agent/manager/admin
username: String, // Username for login
password: String, // Password string
trello: Object, // Trello auth settings
settings: Object, // Settings for manager and other things
createDate: Number, // Date user was created
contactDate: Number, // Date user was last contacted
activityDate: Number // Date of last activity on this user (update/log/etc)
});
So what if the user changes email address?
Is my only way to delete the record and create it again?
Or is there a smarter way?
And the users._id (email) have relations in 16 other tables.
Example the booking table
var bookingSchema = new Schema({
_id: String, // Unique booking ID
user: String, // User ID --> users._id
property: String, // Property ID --> property._id
checkin: Number, // Check in Date
checkout: Number // Check out Date
});
One user can have a LOT of bookings
What I would do is find all records that matches the email and then do a for (i=1 ; i<booking.length ; i++) and then update the email of each record
Is there a smarter way to update all emails that matches using only one mongo call?
(the reason is there are so many relations, so my loop seems a bit like a very primitive way of doing it)
I would say it's much cleaner to create a field for email and create an Unique Index on that.
Unfortunately still the relationship as the ones inside the Relational databases isn't supported! There are plans according to the latest talks to create this feature natively.
The best solution for you would be to think how to use the sub-documents to make things more consistent.