TypeORM OneToMany Relation not Found - typeorm

this is my first time posting on StackOverflow so hopefully, I am doing this right.
As I started working with typeORM I noticed that ManyToOne relations are working as intended through my resolvers and display the results, while OneToMany relations throw 'relation can't be found' errors.
Things I've tried so far:
Restructuring entities exactly as in TypeOrm documentation
Query using both QueryBuilder and find methods with relations/leftjoin
Wiping and rebuilding Postgresdb
User.ts File
#Field(() => [UserComment], { nullable: true })
#OneToMany(() => UserComment, (comment) => comment.user)
comments: UserComment[];
UserComment.ts File
#ManyToOne(() => User, (user) => user.comments)
#Field()
user: User;
Test query;
return User.findOne(req.session.userId, { relations: ["comments"] });
Output
"message": "Relation \"comments\" was not found; please check if it is correct and really exists in your entity.",
I hope I'm not overlooking something silly. Any suggestions are appreciated.
If it helps, here is to show that many to one side of this relation works as intended:
comment resolver:
async testComments(): Promise<UserComment[]> {
return await UserComment.find({ relations: ["user"] });
}
result:
"testComments": [
{
"body": "This is another test comment.",
"user": {
"username": "Admin"
}
},
{
"body": "Another comment coming up!",
"user": {
"username": "Admin"
}
},

I am not sure why you are using the #Field decorator, remove that and try, it should work. It's working in my case.
// remove this #Field(() => [UserComment], { nullable: true })
#OneToMany(() => UserComment, (comment) => comment.user)
comments: UserComment[];
#ManyToOne(() => User, (user) => user.comments)
// remove this #Field()
user: User;

It was indeed a silly mistake related to previous use of mikro-orm (was using OneToMany auto-import from mikro-orm and not typeorm)

Related

Auto-generated Mutation Does Not Create Relationship

I want to test auto-generated CRUD mutations created by calling makeAugmentedSchema from 'neo4j-graphql-js'. There is no problem with creating nodes but creating relationship does not work for me. Please advise on what I am doing wrong here.
Schema:
type Bio{
id: ID!
description: String
}
type Person{
id: ID!
name: String
dob: Date
gender: String
bioRelation: [Bio] #relation(name: "HAS_BIO", direction: "OUT")
}
Mutation:
I am following the Interface Mutations guidance https://grandstack.io/docs/graphql-interface-union-types to create mutation.
mutation {
p: CreatePerson(
name: "Anton",
gender: "Male") {
name
gender
id
}
b: CreateBio(
description: "I am a developer") {
description
id
}
r: AddPersonBioRelation(
from: {id: "p"},
to:{id: "b"}
){
from{
name
}
to{
description
}
}
}
It create Person and Bio nodes but no any relationship gets created between the two:
{
"data": {
"p": {
"name": "Anton",
"gender": "Male",
"id": "586b63fd-f9a5-4274-890f-26ba567c065c"
},
"b": {
"description": "I am a developer",
"id": "a46b4c22-d23b-4630-ac84-9d6248bdda89"
},
"r": null
}
}
This is how AddPersonBioRelation looks like:
Thank you.
I am new to GRANDstack, and I have also been struggling with these types of issues myself. I have typically broken this out separate mutations (in javascript) and used the return value for each as values for the next mutation. for example:
await createIncident({
variables: {
brief: values.brief,
date,
description: values.description,
recordable: values.recordable,
title: values.title
}
}).then((res) => {
addIncidentUser({
variables: {
from: user.id,
to: res.data.CreateIncident.id
}
});
});
the problem that i see in the example you've provided is that you are specifying a string value for from and to as "p" and "b" respectively and NOT the p.id and b.id return values from the parent mutations.
it's fine of me to point that out but what i can't for the LIFE of me figure out is how to properly reference p.id and b.id in the mutation itself. in other words you are trying to send
from: { id: "586b63fd-f9a5-4274-890f-26ba567c065c"}
to: { id: "a46b4c22-d23b-4630-ac84-9d6248bdda89" }
but in reality you are sending
from: { id: "p"}
to: { id: "b" }
which aren't actually references in neo4j so it fails.
if we can figure out how to properly reference p.id and b.id we should get this working.
Thank you, #dryhurst. It appears that there is no way to reference id of newly created nodes, but I found a solution by introducing temp id property. Please see the discussion of this matter and final solution on:
https://community.neo4j.com/t/auto-generated-mutation-does-not-create-relationship/21412/16.

Loopback POST array of entry?

I want to insert 10 entries with one query against 10 queries.
I read that it's possible to do it by sending an array like this :
But I get this error:
Do I need to set something? I don't know what to do at all.
Repo with a sample : https://github.com/mathias22osterhagen22/loopback-array-post-sample
Edit:
people-model.ts:
import {Entity, model, property} from '#loopback/repository';
#model()
export class People extends Entity {
#property({
type: 'number',
id: true,
generated: true,
})
id?: number;
#property({
type: 'string',
required: true,
})
name: string;
constructor(data?: Partial<People>) {
super(data);
}
}
export interface PeopleRelations {
// describe navigational properties here
}
export type PeopleWithRelations = People & PeopleRelations;
The problem with your code was :
"name": "ValidationError", "message": "The People instance is not
valid. Details: 0 is not defined in the model (value: undefined);
1 is not defined in the model (value: undefined); name can't be
blank (value: undefined).",
Here in above as in your #requestBody schema, you are applying to insert a single object property, where as in your body are sending the array of [people] object.
As you can see in your people.model.ts you have declared property name to be required, so system finds for the property "name", which obviously not available in the given array of object as primary node.
As you are passing index array, so its obvious error that you don't have any property named 0 or 1, so it throws error.
The below is the code hat you should apply to get insert the multiple, items of the type.
#post('/peoples', {
responses: {
'200': {
description: 'People model instance',
content: {
'application/json': {
schema: getModelSchemaRef(People)
}
},
},
},
})
async create(
#requestBody({
content: {
'application/json': {
schema: {
type: 'array',
items: getModelSchemaRef(People, {
title: 'NewPeople',
exclude: ['id'],
}),
}
},
},
})
people: [Omit<People, 'id'>]
): Promise<{}> {
people.forEach(item => this.peopleRepository.create(item))
return people;
}
You can also use this below
Promise<People[]> {
return await this.peopleRepository.createAll(people)
}
You can pass the array of your people model by modifying the request body.If you need more help you can leave comment.
I think you have a clear solution now. "Happy Loopbacking :)"

