bigal 15.6.0 → 15.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/dist/index.cjs +171 -74
- package/dist/index.d.cts +3 -2
- package/dist/index.d.mts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.mjs +171 -74
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
# [15.7.0](https://github.com/bigalorm/bigal/compare/v15.6.0...v15.7.0) (2026-01-19)
|
|
2
|
+
|
|
3
|
+
### Features
|
|
4
|
+
|
|
5
|
+
- Support contains for json columns ([#279](https://github.com/bigalorm/bigal/issues/279)) ([229ea23](https://github.com/bigalorm/bigal/commit/229ea23b9dc90e48913f08578d6be1258df84896))
|
|
6
|
+
|
|
1
7
|
# [15.6.0](https://github.com/bigalorm/bigal/compare/v15.5.0...v15.6.0) (2026-01-18)
|
|
2
8
|
|
|
3
9
|
### Features
|
package/dist/index.cjs
CHANGED
|
@@ -1296,7 +1296,21 @@ function buildWhere({
|
|
|
1296
1296
|
params,
|
|
1297
1297
|
joins
|
|
1298
1298
|
});
|
|
1299
|
-
case "contains":
|
|
1299
|
+
case "contains": {
|
|
1300
|
+
const containsColumn = propertyName ? model.columnsByPropertyName[propertyName] : null;
|
|
1301
|
+
const containsColumnType = containsColumn?.type?.toLowerCase();
|
|
1302
|
+
if (containsColumnType === "json") {
|
|
1303
|
+
return buildJsonContainmentStatement({
|
|
1304
|
+
repositoriesByModelNameLowered,
|
|
1305
|
+
model,
|
|
1306
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1307
|
+
propertyName,
|
|
1308
|
+
isNegated,
|
|
1309
|
+
value,
|
|
1310
|
+
params,
|
|
1311
|
+
joins
|
|
1312
|
+
});
|
|
1313
|
+
}
|
|
1300
1314
|
if (Array.isArray(value)) {
|
|
1301
1315
|
const values = value.map((val) => {
|
|
1302
1316
|
if (typeof val !== "string") {
|
|
@@ -1328,6 +1342,7 @@ function buildWhere({
|
|
|
1328
1342
|
});
|
|
1329
1343
|
}
|
|
1330
1344
|
throw new QueryError(`Expected value to be a string for "contains" constraint. Property (${propertyName ?? ""}) in model (${model.name}).`, model);
|
|
1345
|
+
}
|
|
1331
1346
|
case "startsWith":
|
|
1332
1347
|
if (Array.isArray(value)) {
|
|
1333
1348
|
const values = value.map((val) => {
|
|
@@ -1800,6 +1815,70 @@ function resolvePropertyPath({
|
|
|
1800
1815
|
tableAlias: matchingJoin.alias
|
|
1801
1816
|
};
|
|
1802
1817
|
}
|
|
1818
|
+
function buildArrayOrSingleStatement({
|
|
1819
|
+
repositoriesByModelNameLowered,
|
|
1820
|
+
model,
|
|
1821
|
+
propertyName,
|
|
1822
|
+
isNegated,
|
|
1823
|
+
value,
|
|
1824
|
+
params,
|
|
1825
|
+
joins,
|
|
1826
|
+
buildNonNullStatement
|
|
1827
|
+
}) {
|
|
1828
|
+
if (Array.isArray(value)) {
|
|
1829
|
+
if (!value.length) {
|
|
1830
|
+
return isNegated ? "1=1" : "1<>1";
|
|
1831
|
+
}
|
|
1832
|
+
if (value.length > 1) {
|
|
1833
|
+
const orConstraints = [];
|
|
1834
|
+
for (const item of value) {
|
|
1835
|
+
orConstraints.push(
|
|
1836
|
+
buildArrayOrSingleStatement({
|
|
1837
|
+
repositoriesByModelNameLowered,
|
|
1838
|
+
model,
|
|
1839
|
+
propertyName,
|
|
1840
|
+
isNegated,
|
|
1841
|
+
value: item,
|
|
1842
|
+
params,
|
|
1843
|
+
joins,
|
|
1844
|
+
buildNonNullStatement
|
|
1845
|
+
})
|
|
1846
|
+
);
|
|
1847
|
+
}
|
|
1848
|
+
if (orConstraints.length === 1) {
|
|
1849
|
+
return orConstraints[0] ?? "";
|
|
1850
|
+
}
|
|
1851
|
+
if (isNegated) {
|
|
1852
|
+
return orConstraints.join(" AND ");
|
|
1853
|
+
}
|
|
1854
|
+
return orConstraints.length ? `(${orConstraints.join(" OR ")})` : "";
|
|
1855
|
+
}
|
|
1856
|
+
value = value[0];
|
|
1857
|
+
}
|
|
1858
|
+
const resolved = resolvePropertyPath({
|
|
1859
|
+
propertyPath: propertyName,
|
|
1860
|
+
model,
|
|
1861
|
+
joins,
|
|
1862
|
+
repositoriesByModelNameLowered
|
|
1863
|
+
});
|
|
1864
|
+
let column;
|
|
1865
|
+
let tablePrefix;
|
|
1866
|
+
if (resolved) {
|
|
1867
|
+
column = resolved.column;
|
|
1868
|
+
tablePrefix = `"${resolved.tableAlias}".`;
|
|
1869
|
+
} else {
|
|
1870
|
+
const localColumn = model.columnsByPropertyName[propertyName];
|
|
1871
|
+
if (!localColumn) {
|
|
1872
|
+
throw new QueryError(`Unable to find property ${propertyName} on model ${model.name}`, model);
|
|
1873
|
+
}
|
|
1874
|
+
column = localColumn;
|
|
1875
|
+
tablePrefix = "";
|
|
1876
|
+
}
|
|
1877
|
+
if (value === null) {
|
|
1878
|
+
return `${tablePrefix}"${column.name}" ${isNegated ? "IS NOT" : "IS"} NULL`;
|
|
1879
|
+
}
|
|
1880
|
+
return buildNonNullStatement({ column, tablePrefix }, value, params);
|
|
1881
|
+
}
|
|
1803
1882
|
function resolveJoinAlias({
|
|
1804
1883
|
aliasOrPropertyName,
|
|
1805
1884
|
model,
|
|
@@ -1866,21 +1945,64 @@ function buildNestedJoinWhere({
|
|
|
1866
1945
|
if (op === "!") {
|
|
1867
1946
|
if (opValue === null) {
|
|
1868
1947
|
andClauses.push(`"${tableAlias}"."${column.name}" IS NOT NULL`);
|
|
1948
|
+
} else if (typeof opValue === "object" && !Array.isArray(opValue)) {
|
|
1949
|
+
for (const [nestedOp, nestedOpValue] of Object.entries(opValue)) {
|
|
1950
|
+
if (nestedOp === "like" || nestedOp === "contains" || nestedOp === "startsWith" || nestedOp === "endsWith") {
|
|
1951
|
+
const negatedColumnType = column.type?.toLowerCase();
|
|
1952
|
+
if (negatedColumnType === "json") {
|
|
1953
|
+
if (nestedOp !== "contains") {
|
|
1954
|
+
throw new QueryError(
|
|
1955
|
+
`"${nestedOp}" operator is not supported for JSON columns. Use "contains" with an object value for JSON containment. Property (${key}) in model (${joinedModel.name}).`,
|
|
1956
|
+
joinedModel
|
|
1957
|
+
);
|
|
1958
|
+
}
|
|
1959
|
+
params.push(nestedOpValue);
|
|
1960
|
+
andClauses.push(`NOT "${tableAlias}"."${column.name}"@>$${params.length}::jsonb`);
|
|
1961
|
+
} else {
|
|
1962
|
+
let likeValue = nestedOpValue;
|
|
1963
|
+
if (nestedOp === "contains") {
|
|
1964
|
+
likeValue = `%${likeValue}%`;
|
|
1965
|
+
} else if (nestedOp === "startsWith") {
|
|
1966
|
+
likeValue = `${likeValue}%`;
|
|
1967
|
+
} else if (nestedOp === "endsWith") {
|
|
1968
|
+
likeValue = `%${likeValue}`;
|
|
1969
|
+
}
|
|
1970
|
+
params.push(likeValue);
|
|
1971
|
+
andClauses.push(`"${tableAlias}"."${column.name}" NOT ILIKE $${params.length}`);
|
|
1972
|
+
}
|
|
1973
|
+
} else if (nestedOp === ">" || nestedOp === ">=" || nestedOp === "<" || nestedOp === "<=") {
|
|
1974
|
+
params.push(nestedOpValue);
|
|
1975
|
+
const negatedComparisonOps = { ">": "<=", ">=": "<", "<": ">=", "<=": ">" };
|
|
1976
|
+
andClauses.push(`"${tableAlias}"."${column.name}"${negatedComparisonOps[nestedOp]}$${params.length}`);
|
|
1977
|
+
}
|
|
1978
|
+
}
|
|
1869
1979
|
} else {
|
|
1870
1980
|
params.push(opValue);
|
|
1871
1981
|
andClauses.push(`"${tableAlias}"."${column.name}"<>$${params.length}`);
|
|
1872
1982
|
}
|
|
1873
1983
|
} else if (op === "like" || op === "contains" || op === "startsWith" || op === "endsWith") {
|
|
1874
|
-
|
|
1875
|
-
if (
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1984
|
+
const nestedColumnType = column.type?.toLowerCase();
|
|
1985
|
+
if (nestedColumnType === "json") {
|
|
1986
|
+
if (op !== "contains") {
|
|
1987
|
+
throw new QueryError(
|
|
1988
|
+
`"${op}" operator is not supported for JSON columns. Use "contains" with an object value for JSON containment. Property (${key}) in model (${joinedModel.name}).`,
|
|
1989
|
+
joinedModel
|
|
1990
|
+
);
|
|
1991
|
+
}
|
|
1992
|
+
params.push(opValue);
|
|
1993
|
+
andClauses.push(`"${tableAlias}"."${column.name}"@>$${params.length}::jsonb`);
|
|
1994
|
+
} else {
|
|
1995
|
+
let likeValue = opValue;
|
|
1996
|
+
if (op === "contains") {
|
|
1997
|
+
likeValue = `%${likeValue}%`;
|
|
1998
|
+
} else if (op === "startsWith") {
|
|
1999
|
+
likeValue = `${likeValue}%`;
|
|
2000
|
+
} else if (op === "endsWith") {
|
|
2001
|
+
likeValue = `%${likeValue}`;
|
|
2002
|
+
}
|
|
2003
|
+
params.push(likeValue);
|
|
2004
|
+
andClauses.push(`"${tableAlias}"."${column.name}" ILIKE $${params.length}`);
|
|
1881
2005
|
}
|
|
1882
|
-
params.push(likeValue);
|
|
1883
|
-
andClauses.push(`"${tableAlias}"."${column.name}" ILIKE $${params.length}`);
|
|
1884
2006
|
} else if (op === ">" || op === ">=" || op === "<" || op === "<=") {
|
|
1885
2007
|
params.push(opValue);
|
|
1886
2008
|
andClauses.push(`"${tableAlias}"."${column.name}"${op}$${params.length}`);
|
|
@@ -1900,76 +2022,51 @@ function buildNestedJoinWhere({
|
|
|
1900
2022
|
}
|
|
1901
2023
|
return andClauses.join(" AND ");
|
|
1902
2024
|
}
|
|
1903
|
-
function
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
if (isNegated) {
|
|
1907
|
-
return "1=1";
|
|
1908
|
-
}
|
|
1909
|
-
return "1<>1";
|
|
1910
|
-
}
|
|
1911
|
-
if (value.length > 1) {
|
|
1912
|
-
const orConstraints = [];
|
|
1913
|
-
for (const item of value) {
|
|
1914
|
-
orConstraints.push(
|
|
1915
|
-
buildLikeOperatorStatement({
|
|
1916
|
-
repositoriesByModelNameLowered,
|
|
1917
|
-
model,
|
|
1918
|
-
propertyName,
|
|
1919
|
-
isNegated,
|
|
1920
|
-
value: item,
|
|
1921
|
-
params,
|
|
1922
|
-
joins
|
|
1923
|
-
})
|
|
1924
|
-
);
|
|
1925
|
-
}
|
|
1926
|
-
if (orConstraints.length === 1) {
|
|
1927
|
-
return orConstraints[0] ?? "";
|
|
1928
|
-
}
|
|
1929
|
-
if (isNegated) {
|
|
1930
|
-
return orConstraints.join(" AND ");
|
|
1931
|
-
}
|
|
1932
|
-
if (orConstraints.length) {
|
|
1933
|
-
return `(${orConstraints.join(" OR ")})`;
|
|
1934
|
-
}
|
|
1935
|
-
return "";
|
|
1936
|
-
}
|
|
1937
|
-
value = value[0];
|
|
1938
|
-
}
|
|
1939
|
-
const resolved = resolvePropertyPath({
|
|
1940
|
-
propertyPath: propertyName,
|
|
2025
|
+
function buildJsonContainmentStatement({ repositoriesByModelNameLowered, model, propertyName, isNegated, value, params, joins }) {
|
|
2026
|
+
return buildArrayOrSingleStatement({
|
|
2027
|
+
repositoriesByModelNameLowered,
|
|
1941
2028
|
model,
|
|
2029
|
+
propertyName,
|
|
2030
|
+
isNegated,
|
|
2031
|
+
value,
|
|
2032
|
+
params,
|
|
1942
2033
|
joins,
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
let tablePrefix;
|
|
1947
|
-
if (resolved) {
|
|
1948
|
-
column = resolved.column;
|
|
1949
|
-
tablePrefix = `"${resolved.tableAlias}".`;
|
|
1950
|
-
} else {
|
|
1951
|
-
const localColumn = model.columnsByPropertyName[propertyName];
|
|
1952
|
-
if (!localColumn) {
|
|
1953
|
-
throw new QueryError(`Unable to find property ${propertyName} on model ${model.name}`, model);
|
|
2034
|
+
buildNonNullStatement({ column, tablePrefix }, nonNullValue, paramsList) {
|
|
2035
|
+
paramsList.push(nonNullValue);
|
|
2036
|
+
return `${isNegated ? "NOT " : ""}${tablePrefix}"${column.name}"@>$${paramsList.length}::jsonb`;
|
|
1954
2037
|
}
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
2038
|
+
});
|
|
2039
|
+
}
|
|
2040
|
+
function buildLikeOperatorStatement({ repositoriesByModelNameLowered, model, propertyName, isNegated, value, params, joins }) {
|
|
2041
|
+
return buildArrayOrSingleStatement({
|
|
2042
|
+
repositoriesByModelNameLowered,
|
|
2043
|
+
model,
|
|
2044
|
+
propertyName,
|
|
2045
|
+
isNegated,
|
|
2046
|
+
value,
|
|
2047
|
+
params,
|
|
2048
|
+
joins,
|
|
2049
|
+
buildNonNullStatement({ column, tablePrefix }, nonNullValue, paramsList) {
|
|
2050
|
+
if (typeof nonNullValue !== "string") {
|
|
2051
|
+
throw new QueryError(`Expected value to be a string for "like" constraint. Property (${propertyName}) in model (${model.name}).`, model);
|
|
2052
|
+
}
|
|
2053
|
+
if (!nonNullValue) {
|
|
2054
|
+
return `${tablePrefix}"${column.name}" ${isNegated ? "!=" : "="} ''`;
|
|
2055
|
+
}
|
|
2056
|
+
paramsList.push(nonNullValue);
|
|
2057
|
+
const columnType = "type" in column && typeof column.type === "string" ? column.type.toLowerCase() : void 0;
|
|
2058
|
+
if (columnType === "json") {
|
|
2059
|
+
throw new QueryError(
|
|
2060
|
+
`"like" operator is not supported for JSON columns. Use "contains" with an object value for JSON containment. Property (${propertyName}) in model (${model.name}).`,
|
|
2061
|
+
model
|
|
2062
|
+
);
|
|
2063
|
+
}
|
|
1965
2064
|
if (columnType === "array" || columnType === "string[]") {
|
|
1966
|
-
return `${isNegated ? "NOT " : ""}EXISTS(SELECT 1 FROM (SELECT unnest(${tablePrefix}"${column.name}") AS "unnested_${column.name}") __unnested WHERE "unnested_${column.name}" ILIKE $${
|
|
2065
|
+
return `${isNegated ? "NOT " : ""}EXISTS(SELECT 1 FROM (SELECT unnest(${tablePrefix}"${column.name}") AS "unnested_${column.name}") __unnested WHERE "unnested_${column.name}" ILIKE $${paramsList.length})`;
|
|
1967
2066
|
}
|
|
1968
|
-
return `${tablePrefix}"${column.name}"${isNegated ? " NOT" : ""} ILIKE $${
|
|
2067
|
+
return `${tablePrefix}"${column.name}"${isNegated ? " NOT" : ""} ILIKE $${paramsList.length}`;
|
|
1969
2068
|
}
|
|
1970
|
-
|
|
1971
|
-
}
|
|
1972
|
-
throw new QueryError(`Expected value to be a string for "like" constraint. Property (${propertyName}) in model (${model.name}).`, model);
|
|
2069
|
+
});
|
|
1973
2070
|
}
|
|
1974
2071
|
function buildComparisonOperatorStatement({
|
|
1975
2072
|
repositoriesByModelNameLowered,
|
package/dist/index.d.cts
CHANGED
|
@@ -885,6 +885,7 @@ type ExcludeUndefined<T> = Exclude<T, undefined>;
|
|
|
885
885
|
type LiteralValues<TValue> = (TValue | null)[] | TValue | null;
|
|
886
886
|
type WhereClauseValue<TValue> = TValue extends NotEntityBrand | undefined ? Exclude<TValue, NotEntityBrand | undefined> : Extract<TValue, Entity> extends undefined ? LiteralValues<ExcludeUndefined<TValue>> : (ExcludeUndefined<Exclude<TValue, Entity>> | null)[] | (Pick<Extract<ExcludeUndefined<TValue>, Entity>, 'id'> | null)[] | ExcludeUndefined<Exclude<TValue, Entity>> | Pick<Extract<ExcludeUndefined<TValue>, Entity>, 'id'> | null;
|
|
887
887
|
type StringConstraint<TValue extends string> = Partial<Record<'contains' | 'endsWith' | 'like' | 'startsWith', LiteralValues<ExcludeUndefined<TValue>>>>;
|
|
888
|
+
type JsonConstraint<TValue> = Partial<Record<'contains', ExcludeUndefined<TValue> | LiteralValues<ExcludeUndefined<TValue>>>>;
|
|
888
889
|
type NumberOrDateConstraint<TValue extends Date | number> = Partial<Record<'<' | '<=' | '>' | '>=', LiteralValues<ExcludeUndefined<TValue>>>>;
|
|
889
890
|
interface SubqueryInConstraint {
|
|
890
891
|
in: SubqueryBuilderLike;
|
|
@@ -894,7 +895,7 @@ type NumberOrDateConstraintWithSubquery<TValue extends Date | number> = NumberOr
|
|
|
894
895
|
type NegatableConstraint<TValue> = TValue | {
|
|
895
896
|
'!': TValue;
|
|
896
897
|
};
|
|
897
|
-
type WhereQueryStatement<TValue> = [TValue] extends [string] ? NegatableConstraint<StringConstraint<TValue> | SubqueryInConstraint | WhereClauseValue<TValue>> : TValue extends string ? NegatableConstraint<StringConstraint<TValue> | SubqueryInConstraint | WhereClauseValue<TValue>> : TValue extends Date | number ? NegatableConstraint<NumberOrDateConstraintWithSubquery<TValue> | SubqueryInConstraint | WhereClauseValue<TValue>> : NegatableConstraint<SubqueryInConstraint | WhereClauseValue<TValue>>;
|
|
898
|
+
type WhereQueryStatement<TValue> = [TValue] extends [string] ? NegatableConstraint<StringConstraint<TValue> | SubqueryInConstraint | WhereClauseValue<TValue>> : TValue extends string ? NegatableConstraint<StringConstraint<TValue> | SubqueryInConstraint | WhereClauseValue<TValue>> : TValue extends Date | number ? NegatableConstraint<NumberOrDateConstraintWithSubquery<TValue> | SubqueryInConstraint | WhereClauseValue<TValue>> : NegatableConstraint<JsonConstraint<TValue> | SubqueryInConstraint | WhereClauseValue<TValue>>;
|
|
898
899
|
type WhereQuery<T extends Entity> = {
|
|
899
900
|
[K in keyof T as ExcludeEntityCollections<T[K], ExcludeFunctions<T[K], K>>]?: K extends 'id' ? WhereQueryStatement<T | T[K]> : T[K] extends (infer U)[] | undefined ? WhereQueryStatement<ExcludeUndefined<U>> : NegatableConstraint<LiteralValues<ExcludeUndefined<T[K]>>> | WhereQueryStatement<ExcludeUndefined<T[K]>>;
|
|
900
901
|
} & {
|
|
@@ -1197,4 +1198,4 @@ interface InitializeOptions extends IConnection {
|
|
|
1197
1198
|
declare function initialize({ models, pool, readonlyPool, connections, expose }: InitializeOptions): Record<string, IReadonlyRepository<Entity> | IRepository<Entity>>;
|
|
1198
1199
|
|
|
1199
1200
|
export { ColumnBaseMetadata, ColumnCollectionMetadata, ColumnModelMetadata, ColumnTypeMetadata, Entity, ModelMetadata, QueryError, ReadonlyRepository, Repository, ScalarSubquery, SelectBuilder, SubqueryBuilder, column, createDateColumn, getMetadataStorage, initialize, isSubqueryJoin, primaryColumn, subquery, table, updateDateColumn, versionColumn };
|
|
1200
|
-
export type { AggregateBuilder, ClassLike, ColumnBaseMetadataOptions, ColumnCollectionMetadataOptions, ColumnMetadata, ColumnModelMetadataOptions, ColumnModifierMetadata, ColumnTypeMetadataOptions, Comparer, CountResult, CreateUpdateOptions, CreateUpdateParams, DeleteOptions, DestroyResult, DoNotReturnRecords, EntityFieldValue, EntityPrimitiveOrId, EntityStatic, ExcludeEntityCollections, ExcludeFunctions, FindArgs, FindOneArgs, FindOneResult, FindOneResultJSON, FindQueryWithCount, FindQueryWithCountJSON, FindResult, FindResultJSON, FindWithCountResult, GetValueType, HavingComparer, HavingCondition, IConnection, IReadonlyRepository, IRepository, IRepositoryOptions, IncludeFunctions, InitializeOptions, IsValueOfType, JoinDefinition, JoinInfo, JoinType, JoinedSort, JoinedWhereQuery, LiteralValues, ModelJoinDefinition, ModelMetadataOptions, ModelRelationshipKeys, MultipleSortString, NegatableConstraint, NotEntity, NotEntityBrand, NumberOrDateConstraint, NumberOrDateConstraintWithSubquery, OmitEntityCollections, OmitFunctions, OrderBy, PaginateOptions, PickAsType, PickByValueType, PickFunctions, PlainObject, PoolLike, PoolQueryResult, PopulateArgs, Populated, QueryResult, QueryResultOptionalPopulated, QueryResultPopulated, QueryResultRow, ReturnSelect$1 as ReturnSelect, ScalarSubqueryConstraint, SelectAggregateExpression, SelectItem, Sort, SortObject, SortObjectValue, SortString, StringConstraint, SubqueryBuilderLike, SubqueryInConstraint, SubqueryJoinDefinition, SubqueryJoinOnCondition, WhereClauseValue, WhereQuery, WhereQueryStatement };
|
|
1201
|
+
export type { AggregateBuilder, ClassLike, ColumnBaseMetadataOptions, ColumnCollectionMetadataOptions, ColumnMetadata, ColumnModelMetadataOptions, ColumnModifierMetadata, ColumnTypeMetadataOptions, Comparer, CountResult, CreateUpdateOptions, CreateUpdateParams, DeleteOptions, DestroyResult, DoNotReturnRecords, EntityFieldValue, EntityPrimitiveOrId, EntityStatic, ExcludeEntityCollections, ExcludeFunctions, FindArgs, FindOneArgs, FindOneResult, FindOneResultJSON, FindQueryWithCount, FindQueryWithCountJSON, FindResult, FindResultJSON, FindWithCountResult, GetValueType, HavingComparer, HavingCondition, IConnection, IReadonlyRepository, IRepository, IRepositoryOptions, IncludeFunctions, InitializeOptions, IsValueOfType, JoinDefinition, JoinInfo, JoinType, JoinedSort, JoinedWhereQuery, JsonConstraint, LiteralValues, ModelJoinDefinition, ModelMetadataOptions, ModelRelationshipKeys, MultipleSortString, NegatableConstraint, NotEntity, NotEntityBrand, NumberOrDateConstraint, NumberOrDateConstraintWithSubquery, OmitEntityCollections, OmitFunctions, OrderBy, PaginateOptions, PickAsType, PickByValueType, PickFunctions, PlainObject, PoolLike, PoolQueryResult, PopulateArgs, Populated, QueryResult, QueryResultOptionalPopulated, QueryResultPopulated, QueryResultRow, ReturnSelect$1 as ReturnSelect, ScalarSubqueryConstraint, SelectAggregateExpression, SelectItem, Sort, SortObject, SortObjectValue, SortString, StringConstraint, SubqueryBuilderLike, SubqueryInConstraint, SubqueryJoinDefinition, SubqueryJoinOnCondition, WhereClauseValue, WhereQuery, WhereQueryStatement };
|
package/dist/index.d.mts
CHANGED
|
@@ -885,6 +885,7 @@ type ExcludeUndefined<T> = Exclude<T, undefined>;
|
|
|
885
885
|
type LiteralValues<TValue> = (TValue | null)[] | TValue | null;
|
|
886
886
|
type WhereClauseValue<TValue> = TValue extends NotEntityBrand | undefined ? Exclude<TValue, NotEntityBrand | undefined> : Extract<TValue, Entity> extends undefined ? LiteralValues<ExcludeUndefined<TValue>> : (ExcludeUndefined<Exclude<TValue, Entity>> | null)[] | (Pick<Extract<ExcludeUndefined<TValue>, Entity>, 'id'> | null)[] | ExcludeUndefined<Exclude<TValue, Entity>> | Pick<Extract<ExcludeUndefined<TValue>, Entity>, 'id'> | null;
|
|
887
887
|
type StringConstraint<TValue extends string> = Partial<Record<'contains' | 'endsWith' | 'like' | 'startsWith', LiteralValues<ExcludeUndefined<TValue>>>>;
|
|
888
|
+
type JsonConstraint<TValue> = Partial<Record<'contains', ExcludeUndefined<TValue> | LiteralValues<ExcludeUndefined<TValue>>>>;
|
|
888
889
|
type NumberOrDateConstraint<TValue extends Date | number> = Partial<Record<'<' | '<=' | '>' | '>=', LiteralValues<ExcludeUndefined<TValue>>>>;
|
|
889
890
|
interface SubqueryInConstraint {
|
|
890
891
|
in: SubqueryBuilderLike;
|
|
@@ -894,7 +895,7 @@ type NumberOrDateConstraintWithSubquery<TValue extends Date | number> = NumberOr
|
|
|
894
895
|
type NegatableConstraint<TValue> = TValue | {
|
|
895
896
|
'!': TValue;
|
|
896
897
|
};
|
|
897
|
-
type WhereQueryStatement<TValue> = [TValue] extends [string] ? NegatableConstraint<StringConstraint<TValue> | SubqueryInConstraint | WhereClauseValue<TValue>> : TValue extends string ? NegatableConstraint<StringConstraint<TValue> | SubqueryInConstraint | WhereClauseValue<TValue>> : TValue extends Date | number ? NegatableConstraint<NumberOrDateConstraintWithSubquery<TValue> | SubqueryInConstraint | WhereClauseValue<TValue>> : NegatableConstraint<SubqueryInConstraint | WhereClauseValue<TValue>>;
|
|
898
|
+
type WhereQueryStatement<TValue> = [TValue] extends [string] ? NegatableConstraint<StringConstraint<TValue> | SubqueryInConstraint | WhereClauseValue<TValue>> : TValue extends string ? NegatableConstraint<StringConstraint<TValue> | SubqueryInConstraint | WhereClauseValue<TValue>> : TValue extends Date | number ? NegatableConstraint<NumberOrDateConstraintWithSubquery<TValue> | SubqueryInConstraint | WhereClauseValue<TValue>> : NegatableConstraint<JsonConstraint<TValue> | SubqueryInConstraint | WhereClauseValue<TValue>>;
|
|
898
899
|
type WhereQuery<T extends Entity> = {
|
|
899
900
|
[K in keyof T as ExcludeEntityCollections<T[K], ExcludeFunctions<T[K], K>>]?: K extends 'id' ? WhereQueryStatement<T | T[K]> : T[K] extends (infer U)[] | undefined ? WhereQueryStatement<ExcludeUndefined<U>> : NegatableConstraint<LiteralValues<ExcludeUndefined<T[K]>>> | WhereQueryStatement<ExcludeUndefined<T[K]>>;
|
|
900
901
|
} & {
|
|
@@ -1197,4 +1198,4 @@ interface InitializeOptions extends IConnection {
|
|
|
1197
1198
|
declare function initialize({ models, pool, readonlyPool, connections, expose }: InitializeOptions): Record<string, IReadonlyRepository<Entity> | IRepository<Entity>>;
|
|
1198
1199
|
|
|
1199
1200
|
export { ColumnBaseMetadata, ColumnCollectionMetadata, ColumnModelMetadata, ColumnTypeMetadata, Entity, ModelMetadata, QueryError, ReadonlyRepository, Repository, ScalarSubquery, SelectBuilder, SubqueryBuilder, column, createDateColumn, getMetadataStorage, initialize, isSubqueryJoin, primaryColumn, subquery, table, updateDateColumn, versionColumn };
|
|
1200
|
-
export type { AggregateBuilder, ClassLike, ColumnBaseMetadataOptions, ColumnCollectionMetadataOptions, ColumnMetadata, ColumnModelMetadataOptions, ColumnModifierMetadata, ColumnTypeMetadataOptions, Comparer, CountResult, CreateUpdateOptions, CreateUpdateParams, DeleteOptions, DestroyResult, DoNotReturnRecords, EntityFieldValue, EntityPrimitiveOrId, EntityStatic, ExcludeEntityCollections, ExcludeFunctions, FindArgs, FindOneArgs, FindOneResult, FindOneResultJSON, FindQueryWithCount, FindQueryWithCountJSON, FindResult, FindResultJSON, FindWithCountResult, GetValueType, HavingComparer, HavingCondition, IConnection, IReadonlyRepository, IRepository, IRepositoryOptions, IncludeFunctions, InitializeOptions, IsValueOfType, JoinDefinition, JoinInfo, JoinType, JoinedSort, JoinedWhereQuery, LiteralValues, ModelJoinDefinition, ModelMetadataOptions, ModelRelationshipKeys, MultipleSortString, NegatableConstraint, NotEntity, NotEntityBrand, NumberOrDateConstraint, NumberOrDateConstraintWithSubquery, OmitEntityCollections, OmitFunctions, OrderBy, PaginateOptions, PickAsType, PickByValueType, PickFunctions, PlainObject, PoolLike, PoolQueryResult, PopulateArgs, Populated, QueryResult, QueryResultOptionalPopulated, QueryResultPopulated, QueryResultRow, ReturnSelect$1 as ReturnSelect, ScalarSubqueryConstraint, SelectAggregateExpression, SelectItem, Sort, SortObject, SortObjectValue, SortString, StringConstraint, SubqueryBuilderLike, SubqueryInConstraint, SubqueryJoinDefinition, SubqueryJoinOnCondition, WhereClauseValue, WhereQuery, WhereQueryStatement };
|
|
1201
|
+
export type { AggregateBuilder, ClassLike, ColumnBaseMetadataOptions, ColumnCollectionMetadataOptions, ColumnMetadata, ColumnModelMetadataOptions, ColumnModifierMetadata, ColumnTypeMetadataOptions, Comparer, CountResult, CreateUpdateOptions, CreateUpdateParams, DeleteOptions, DestroyResult, DoNotReturnRecords, EntityFieldValue, EntityPrimitiveOrId, EntityStatic, ExcludeEntityCollections, ExcludeFunctions, FindArgs, FindOneArgs, FindOneResult, FindOneResultJSON, FindQueryWithCount, FindQueryWithCountJSON, FindResult, FindResultJSON, FindWithCountResult, GetValueType, HavingComparer, HavingCondition, IConnection, IReadonlyRepository, IRepository, IRepositoryOptions, IncludeFunctions, InitializeOptions, IsValueOfType, JoinDefinition, JoinInfo, JoinType, JoinedSort, JoinedWhereQuery, JsonConstraint, LiteralValues, ModelJoinDefinition, ModelMetadataOptions, ModelRelationshipKeys, MultipleSortString, NegatableConstraint, NotEntity, NotEntityBrand, NumberOrDateConstraint, NumberOrDateConstraintWithSubquery, OmitEntityCollections, OmitFunctions, OrderBy, PaginateOptions, PickAsType, PickByValueType, PickFunctions, PlainObject, PoolLike, PoolQueryResult, PopulateArgs, Populated, QueryResult, QueryResultOptionalPopulated, QueryResultPopulated, QueryResultRow, ReturnSelect$1 as ReturnSelect, ScalarSubqueryConstraint, SelectAggregateExpression, SelectItem, Sort, SortObject, SortObjectValue, SortString, StringConstraint, SubqueryBuilderLike, SubqueryInConstraint, SubqueryJoinDefinition, SubqueryJoinOnCondition, WhereClauseValue, WhereQuery, WhereQueryStatement };
|
package/dist/index.d.ts
CHANGED
|
@@ -885,6 +885,7 @@ type ExcludeUndefined<T> = Exclude<T, undefined>;
|
|
|
885
885
|
type LiteralValues<TValue> = (TValue | null)[] | TValue | null;
|
|
886
886
|
type WhereClauseValue<TValue> = TValue extends NotEntityBrand | undefined ? Exclude<TValue, NotEntityBrand | undefined> : Extract<TValue, Entity> extends undefined ? LiteralValues<ExcludeUndefined<TValue>> : (ExcludeUndefined<Exclude<TValue, Entity>> | null)[] | (Pick<Extract<ExcludeUndefined<TValue>, Entity>, 'id'> | null)[] | ExcludeUndefined<Exclude<TValue, Entity>> | Pick<Extract<ExcludeUndefined<TValue>, Entity>, 'id'> | null;
|
|
887
887
|
type StringConstraint<TValue extends string> = Partial<Record<'contains' | 'endsWith' | 'like' | 'startsWith', LiteralValues<ExcludeUndefined<TValue>>>>;
|
|
888
|
+
type JsonConstraint<TValue> = Partial<Record<'contains', ExcludeUndefined<TValue> | LiteralValues<ExcludeUndefined<TValue>>>>;
|
|
888
889
|
type NumberOrDateConstraint<TValue extends Date | number> = Partial<Record<'<' | '<=' | '>' | '>=', LiteralValues<ExcludeUndefined<TValue>>>>;
|
|
889
890
|
interface SubqueryInConstraint {
|
|
890
891
|
in: SubqueryBuilderLike;
|
|
@@ -894,7 +895,7 @@ type NumberOrDateConstraintWithSubquery<TValue extends Date | number> = NumberOr
|
|
|
894
895
|
type NegatableConstraint<TValue> = TValue | {
|
|
895
896
|
'!': TValue;
|
|
896
897
|
};
|
|
897
|
-
type WhereQueryStatement<TValue> = [TValue] extends [string] ? NegatableConstraint<StringConstraint<TValue> | SubqueryInConstraint | WhereClauseValue<TValue>> : TValue extends string ? NegatableConstraint<StringConstraint<TValue> | SubqueryInConstraint | WhereClauseValue<TValue>> : TValue extends Date | number ? NegatableConstraint<NumberOrDateConstraintWithSubquery<TValue> | SubqueryInConstraint | WhereClauseValue<TValue>> : NegatableConstraint<SubqueryInConstraint | WhereClauseValue<TValue>>;
|
|
898
|
+
type WhereQueryStatement<TValue> = [TValue] extends [string] ? NegatableConstraint<StringConstraint<TValue> | SubqueryInConstraint | WhereClauseValue<TValue>> : TValue extends string ? NegatableConstraint<StringConstraint<TValue> | SubqueryInConstraint | WhereClauseValue<TValue>> : TValue extends Date | number ? NegatableConstraint<NumberOrDateConstraintWithSubquery<TValue> | SubqueryInConstraint | WhereClauseValue<TValue>> : NegatableConstraint<JsonConstraint<TValue> | SubqueryInConstraint | WhereClauseValue<TValue>>;
|
|
898
899
|
type WhereQuery<T extends Entity> = {
|
|
899
900
|
[K in keyof T as ExcludeEntityCollections<T[K], ExcludeFunctions<T[K], K>>]?: K extends 'id' ? WhereQueryStatement<T | T[K]> : T[K] extends (infer U)[] | undefined ? WhereQueryStatement<ExcludeUndefined<U>> : NegatableConstraint<LiteralValues<ExcludeUndefined<T[K]>>> | WhereQueryStatement<ExcludeUndefined<T[K]>>;
|
|
900
901
|
} & {
|
|
@@ -1197,4 +1198,4 @@ interface InitializeOptions extends IConnection {
|
|
|
1197
1198
|
declare function initialize({ models, pool, readonlyPool, connections, expose }: InitializeOptions): Record<string, IReadonlyRepository<Entity> | IRepository<Entity>>;
|
|
1198
1199
|
|
|
1199
1200
|
export { ColumnBaseMetadata, ColumnCollectionMetadata, ColumnModelMetadata, ColumnTypeMetadata, Entity, ModelMetadata, QueryError, ReadonlyRepository, Repository, ScalarSubquery, SelectBuilder, SubqueryBuilder, column, createDateColumn, getMetadataStorage, initialize, isSubqueryJoin, primaryColumn, subquery, table, updateDateColumn, versionColumn };
|
|
1200
|
-
export type { AggregateBuilder, ClassLike, ColumnBaseMetadataOptions, ColumnCollectionMetadataOptions, ColumnMetadata, ColumnModelMetadataOptions, ColumnModifierMetadata, ColumnTypeMetadataOptions, Comparer, CountResult, CreateUpdateOptions, CreateUpdateParams, DeleteOptions, DestroyResult, DoNotReturnRecords, EntityFieldValue, EntityPrimitiveOrId, EntityStatic, ExcludeEntityCollections, ExcludeFunctions, FindArgs, FindOneArgs, FindOneResult, FindOneResultJSON, FindQueryWithCount, FindQueryWithCountJSON, FindResult, FindResultJSON, FindWithCountResult, GetValueType, HavingComparer, HavingCondition, IConnection, IReadonlyRepository, IRepository, IRepositoryOptions, IncludeFunctions, InitializeOptions, IsValueOfType, JoinDefinition, JoinInfo, JoinType, JoinedSort, JoinedWhereQuery, LiteralValues, ModelJoinDefinition, ModelMetadataOptions, ModelRelationshipKeys, MultipleSortString, NegatableConstraint, NotEntity, NotEntityBrand, NumberOrDateConstraint, NumberOrDateConstraintWithSubquery, OmitEntityCollections, OmitFunctions, OrderBy, PaginateOptions, PickAsType, PickByValueType, PickFunctions, PlainObject, PoolLike, PoolQueryResult, PopulateArgs, Populated, QueryResult, QueryResultOptionalPopulated, QueryResultPopulated, QueryResultRow, ReturnSelect$1 as ReturnSelect, ScalarSubqueryConstraint, SelectAggregateExpression, SelectItem, Sort, SortObject, SortObjectValue, SortString, StringConstraint, SubqueryBuilderLike, SubqueryInConstraint, SubqueryJoinDefinition, SubqueryJoinOnCondition, WhereClauseValue, WhereQuery, WhereQueryStatement };
|
|
1201
|
+
export type { AggregateBuilder, ClassLike, ColumnBaseMetadataOptions, ColumnCollectionMetadataOptions, ColumnMetadata, ColumnModelMetadataOptions, ColumnModifierMetadata, ColumnTypeMetadataOptions, Comparer, CountResult, CreateUpdateOptions, CreateUpdateParams, DeleteOptions, DestroyResult, DoNotReturnRecords, EntityFieldValue, EntityPrimitiveOrId, EntityStatic, ExcludeEntityCollections, ExcludeFunctions, FindArgs, FindOneArgs, FindOneResult, FindOneResultJSON, FindQueryWithCount, FindQueryWithCountJSON, FindResult, FindResultJSON, FindWithCountResult, GetValueType, HavingComparer, HavingCondition, IConnection, IReadonlyRepository, IRepository, IRepositoryOptions, IncludeFunctions, InitializeOptions, IsValueOfType, JoinDefinition, JoinInfo, JoinType, JoinedSort, JoinedWhereQuery, JsonConstraint, LiteralValues, ModelJoinDefinition, ModelMetadataOptions, ModelRelationshipKeys, MultipleSortString, NegatableConstraint, NotEntity, NotEntityBrand, NumberOrDateConstraint, NumberOrDateConstraintWithSubquery, OmitEntityCollections, OmitFunctions, OrderBy, PaginateOptions, PickAsType, PickByValueType, PickFunctions, PlainObject, PoolLike, PoolQueryResult, PopulateArgs, Populated, QueryResult, QueryResultOptionalPopulated, QueryResultPopulated, QueryResultRow, ReturnSelect$1 as ReturnSelect, ScalarSubqueryConstraint, SelectAggregateExpression, SelectItem, Sort, SortObject, SortObjectValue, SortString, StringConstraint, SubqueryBuilderLike, SubqueryInConstraint, SubqueryJoinDefinition, SubqueryJoinOnCondition, WhereClauseValue, WhereQuery, WhereQueryStatement };
|
package/dist/index.mjs
CHANGED
|
@@ -1294,7 +1294,21 @@ function buildWhere({
|
|
|
1294
1294
|
params,
|
|
1295
1295
|
joins
|
|
1296
1296
|
});
|
|
1297
|
-
case "contains":
|
|
1297
|
+
case "contains": {
|
|
1298
|
+
const containsColumn = propertyName ? model.columnsByPropertyName[propertyName] : null;
|
|
1299
|
+
const containsColumnType = containsColumn?.type?.toLowerCase();
|
|
1300
|
+
if (containsColumnType === "json") {
|
|
1301
|
+
return buildJsonContainmentStatement({
|
|
1302
|
+
repositoriesByModelNameLowered,
|
|
1303
|
+
model,
|
|
1304
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1305
|
+
propertyName,
|
|
1306
|
+
isNegated,
|
|
1307
|
+
value,
|
|
1308
|
+
params,
|
|
1309
|
+
joins
|
|
1310
|
+
});
|
|
1311
|
+
}
|
|
1298
1312
|
if (Array.isArray(value)) {
|
|
1299
1313
|
const values = value.map((val) => {
|
|
1300
1314
|
if (typeof val !== "string") {
|
|
@@ -1326,6 +1340,7 @@ function buildWhere({
|
|
|
1326
1340
|
});
|
|
1327
1341
|
}
|
|
1328
1342
|
throw new QueryError(`Expected value to be a string for "contains" constraint. Property (${propertyName ?? ""}) in model (${model.name}).`, model);
|
|
1343
|
+
}
|
|
1329
1344
|
case "startsWith":
|
|
1330
1345
|
if (Array.isArray(value)) {
|
|
1331
1346
|
const values = value.map((val) => {
|
|
@@ -1798,6 +1813,70 @@ function resolvePropertyPath({
|
|
|
1798
1813
|
tableAlias: matchingJoin.alias
|
|
1799
1814
|
};
|
|
1800
1815
|
}
|
|
1816
|
+
function buildArrayOrSingleStatement({
|
|
1817
|
+
repositoriesByModelNameLowered,
|
|
1818
|
+
model,
|
|
1819
|
+
propertyName,
|
|
1820
|
+
isNegated,
|
|
1821
|
+
value,
|
|
1822
|
+
params,
|
|
1823
|
+
joins,
|
|
1824
|
+
buildNonNullStatement
|
|
1825
|
+
}) {
|
|
1826
|
+
if (Array.isArray(value)) {
|
|
1827
|
+
if (!value.length) {
|
|
1828
|
+
return isNegated ? "1=1" : "1<>1";
|
|
1829
|
+
}
|
|
1830
|
+
if (value.length > 1) {
|
|
1831
|
+
const orConstraints = [];
|
|
1832
|
+
for (const item of value) {
|
|
1833
|
+
orConstraints.push(
|
|
1834
|
+
buildArrayOrSingleStatement({
|
|
1835
|
+
repositoriesByModelNameLowered,
|
|
1836
|
+
model,
|
|
1837
|
+
propertyName,
|
|
1838
|
+
isNegated,
|
|
1839
|
+
value: item,
|
|
1840
|
+
params,
|
|
1841
|
+
joins,
|
|
1842
|
+
buildNonNullStatement
|
|
1843
|
+
})
|
|
1844
|
+
);
|
|
1845
|
+
}
|
|
1846
|
+
if (orConstraints.length === 1) {
|
|
1847
|
+
return orConstraints[0] ?? "";
|
|
1848
|
+
}
|
|
1849
|
+
if (isNegated) {
|
|
1850
|
+
return orConstraints.join(" AND ");
|
|
1851
|
+
}
|
|
1852
|
+
return orConstraints.length ? `(${orConstraints.join(" OR ")})` : "";
|
|
1853
|
+
}
|
|
1854
|
+
value = value[0];
|
|
1855
|
+
}
|
|
1856
|
+
const resolved = resolvePropertyPath({
|
|
1857
|
+
propertyPath: propertyName,
|
|
1858
|
+
model,
|
|
1859
|
+
joins,
|
|
1860
|
+
repositoriesByModelNameLowered
|
|
1861
|
+
});
|
|
1862
|
+
let column;
|
|
1863
|
+
let tablePrefix;
|
|
1864
|
+
if (resolved) {
|
|
1865
|
+
column = resolved.column;
|
|
1866
|
+
tablePrefix = `"${resolved.tableAlias}".`;
|
|
1867
|
+
} else {
|
|
1868
|
+
const localColumn = model.columnsByPropertyName[propertyName];
|
|
1869
|
+
if (!localColumn) {
|
|
1870
|
+
throw new QueryError(`Unable to find property ${propertyName} on model ${model.name}`, model);
|
|
1871
|
+
}
|
|
1872
|
+
column = localColumn;
|
|
1873
|
+
tablePrefix = "";
|
|
1874
|
+
}
|
|
1875
|
+
if (value === null) {
|
|
1876
|
+
return `${tablePrefix}"${column.name}" ${isNegated ? "IS NOT" : "IS"} NULL`;
|
|
1877
|
+
}
|
|
1878
|
+
return buildNonNullStatement({ column, tablePrefix }, value, params);
|
|
1879
|
+
}
|
|
1801
1880
|
function resolveJoinAlias({
|
|
1802
1881
|
aliasOrPropertyName,
|
|
1803
1882
|
model,
|
|
@@ -1864,21 +1943,64 @@ function buildNestedJoinWhere({
|
|
|
1864
1943
|
if (op === "!") {
|
|
1865
1944
|
if (opValue === null) {
|
|
1866
1945
|
andClauses.push(`"${tableAlias}"."${column.name}" IS NOT NULL`);
|
|
1946
|
+
} else if (typeof opValue === "object" && !Array.isArray(opValue)) {
|
|
1947
|
+
for (const [nestedOp, nestedOpValue] of Object.entries(opValue)) {
|
|
1948
|
+
if (nestedOp === "like" || nestedOp === "contains" || nestedOp === "startsWith" || nestedOp === "endsWith") {
|
|
1949
|
+
const negatedColumnType = column.type?.toLowerCase();
|
|
1950
|
+
if (negatedColumnType === "json") {
|
|
1951
|
+
if (nestedOp !== "contains") {
|
|
1952
|
+
throw new QueryError(
|
|
1953
|
+
`"${nestedOp}" operator is not supported for JSON columns. Use "contains" with an object value for JSON containment. Property (${key}) in model (${joinedModel.name}).`,
|
|
1954
|
+
joinedModel
|
|
1955
|
+
);
|
|
1956
|
+
}
|
|
1957
|
+
params.push(nestedOpValue);
|
|
1958
|
+
andClauses.push(`NOT "${tableAlias}"."${column.name}"@>$${params.length}::jsonb`);
|
|
1959
|
+
} else {
|
|
1960
|
+
let likeValue = nestedOpValue;
|
|
1961
|
+
if (nestedOp === "contains") {
|
|
1962
|
+
likeValue = `%${likeValue}%`;
|
|
1963
|
+
} else if (nestedOp === "startsWith") {
|
|
1964
|
+
likeValue = `${likeValue}%`;
|
|
1965
|
+
} else if (nestedOp === "endsWith") {
|
|
1966
|
+
likeValue = `%${likeValue}`;
|
|
1967
|
+
}
|
|
1968
|
+
params.push(likeValue);
|
|
1969
|
+
andClauses.push(`"${tableAlias}"."${column.name}" NOT ILIKE $${params.length}`);
|
|
1970
|
+
}
|
|
1971
|
+
} else if (nestedOp === ">" || nestedOp === ">=" || nestedOp === "<" || nestedOp === "<=") {
|
|
1972
|
+
params.push(nestedOpValue);
|
|
1973
|
+
const negatedComparisonOps = { ">": "<=", ">=": "<", "<": ">=", "<=": ">" };
|
|
1974
|
+
andClauses.push(`"${tableAlias}"."${column.name}"${negatedComparisonOps[nestedOp]}$${params.length}`);
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1867
1977
|
} else {
|
|
1868
1978
|
params.push(opValue);
|
|
1869
1979
|
andClauses.push(`"${tableAlias}"."${column.name}"<>$${params.length}`);
|
|
1870
1980
|
}
|
|
1871
1981
|
} else if (op === "like" || op === "contains" || op === "startsWith" || op === "endsWith") {
|
|
1872
|
-
|
|
1873
|
-
if (
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1982
|
+
const nestedColumnType = column.type?.toLowerCase();
|
|
1983
|
+
if (nestedColumnType === "json") {
|
|
1984
|
+
if (op !== "contains") {
|
|
1985
|
+
throw new QueryError(
|
|
1986
|
+
`"${op}" operator is not supported for JSON columns. Use "contains" with an object value for JSON containment. Property (${key}) in model (${joinedModel.name}).`,
|
|
1987
|
+
joinedModel
|
|
1988
|
+
);
|
|
1989
|
+
}
|
|
1990
|
+
params.push(opValue);
|
|
1991
|
+
andClauses.push(`"${tableAlias}"."${column.name}"@>$${params.length}::jsonb`);
|
|
1992
|
+
} else {
|
|
1993
|
+
let likeValue = opValue;
|
|
1994
|
+
if (op === "contains") {
|
|
1995
|
+
likeValue = `%${likeValue}%`;
|
|
1996
|
+
} else if (op === "startsWith") {
|
|
1997
|
+
likeValue = `${likeValue}%`;
|
|
1998
|
+
} else if (op === "endsWith") {
|
|
1999
|
+
likeValue = `%${likeValue}`;
|
|
2000
|
+
}
|
|
2001
|
+
params.push(likeValue);
|
|
2002
|
+
andClauses.push(`"${tableAlias}"."${column.name}" ILIKE $${params.length}`);
|
|
1879
2003
|
}
|
|
1880
|
-
params.push(likeValue);
|
|
1881
|
-
andClauses.push(`"${tableAlias}"."${column.name}" ILIKE $${params.length}`);
|
|
1882
2004
|
} else if (op === ">" || op === ">=" || op === "<" || op === "<=") {
|
|
1883
2005
|
params.push(opValue);
|
|
1884
2006
|
andClauses.push(`"${tableAlias}"."${column.name}"${op}$${params.length}`);
|
|
@@ -1898,76 +2020,51 @@ function buildNestedJoinWhere({
|
|
|
1898
2020
|
}
|
|
1899
2021
|
return andClauses.join(" AND ");
|
|
1900
2022
|
}
|
|
1901
|
-
function
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
if (isNegated) {
|
|
1905
|
-
return "1=1";
|
|
1906
|
-
}
|
|
1907
|
-
return "1<>1";
|
|
1908
|
-
}
|
|
1909
|
-
if (value.length > 1) {
|
|
1910
|
-
const orConstraints = [];
|
|
1911
|
-
for (const item of value) {
|
|
1912
|
-
orConstraints.push(
|
|
1913
|
-
buildLikeOperatorStatement({
|
|
1914
|
-
repositoriesByModelNameLowered,
|
|
1915
|
-
model,
|
|
1916
|
-
propertyName,
|
|
1917
|
-
isNegated,
|
|
1918
|
-
value: item,
|
|
1919
|
-
params,
|
|
1920
|
-
joins
|
|
1921
|
-
})
|
|
1922
|
-
);
|
|
1923
|
-
}
|
|
1924
|
-
if (orConstraints.length === 1) {
|
|
1925
|
-
return orConstraints[0] ?? "";
|
|
1926
|
-
}
|
|
1927
|
-
if (isNegated) {
|
|
1928
|
-
return orConstraints.join(" AND ");
|
|
1929
|
-
}
|
|
1930
|
-
if (orConstraints.length) {
|
|
1931
|
-
return `(${orConstraints.join(" OR ")})`;
|
|
1932
|
-
}
|
|
1933
|
-
return "";
|
|
1934
|
-
}
|
|
1935
|
-
value = value[0];
|
|
1936
|
-
}
|
|
1937
|
-
const resolved = resolvePropertyPath({
|
|
1938
|
-
propertyPath: propertyName,
|
|
2023
|
+
function buildJsonContainmentStatement({ repositoriesByModelNameLowered, model, propertyName, isNegated, value, params, joins }) {
|
|
2024
|
+
return buildArrayOrSingleStatement({
|
|
2025
|
+
repositoriesByModelNameLowered,
|
|
1939
2026
|
model,
|
|
2027
|
+
propertyName,
|
|
2028
|
+
isNegated,
|
|
2029
|
+
value,
|
|
2030
|
+
params,
|
|
1940
2031
|
joins,
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
let tablePrefix;
|
|
1945
|
-
if (resolved) {
|
|
1946
|
-
column = resolved.column;
|
|
1947
|
-
tablePrefix = `"${resolved.tableAlias}".`;
|
|
1948
|
-
} else {
|
|
1949
|
-
const localColumn = model.columnsByPropertyName[propertyName];
|
|
1950
|
-
if (!localColumn) {
|
|
1951
|
-
throw new QueryError(`Unable to find property ${propertyName} on model ${model.name}`, model);
|
|
2032
|
+
buildNonNullStatement({ column, tablePrefix }, nonNullValue, paramsList) {
|
|
2033
|
+
paramsList.push(nonNullValue);
|
|
2034
|
+
return `${isNegated ? "NOT " : ""}${tablePrefix}"${column.name}"@>$${paramsList.length}::jsonb`;
|
|
1952
2035
|
}
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
2036
|
+
});
|
|
2037
|
+
}
|
|
2038
|
+
function buildLikeOperatorStatement({ repositoriesByModelNameLowered, model, propertyName, isNegated, value, params, joins }) {
|
|
2039
|
+
return buildArrayOrSingleStatement({
|
|
2040
|
+
repositoriesByModelNameLowered,
|
|
2041
|
+
model,
|
|
2042
|
+
propertyName,
|
|
2043
|
+
isNegated,
|
|
2044
|
+
value,
|
|
2045
|
+
params,
|
|
2046
|
+
joins,
|
|
2047
|
+
buildNonNullStatement({ column, tablePrefix }, nonNullValue, paramsList) {
|
|
2048
|
+
if (typeof nonNullValue !== "string") {
|
|
2049
|
+
throw new QueryError(`Expected value to be a string for "like" constraint. Property (${propertyName}) in model (${model.name}).`, model);
|
|
2050
|
+
}
|
|
2051
|
+
if (!nonNullValue) {
|
|
2052
|
+
return `${tablePrefix}"${column.name}" ${isNegated ? "!=" : "="} ''`;
|
|
2053
|
+
}
|
|
2054
|
+
paramsList.push(nonNullValue);
|
|
2055
|
+
const columnType = "type" in column && typeof column.type === "string" ? column.type.toLowerCase() : void 0;
|
|
2056
|
+
if (columnType === "json") {
|
|
2057
|
+
throw new QueryError(
|
|
2058
|
+
`"like" operator is not supported for JSON columns. Use "contains" with an object value for JSON containment. Property (${propertyName}) in model (${model.name}).`,
|
|
2059
|
+
model
|
|
2060
|
+
);
|
|
2061
|
+
}
|
|
1963
2062
|
if (columnType === "array" || columnType === "string[]") {
|
|
1964
|
-
return `${isNegated ? "NOT " : ""}EXISTS(SELECT 1 FROM (SELECT unnest(${tablePrefix}"${column.name}") AS "unnested_${column.name}") __unnested WHERE "unnested_${column.name}" ILIKE $${
|
|
2063
|
+
return `${isNegated ? "NOT " : ""}EXISTS(SELECT 1 FROM (SELECT unnest(${tablePrefix}"${column.name}") AS "unnested_${column.name}") __unnested WHERE "unnested_${column.name}" ILIKE $${paramsList.length})`;
|
|
1965
2064
|
}
|
|
1966
|
-
return `${tablePrefix}"${column.name}"${isNegated ? " NOT" : ""} ILIKE $${
|
|
2065
|
+
return `${tablePrefix}"${column.name}"${isNegated ? " NOT" : ""} ILIKE $${paramsList.length}`;
|
|
1967
2066
|
}
|
|
1968
|
-
|
|
1969
|
-
}
|
|
1970
|
-
throw new QueryError(`Expected value to be a string for "like" constraint. Property (${propertyName}) in model (${model.name}).`, model);
|
|
2067
|
+
});
|
|
1971
2068
|
}
|
|
1972
2069
|
function buildComparisonOperatorStatement({
|
|
1973
2070
|
repositoriesByModelNameLowered,
|