chore: 版本 0.1.1 更新 toFindOptionsWhere 支持深层 OR 嵌套分配律展开
Node.js Build / build (push) Successful in 6s

This commit is contained in:
2026-08-07 11:36:07 +08:00
parent 324b995f05
commit bafa6198e5
6 changed files with 670 additions and 67 deletions
+114
View File
@@ -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();