Skip to content

Relations in find operations in a repository may change incorrectly from "left" to "inner" #9395

@PedroLimaComputacao

Description

@PedroLimaComputacao

Issue Description

In find operations, when you have a condition in where that relates to an optional relation, the relation changes from "left" to "inner" incorrectly. That's a problem when the condition is written as an array (resulting in an OR operator), so a condition in one of the array elements may be optional if it is not described in all elements.

Expected Behavior

According to the documentation, FindOptionsRelations is a shortened version of join and leftJoinAndSelect, therefore, all relations described in them should always result in a left join.

Actual Behavior

All relations represented in a where statement that weren't explicitly described in relations are then created as an inner join. That's iffy, but... sure why not. The problem is that even relations that were explicitly written in relations (therefore expected to be of type "left") are then converted to type "inner". That's invasive and corrupts the expected behavior of relations.

Steps to Reproduce

  1. Have an entity model with an optional many-to-one relation. For example: An employee may or may not have an office, but an office can house many employees if it's big enough.
  2. Create a repository of the model.
  3. Call .find(), and in the options write where as an array. In one of the elements, have a condition that uses the optional many-to-one relation and in another element write other conditions that don't depend on the optional relation.
import { Entity, Column, PrimaryGeneratedColumn, JoinColumn, ManyToOne } from 'typeorm';

@Entity('employee')
export class Employee {
	@Column()
	name: string;

	@Column()
	office_id?: string;
	
	@ManyToOne(() => Office)
	@JoinColumn({ name: 'office_id' })
	office?: Office;
}

@Entity('office')
export class Office {
	@PrimaryGeneratedColumn('uuid')
	id: string;
	
	@Column()
	hasWindow: boolean;
}
import myDataSource from 'path/to/myDataSource';
import Employee from 'path/to/Employee';

const rep = myDataSource.getRepository(Employee);

const result = await rep.find({
	where: [
		{ name: 'Jhon' },
		{ office: { hasWindow: false } },
	],
	relations: { office: true },
});

In the above example, an employee named Jhon who doesn't have an office would not appear in the results, even though he perfectly suffices the first condition of the where (his name is Jhon). It shouldn't matter that he doesn't have an office, but the join relation "office" was wrongfully converted to an "inner".

My Environment

Dependency Version
Operating System Windows 10
Node.js version v14.17.4
Typescript version 4.8.3
TypeORM version 0.3.9

Additional Context

--

Relevant Database Driver(s)

I've only tested in mysql.

DB Type Reproducible
aurora-mysql no
aurora-postgres no
better-sqlite3 no
cockroachdb no
cordova no
expo no
mongodb no
mysql yes
nativescript no
oracle no
postgres no
react-native no
sap no
spanner no
sqlite no
sqlite-abstract no
sqljs no
sqlserver no

Are you willing to resolve this issue by submitting a Pull Request?

  • ✖️ Yes, I have the time, and I know how to start.
  • ✅ Yes, I have the time, but I don't know how to start. I would need guidance.
  • ✖️ No, I don’t have the time, but I can support (using donations) development.
  • ✖️ No, I don’t have the time and I’m okay to wait for the community / maintainers to resolve this issue.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions