Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,22 @@ wServices['article'].some({

"where" parameters are ANDed with other filter parameters.

Use `CONTAINS` for array properties like `string[]` or enum arrays. You can pass one value or multiple values. Multiple values are combined with `or`.

```ts
wServices['article'].some({
where: {
tags: {
CONTAINS: ['sale', 'new']
}
}
});
```

This evaluates to:

tags contains "sale" or tags contains "new"

It is also possible to set an empty list within an IN-query:

```ts
Expand Down
57 changes: 49 additions & 8 deletions src/generator/01-base/static/queriesWithQueryLanguage.ts.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export type ComparisonOperator =

export type LengthOperator = 'LENGTH';

export type ArrayOperator = 'IN';
export type ArrayOperator = 'IN' | 'CONTAINS';

export type NullOperator = 'NULL';

Expand Down Expand Up @@ -96,28 +96,44 @@ export type LengthExprWithModifier =

export type FilterExpr<T> =
{ [K in ComparisonOperator]?: T } &
{ [K in ArrayOperator]?: T[] } &
{ IN?: T[] } &
{ [K in keyof LengthExpr]?: never };

export type ArrayFilterExpr<T> =
FilterExpr<T> &
{ CONTAINS?: T | ReadonlyArray<T> };

export type FilterExprWithoutNull<T> =
RequireAtLeastOne<FilterExpr<T>> &
{ [K in NullOperator]?: never } &
{ [K in ModifierFunction]?: boolean };

export type ArrayFilterExprWithoutNull<T> =
RequireAtLeastOne<ArrayFilterExpr<T>> &
{ [K in NullOperator]?: never } &
{ [K in ModifierFunction]?: boolean };

export type FilterExprWithNull<T> =
FilterExpr<T> &
{ [K in NullOperator]?: boolean} &
{ [K in ModifierFunction]?: never };

export type ArrayFilterExprWithNull<T> =
ArrayFilterExpr<T> &
{ [K in NullOperator]?: boolean} &
{ [K in ModifierFunction]?: never };

export type MapOperators<T> = LengthExprWithModifier | FilterExprWithoutNull<T> | FilterExprWithNull<T>;

export type ArrayMapOperators<T> = LengthExprWithModifier | ArrayFilterExprWithoutNull<T> | ArrayFilterExprWithNull<T>;

export type SingleFilterExpr<T> = {
[P in keyof T]?: T[P] extends Array<infer U> | undefined
? U extends Record<any, any>
? SingleFilterExpr<U> | { NOT?: SingleFilterExpr<U> }
: MapOperators<U>
: T[P] extends Record<any, any> | undefined
? SingleFilterExpr<T[P]> | { NOT?: SingleFilterExpr<T[P]> }
[P in keyof T]?: NonNullable<T[P]> extends ReadonlyArray<infer U>
? NonNullable<U> extends Record<any, any>
? SingleFilterExpr<NonNullable<U>> | { NOT?: SingleFilterExpr<NonNullable<U>> }
: ArrayMapOperators<NonNullable<U>>
: NonNullable<T[P]> extends Record<any, any>
? SingleFilterExpr<NonNullable<T[P]>> | { NOT?: SingleFilterExpr<NonNullable<T[P]>> }
: MapOperators<T[P]>;
};

Expand Down Expand Up @@ -194,6 +210,7 @@ const comparisonOperatorMap: Record<Operator, string> = {
GE: '>=',
LIKE: '~',
IN: 'in',
CONTAINS: 'contains',
NULL: 'null'
};

Expand Down Expand Up @@ -280,6 +297,30 @@ const flattenWhere = (
)}]`
);
}
} else if ((operator as Operator) === 'CONTAINS') {
const property = setModifiers.reduce(
(acc, [first]) => `${first.toLowerCase()}(${acc})`,
nestedPaths.some((path) => path === prop) ? nestedPaths.join('.') : [...nestedPaths, prop].join('.')
);
const containsValues = Array.isArray(value) ? value : [value];
const formatContainsValue = (containsValue: string | number) =>
typeof containsValue === 'string'
? setModifiers.reduce((acc, [first]) => `${first}(${acc})`, JSON.stringify(containsValue))
: containsValue;

if (containsValues.length === 0) {
entries.push('1 = 0');
} else if (containsValues.length === 1) {
entries.push(
`${property} ${comparisonOperatorMap[operator as Operator]} ${formatContainsValue(containsValues[0])}`
);
} else {
entries.push(
`(${containsValues
.map((containsValue) => `${property} ${comparisonOperatorMap[operator as Operator]} ${formatContainsValue(containsValue)}`)
.join(' or ')})`
);
}
} else if ((operator as LengthOperator) === 'LENGTH') {
const lengthProp = `length(${setModifiers.reduce(
(acc, [first]) => `${first.toLowerCase()}(${acc})`,
Expand Down
Loading