This commit is contained in:
@@ -141,6 +141,120 @@ describe('toFindOptionsWhere', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('toFindOptionsWhere 深层 OR 嵌套', () => {
|
||||
it('X AND (A OR B) 分配律展开为 [{X,A},{X,B}]', () => {
|
||||
const out = toFindOptionsWhere([
|
||||
{ field: 'x', query: 'EQ', value: 1 },
|
||||
{
|
||||
logic: 'AND',
|
||||
children: [
|
||||
{ field: 'a', query: 'EQ', value: 2 },
|
||||
{ field: 'b', query: 'EQ', value: 3, logic: 'OR' },
|
||||
],
|
||||
},
|
||||
]);
|
||||
expect(out).toHaveLength(2);
|
||||
expect(out[0].x.value).toBe(1);
|
||||
expect(out[0].a.value).toBe(2);
|
||||
expect(out[1].x.value).toBe(1);
|
||||
expect(out[1].b.value).toBe(3);
|
||||
});
|
||||
|
||||
it('(A OR B) AND (C OR D) 笛卡尔积展开', () => {
|
||||
const out = toFindOptionsWhere([
|
||||
{
|
||||
logic: 'AND',
|
||||
children: [
|
||||
{ field: 'a', query: 'EQ', value: 1 },
|
||||
{ field: 'b', query: 'EQ', value: 2, logic: 'OR' },
|
||||
],
|
||||
},
|
||||
{
|
||||
logic: 'AND',
|
||||
children: [
|
||||
{ field: 'c', query: 'EQ', value: 3 },
|
||||
{ field: 'd', query: 'EQ', value: 4, logic: 'OR' },
|
||||
],
|
||||
},
|
||||
]);
|
||||
expect(out).toHaveLength(4);
|
||||
// [{A,C},{A,D},{B,C},{B,D}]
|
||||
const combos = out.map((g) => Object.keys(g).sort().join(','));
|
||||
expect(combos).toEqual(expect.arrayContaining(['a,c', 'a,d', 'b,c', 'b,d']));
|
||||
});
|
||||
|
||||
it('A OR (B AND C) 保持两组', () => {
|
||||
const out = toFindOptionsWhere([
|
||||
{ field: 'a', query: 'EQ', value: 1 },
|
||||
{
|
||||
logic: 'OR',
|
||||
children: [
|
||||
{ field: 'b', query: 'EQ', value: 2 },
|
||||
{ field: 'c', query: 'EQ', value: 3, logic: 'AND' },
|
||||
],
|
||||
},
|
||||
]);
|
||||
expect(out).toHaveLength(2);
|
||||
expect(out[0]).toEqual({ a: Equal(1) });
|
||||
expect(out[1].b.value).toBe(2);
|
||||
expect(out[1].c.value).toBe(3);
|
||||
});
|
||||
|
||||
it('同字段 AND 用 And() 组合', () => {
|
||||
const out = toFindOptionsWhere([
|
||||
{ field: 'name', query: 'EQ', value: 'a' },
|
||||
{ field: 'name', query: 'LIKE', value: 'b', logic: 'AND' },
|
||||
]);
|
||||
expect(out).toHaveLength(1);
|
||||
expect(out[0].name.type).toBe('and');
|
||||
});
|
||||
});
|
||||
|
||||
describe('toFindOptionsWhere 真实 SQL 验证', () => {
|
||||
it('深层嵌套生成正确 SQL', async () => {
|
||||
const { DataSource, EntitySchema } = await import('typeorm');
|
||||
const User = new EntitySchema({
|
||||
name: 'User',
|
||||
tableName: 'users',
|
||||
columns: {
|
||||
id: { type: 'int', primary: true, generated: true },
|
||||
name: { type: 'varchar' },
|
||||
age: { type: 'int' },
|
||||
},
|
||||
});
|
||||
const ds = new DataSource({
|
||||
type: 'better-sqlite3',
|
||||
database: ':memory:',
|
||||
entities: [User],
|
||||
synchronize: true,
|
||||
});
|
||||
await ds.initialize();
|
||||
try {
|
||||
const repo = ds.getRepository('User');
|
||||
const where = toFindOptionsWhere([
|
||||
{ field: 'name', query: 'EQ', value: 'a' },
|
||||
{
|
||||
logic: 'AND',
|
||||
children: [
|
||||
{ field: 'age', query: 'GT', value: 18 },
|
||||
{ field: 'age', query: 'LT', value: 60, logic: 'OR' },
|
||||
],
|
||||
},
|
||||
]);
|
||||
const qb = repo.createQueryBuilder('u');
|
||||
qb.where(where);
|
||||
const [sql, params] = qb.getQueryAndParameters();
|
||||
// 分配律: X AND (A OR B) => (X AND A) OR (X AND B)
|
||||
expect(sql).toContain('("u"."name" = ? AND "u"."age" > 18)');
|
||||
expect(sql).toContain('("u"."name" = ? AND "u"."age" < 60)');
|
||||
expect(sql).toContain(' OR ');
|
||||
expect(params.filter((p) => p === 'a')).toHaveLength(2);
|
||||
} finally {
|
||||
await ds.destroy();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('applyToQueryBuilder', () => {
|
||||
it('单 EQ → andWhere("name = :p1", {p1:"xx"})', () => {
|
||||
const qb = createMockQb();
|
||||
|
||||
Reference in New Issue
Block a user