mirror of
https://github.com/typeorm/typeorm.git
synced 2025-12-08 21:26:23 +00:00
fix: fix up aggregate methods ambiguous column (#11822)
This commit is contained in:
parent
73fda419e4
commit
6e34756b9d
@ -1090,12 +1090,16 @@ export class EntityManager {
|
||||
)
|
||||
}
|
||||
|
||||
const result = await this.createQueryBuilder(entityClass, metadata.name)
|
||||
.setFindOptions({ where })
|
||||
const qb = this.createQueryBuilder(entityClass, metadata.name)
|
||||
qb.setFindOptions({ where })
|
||||
|
||||
const alias = qb.alias
|
||||
|
||||
const result = await qb
|
||||
.select(
|
||||
`${fnName}(${this.connection.driver.escape(
|
||||
column.databaseName,
|
||||
)})`,
|
||||
alias,
|
||||
)}.${this.connection.driver.escape(column.databaseName)})`,
|
||||
fnName,
|
||||
)
|
||||
.getRawOne()
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
OneToMany,
|
||||
} from "../../../../../src"
|
||||
import { Post } from "./Post"
|
||||
|
||||
@Entity()
|
||||
export class Author {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number
|
||||
|
||||
@Column()
|
||||
name: string
|
||||
|
||||
@OneToMany(() => Post, (post) => post.author)
|
||||
posts: Post[]
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
ManyToOne,
|
||||
} from "../../../../../src"
|
||||
import { Author } from "./Author"
|
||||
|
||||
@Entity()
|
||||
export class Post {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number
|
||||
|
||||
@Column()
|
||||
title: string
|
||||
|
||||
@Column()
|
||||
viewCount: number
|
||||
|
||||
@ManyToOne(() => Author, (author) => author.posts)
|
||||
author: Author
|
||||
}
|
||||
@ -0,0 +1,280 @@
|
||||
import "reflect-metadata"
|
||||
import {
|
||||
closeTestingConnections,
|
||||
createTestingConnections,
|
||||
reloadTestingDatabases,
|
||||
} from "../../../utils/test-utils"
|
||||
import { DataSource } from "../../../../src"
|
||||
import { Post } from "./entity/Post"
|
||||
import { Author } from "./entity/Author"
|
||||
import { expect } from "chai"
|
||||
|
||||
describe("repository > aggregate methods with relations", () => {
|
||||
let connections: DataSource[]
|
||||
before(
|
||||
async () =>
|
||||
(connections = await createTestingConnections({
|
||||
entities: [__dirname + "/entity/*{.js,.ts}"],
|
||||
schemaCreate: true,
|
||||
dropSchema: true,
|
||||
})),
|
||||
)
|
||||
beforeEach(() => reloadTestingDatabases(connections))
|
||||
after(() => closeTestingConnections(connections))
|
||||
|
||||
describe("sum with relation filter", () => {
|
||||
it("should return the aggregate sum when filtering by relation", () =>
|
||||
Promise.all(
|
||||
connections.map(async (connection) => {
|
||||
const authorRepo = connection.getRepository(Author)
|
||||
const postRepo = connection.getRepository(Post)
|
||||
|
||||
const author1 = await authorRepo.save({
|
||||
name: "Author 1",
|
||||
})
|
||||
const author2 = await authorRepo.save({
|
||||
name: "Author 2",
|
||||
})
|
||||
|
||||
await postRepo.save([
|
||||
{ title: "Post 1", viewCount: 100, author: author1 },
|
||||
{ title: "Post 2", viewCount: 200, author: author1 },
|
||||
{ title: "Post 3", viewCount: 300, author: author2 },
|
||||
])
|
||||
|
||||
const sum = await postRepo.sum("viewCount", {
|
||||
author: { id: author1.id },
|
||||
})
|
||||
|
||||
expect(sum).to.equal(300)
|
||||
}),
|
||||
))
|
||||
|
||||
it("should return null when no records match relation filter", () =>
|
||||
Promise.all(
|
||||
connections.map(async (connection) => {
|
||||
const authorRepo = connection.getRepository(Author)
|
||||
const postRepo = connection.getRepository(Post)
|
||||
|
||||
const author1 = await authorRepo.save({
|
||||
name: "Author 1",
|
||||
})
|
||||
|
||||
const sum = await postRepo.sum("viewCount", {
|
||||
author: { id: author1.id },
|
||||
})
|
||||
|
||||
expect(sum).to.be.equal(null)
|
||||
}),
|
||||
))
|
||||
})
|
||||
|
||||
describe("average with relation filter", () => {
|
||||
it("should return the aggregate average when filtering by relation", () =>
|
||||
Promise.all(
|
||||
connections.map(async (connection) => {
|
||||
const authorRepo = connection.getRepository(Author)
|
||||
const postRepo = connection.getRepository(Post)
|
||||
|
||||
const author1 = await authorRepo.save({
|
||||
name: "Author 1",
|
||||
})
|
||||
const author2 = await authorRepo.save({
|
||||
name: "Author 2",
|
||||
})
|
||||
|
||||
await postRepo.save([
|
||||
{ title: "Post 1", viewCount: 100, author: author1 },
|
||||
{ title: "Post 2", viewCount: 200, author: author1 },
|
||||
{ title: "Post 3", viewCount: 300, author: author2 },
|
||||
])
|
||||
|
||||
const average = await postRepo.average("viewCount", {
|
||||
author: { id: author1.id },
|
||||
})
|
||||
|
||||
expect(average).to.equal(150)
|
||||
}),
|
||||
))
|
||||
|
||||
it("should return null when no records match relation filter", () =>
|
||||
Promise.all(
|
||||
connections.map(async (connection) => {
|
||||
const authorRepo = connection.getRepository(Author)
|
||||
const postRepo = connection.getRepository(Post)
|
||||
|
||||
const author1 = await authorRepo.save({
|
||||
name: "Author 1",
|
||||
})
|
||||
|
||||
const average = await postRepo.average("viewCount", {
|
||||
author: { id: author1.id },
|
||||
})
|
||||
|
||||
expect(average).to.be.equal(null)
|
||||
}),
|
||||
))
|
||||
})
|
||||
|
||||
describe("minimum with relation filter", () => {
|
||||
it("should return the aggregate minimum when filtering by relation", () =>
|
||||
Promise.all(
|
||||
connections.map(async (connection) => {
|
||||
const authorRepo = connection.getRepository(Author)
|
||||
const postRepo = connection.getRepository(Post)
|
||||
|
||||
const author1 = await authorRepo.save({
|
||||
name: "Author 1",
|
||||
})
|
||||
const author2 = await authorRepo.save({
|
||||
name: "Author 2",
|
||||
})
|
||||
|
||||
await postRepo.save([
|
||||
{ title: "Post 1", viewCount: 100, author: author1 },
|
||||
{ title: "Post 2", viewCount: 200, author: author1 },
|
||||
{ title: "Post 3", viewCount: 50, author: author2 },
|
||||
])
|
||||
|
||||
const minimum = await postRepo.minimum("viewCount", {
|
||||
author: { id: author1.id },
|
||||
})
|
||||
|
||||
expect(minimum).to.equal(100)
|
||||
}),
|
||||
))
|
||||
|
||||
it("should return null when no records match relation filter", () =>
|
||||
Promise.all(
|
||||
connections.map(async (connection) => {
|
||||
const authorRepo = connection.getRepository(Author)
|
||||
const postRepo = connection.getRepository(Post)
|
||||
|
||||
const author1 = await authorRepo.save({
|
||||
name: "Author 1",
|
||||
})
|
||||
|
||||
const minimum = await postRepo.minimum("viewCount", {
|
||||
author: { id: author1.id },
|
||||
})
|
||||
|
||||
expect(minimum).to.be.equal(null)
|
||||
}),
|
||||
))
|
||||
})
|
||||
|
||||
describe("maximum with relation filter", () => {
|
||||
it("should return the aggregate maximum when filtering by relation", () =>
|
||||
Promise.all(
|
||||
connections.map(async (connection) => {
|
||||
const authorRepo = connection.getRepository(Author)
|
||||
const postRepo = connection.getRepository(Post)
|
||||
|
||||
const author1 = await authorRepo.save({
|
||||
name: "Author 1",
|
||||
})
|
||||
const author2 = await authorRepo.save({
|
||||
name: "Author 2",
|
||||
})
|
||||
|
||||
await postRepo.save([
|
||||
{ title: "Post 1", viewCount: 100, author: author1 },
|
||||
{ title: "Post 2", viewCount: 200, author: author1 },
|
||||
{ title: "Post 3", viewCount: 500, author: author2 },
|
||||
])
|
||||
|
||||
const maximum = await postRepo.maximum("viewCount", {
|
||||
author: { id: author1.id },
|
||||
})
|
||||
|
||||
expect(maximum).to.equal(200)
|
||||
}),
|
||||
))
|
||||
|
||||
it("should return null when no records match relation filter", () =>
|
||||
Promise.all(
|
||||
connections.map(async (connection) => {
|
||||
const authorRepo = connection.getRepository(Author)
|
||||
const postRepo = connection.getRepository(Post)
|
||||
|
||||
const author1 = await authorRepo.save({
|
||||
name: "Author 1",
|
||||
})
|
||||
|
||||
const maximum = await postRepo.maximum("viewCount", {
|
||||
author: { id: author1.id },
|
||||
})
|
||||
|
||||
expect(maximum).to.be.equal(null)
|
||||
}),
|
||||
))
|
||||
})
|
||||
|
||||
describe("aggregate methods with nested relation filters", () => {
|
||||
it("should handle complex relation filters correctly", () =>
|
||||
Promise.all(
|
||||
connections.map(async (connection) => {
|
||||
const authorRepo = connection.getRepository(Author)
|
||||
const postRepo = connection.getRepository(Post)
|
||||
|
||||
const author1 = await authorRepo.save({
|
||||
name: "John Doe",
|
||||
})
|
||||
const author2 = await authorRepo.save({
|
||||
name: "Jane Smith",
|
||||
})
|
||||
|
||||
await postRepo.save([
|
||||
{ title: "Post 1", viewCount: 100, author: author1 },
|
||||
{ title: "Post 2", viewCount: 200, author: author1 },
|
||||
{ title: "Post 3", viewCount: 300, author: author2 },
|
||||
])
|
||||
|
||||
// Filter by both relation id and relation property
|
||||
const sum = await postRepo.sum("viewCount", {
|
||||
author: { id: author1.id, name: "John Doe" },
|
||||
})
|
||||
|
||||
expect(sum).to.equal(300)
|
||||
}),
|
||||
))
|
||||
})
|
||||
|
||||
describe("aggregate methods with multiple tables having same column name", () => {
|
||||
it("should correctly qualify column names to avoid ambiguous references", () =>
|
||||
Promise.all(
|
||||
connections.map(async (connection) => {
|
||||
const authorRepo = connection.getRepository(Author)
|
||||
const postRepo = connection.getRepository(Post)
|
||||
|
||||
const author1 = await authorRepo.save({
|
||||
name: "Author 1",
|
||||
})
|
||||
const author2 = await authorRepo.save({
|
||||
name: "Author 2",
|
||||
})
|
||||
|
||||
await postRepo.save([
|
||||
{ title: "Post 1", viewCount: 100, author: author1 },
|
||||
{ title: "Post 2", viewCount: 200, author: author1 },
|
||||
{ title: "Post 3", viewCount: 300, author: author2 },
|
||||
])
|
||||
|
||||
// Both Post and Author have 'id' column - this should not cause ambiguous column error
|
||||
const maxId = await postRepo.maximum("id", {
|
||||
author: { id: author1.id },
|
||||
})
|
||||
|
||||
expect(maxId).to.be.a("number")
|
||||
expect(maxId).to.be.greaterThan(0)
|
||||
|
||||
// Verify we got the correct max ID from author1's posts, not from author2 or any other table
|
||||
const author1Posts = await postRepo.find({
|
||||
where: { author: { id: author1.id } },
|
||||
order: { id: "DESC" },
|
||||
})
|
||||
expect(maxId).to.equal(author1Posts[0].id)
|
||||
}),
|
||||
))
|
||||
})
|
||||
})
|
||||
Loading…
x
Reference in New Issue
Block a user