How to load relations with tree entity with typeorm?

I have a Role Entity and a Route Entity which is tree structure, and they're ManyToMany relation.
Now I want to retrive all the roles via RoleRepository.find({relations: ['routes']}), which will load all the roles data as expected, however the routes prop won't automaticlly load it's children data, which looks like:
[{
id: 1,
name: 'route1',
routes: [{
id: 1,
path: '/home'
}]
}]
I've checked all documentation and had no clue to make it.
#Entity()
export class Role {
#PrimaryGeneratedColumn()
public id: number
... some other columns
#ManyToMany(type => Route, route => route.roles)
public routes: Route[]
}
#Entity()
#Tree('materialized-path')
export class Route {
#PrimaryGeneratedColumn()
public id: number
#TreeParent()
public parent: Route
#TreeChildren({ cascade: true })
public children: Route[]
#ManyToMany(type => Role, role => role.routes)
#JoinTable()
public roles: Role[]
}
I think the correct way to get the relations when is a tree entity is making:
await getManager().getTreeRepository(Route).findTrees({ relations: ["roles"] });
To load the children of a relation you can just add the property you want within the relations array like this:
RoleRepository.find({relations: ['routes', 'routes.roles']})
it should give you something like that:
"route": {
"id": 1,
"name": "route1",
"roles": [{
"id": 1,
"name": "role1",
"routes": [{
"id": 1,
"name": "route1"
}, {
"id": 2,
"name": "route2"
}
]
}
]
}

How to allow circular relation with nullable: false?

I want to store who is the "creator" and "updater" of every record as a reference to the "User" table in my database. This applies to every table, including the "User" table itself. This is because it can happen that a record is created either by the user that is registering or by some other already existing user.
On the "User" table I have a DB trigger that fills the "creator" and "updater" fields to the same value as the newly generated "id" by default, hence I would like to use a NOT NULL constraint on my "creator" and "updater" column. Unfortunately, this results in the following error: "Circular relations detected: User -> User. To resolve this issue you need to set nullable: false somewhere in this dependency structure."
Is there a way for me to have a circular relation with a not null constraint using TypeORM?
I was actually able to make it work. I'm going to share my solution in case anyone is looking for an answer.
Before I was trying to auto generate the "created_by" column in the following way:
#ManyToOne(type => User, { onUpdate: 'CASCADE', onDelete: 'CASCADE', nullable: false })
#JoinColumn({ name: 'created_by' })
creator: User
All I had to do to make it work was to remove the nullable: false from there and move it to a new #Column field so that in total I would have:
#Column({ nullable: false })
created_by: number
#ManyToOne(type => User, { onUpdate: 'CASCADE', onDelete: 'CASCADE' })
#JoinColumn({ name: 'created_by' })
creator: User
I had a similar issue. I have a scenario where I have foreign key in the same table.
If I explain my scenario a bit more. I was working on storing conditional statements in an application.
condition#1 a > 4
condition#2 a + b < 0
Now I wanted to give user the ability to put a logical operator in b/w condition#1 and condition#2 . Let say user selected OR operator in b/w.
That would look like
(condition#1 OR condition#2)
Now comes the interesting part where I felt the need to have FK in the same. What if user wants to add another condition with OR operator before or after #1 and #2.
condition#3 OR (condition#1 OR condition#2)
So for storing these conditions I created relationships.
I stored 2 records for this scenario
1st for storing (condition#1 OR condition#2) and 2nd record for storing
condition#3 OR (condition#1 OR condition#2)
Now come to the point how I fixed it.
Previously it was something like :
export class ConditionLogicalOperator {
#PrimaryGeneratedColumn({
type: "int",
name: "ConditionLogicalOperatorID"
})
ConditionLogicalOperatorID: number;
#Column("int", {
nullable: false,
name: "LogicalOperatorID"
})
LogicalOperatorID: number;
#ManyToOne(() => ConditionLogicalOperator, (ConditionLogicalOperator: ConditionLogicalOperator) => ConditionLogicalOperator.conditionLogicalOperators)
#JoinColumn({ name: 'ConditionID1' })
conditionLogicalOperator1: ConditionLogicalOperator | null;
#ManyToOne(() => ConditionLogicalOperator, (ConditionLogicalOperator: ConditionLogicalOperator) => ConditionLogicalOperator.conditionLogicalOperators2)
#JoinColumn({ name: 'ConditionID2' })
conditionLogicalOperator2: ConditionLogicalOperator | null;
....
....
}
The trick was to add an empty object { } to Relationship. Please notice { } at the end of #ManyToOne line in each property. It worked for me.
export class ConditionLogicalOperator {
#PrimaryGeneratedColumn({
type: "int",
name: "ConditionLogicalOperatorID"
})
ConditionLogicalOperatorID: number;
#Column("int", {
nullable: false,
name: "LogicalOperatorID"
})
LogicalOperatorID: number;
#ManyToOne(() => ConditionLogicalOperator, (ConditionLogicalOperator: ConditionLogicalOperator) => ConditionLogicalOperator.conditionLogicalOperators, { })
#JoinColumn({ name: 'ConditionID1' })
conditionLogicalOperator1: ConditionLogicalOperator | null;
#ManyToOne(() => ConditionLogicalOperator, (ConditionLogicalOperator: ConditionLogicalOperator) => ConditionLogicalOperator.conditionLogicalOperators2, { })
#JoinColumn({ name: 'ConditionID2' })
conditionLogicalOperator2: ConditionLogicalOperator | null;
....
....
}

OneToMany relation with nested ManyToOne relation returns broken relation entity

I'm creating a structure where I have Repositories, Contributions and Users.
A repository has multiple contributions done by different users. It is important that I know the count property on the contribution.
But when I create a new Repository entity without connections I get weird behaviour.
Example Code
My index.ts:
import "reflect-metadata";
import {createConnection, getRepository} from "typeorm";
import {Repository} from "./entity/Repository";
createConnection().then(async connection => {
const newRepo = new Repository();
newRepo.name = 'test';
newRepo.description = 'no contributions made';
await connection.manager.save(newRepo);
const RepoRepository = getRepository(Repository);
const repos = await RepoRepository.find();
console.log(repos);
console.log(repos[0].contributions);
await connection.manager.save(repos);
connection.close();
}).catch(error => console.log(error));
my console output:
repos [ Repository {
name: 'test',
description: 'no contributions made',
contributions: [ [Contribution] ] } ]
contributions [ Contribution { count: null, user: null } ]
which is weird because I never created any contributions.
And the connection.manager.save(repos); call errors out on:
{ QueryFailedError: ER_BAD_NULL_ERROR: Column 'count' cannot be null
at new QueryFailedError (D:\GitHub\Issue\src\error\QueryFailedError.ts:9:9)
at Query.<anonymous> (D:\GitHub\Issue\src\driver\mysql\MysqlQueryRunner.ts:164:37)
at Query.<anonymous> (D:\GitHub\Issue\node_modules\mysql\lib\Connection.js:502:10)
at Query._callback (D:\GitHub\Issue\node_modules\mysql\lib\Connection.js:468:16)
at Query.Sequence.end (D:\GitHub\Issue\node_modules\mysql\lib\protocol\sequences\Sequence.js:83:24)
at Query.ErrorPacket (D:\GitHub\Issue\node_modules\mysql\lib\protocol\sequences\Query.js:90:8)
at Protocol._parsePacket (D:\GitHub\Issue\node_modules\mysql\lib\protocol\Protocol.js:278:23)
at Parser.write (D:\GitHub\Issue\node_modules\mysql\lib\protocol\Parser.js:76:12)
at Protocol.write (D:\GitHub\Issue\node_modules\mysql\lib\protocol\Protocol.js:38:16)
at Socket.<anonymous> (D:\GitHub\Issue\node_modules\mysql\lib\Connection.js:91:28)
message: 'ER_BAD_NULL_ERROR: Column \'count\' cannot be null',
code: 'ER_BAD_NULL_ERROR',
errno: 1048,
sqlMessage: 'Column \'count\' cannot be null',
sqlState: '23000',
index: 0,
sql:
'INSERT INTO `contribution`(`count`, `repositoryName`, `employeeGithub`) VALUES (NULL, \'test\', NULL)',
name: 'QueryFailedError',
query:
'INSERT INTO `contribution`(`count`, `repositoryName`, `employeeGithub`) VALUES (?, ?, ?)',
parameters: [ null, 'test', null ] }
My Entities:
#Entity()
export class Repository {
#PrimaryColumn()
name: string;
#Column("text")
description: string;
#OneToMany(type => Contribution, contribution => contribution.repository, {eager: true, cascade: true})
contributions: Contribution[];
}
#Entity()
export class Contribution{
#ManyToOne(type => Repository, repository => repository.contributions, {primary: true})
repository: Repository;
#ManyToOne(type => User, user => user.contributions, {eager: true, cascade: true, primary: true})
#JoinTable()
user: User;
#Column()
count: number;
}
#Entity()
export class User{
#PrimaryColumn()
github: string;
#OneToMany(type => Contribution, contribution => contribution.user)
contributions: Contribution[];
}
What I tried:
Removing the eager on the ManyToOne from Contribution to User fixes the error and I correctly get an empty contributions array. But it isn't really a solution for the repositories that do have contributions.
The main question is: Am I doing it wrong or is this not something that works in typeorm?

Resources