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 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
- let likeValue = opValue;
1875
- if (op === "contains") {
1876
- likeValue = `%${likeValue}%`;
1877
- } else if (op === "startsWith") {
1878
- likeValue = `${likeValue}%`;
1879
- } else if (op === "endsWith") {
1880
- likeValue = `%${likeValue}`;
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 buildLikeOperatorStatement({ repositoriesByModelNameLowered, model, propertyName, isNegated, value, params, joins }) {
1904
- if (Array.isArray(value)) {
1905
- if (!value.length) {
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
- repositoriesByModelNameLowered
1944
- });
1945
- let column;
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
- column = localColumn;
1956
- tablePrefix = "";
1957
- }
1958
- if (value === null) {
1959
- return `${tablePrefix}"${column.name}" ${isNegated ? "IS NOT" : "IS"} NULL`;
1960
- }
1961
- if (typeof value === "string") {
1962
- if (value) {
1963
- params.push(value);
1964
- const columnType = column.type?.toLowerCase();
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 $${params.length})`;
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 $${params.length}`;
2067
+ return `${tablePrefix}"${column.name}"${isNegated ? " NOT" : ""} ILIKE $${paramsList.length}`;
1969
2068
  }
1970
- return `${tablePrefix}"${column.name}" ${isNegated ? "!=" : "="} ''`;
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
- let likeValue = opValue;
1873
- if (op === "contains") {
1874
- likeValue = `%${likeValue}%`;
1875
- } else if (op === "startsWith") {
1876
- likeValue = `${likeValue}%`;
1877
- } else if (op === "endsWith") {
1878
- likeValue = `%${likeValue}`;
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 buildLikeOperatorStatement({ repositoriesByModelNameLowered, model, propertyName, isNegated, value, params, joins }) {
1902
- if (Array.isArray(value)) {
1903
- if (!value.length) {
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
- repositoriesByModelNameLowered
1942
- });
1943
- let column;
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
- column = localColumn;
1954
- tablePrefix = "";
1955
- }
1956
- if (value === null) {
1957
- return `${tablePrefix}"${column.name}" ${isNegated ? "IS NOT" : "IS"} NULL`;
1958
- }
1959
- if (typeof value === "string") {
1960
- if (value) {
1961
- params.push(value);
1962
- const columnType = column.type?.toLowerCase();
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 $${params.length})`;
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 $${params.length}`;
2065
+ return `${tablePrefix}"${column.name}"${isNegated ? " NOT" : ""} ILIKE $${paramsList.length}`;
1967
2066
  }
1968
- return `${tablePrefix}"${column.name}" ${isNegated ? "!=" : "="} ''`;
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bigal",
3
- "version": "15.6.0",
3
+ "version": "15.7.0",
4
4
  "description": "A fast and lightweight orm for postgres and node.js, written in typescript.",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",