UNPKG

@sergio9929/pb-query

Version:

A type-safe PocketBase query builder

666 lines 33.7 kB
//#region src/constants.d.ts declare const OPERATORS: { readonly equal: "="; readonly notEqual: "!="; readonly greaterThan: ">"; readonly greaterThanOrEqual: ">="; readonly lessThan: "<"; readonly lessThanOrEqual: "<="; readonly like: "~"; readonly notLike: "!~"; readonly anyEqual: "?="; readonly anyNotEqual: "?!="; readonly anyGreaterThan: "?>"; readonly anyGreaterThanOrEqual: "?>="; readonly anyLessThan: "?<"; readonly anyLessThanOrEqual: "?<="; readonly anyLike: "?~"; readonly anyNotLike: "?!~"; }; declare const DATETIME_MACROS: readonly ["@now", "@second", "@minute", "@hour", "@weekday", "@day", "@month", "@year", "@yesterday", "@tomorrow", "@todayStart", "@todayEnd", "@monthStart", "@monthEnd", "@yearStart", "@yearEnd"]; //#endregion //#region src/types.d.ts type FilterFunction = (raw: string, params?: { [key: string]: unknown; }) => string; type DatetimeMacro = (typeof DATETIME_MACROS)[number]; type RawQueryObject = { raw: string; values: Record<string, unknown>; }; type QueryResult<T = string> = { filter: T; fields: string; expand: string; sort: string; }; type GeoPoint = { lon: number; lat: number; }; type SortKey<K extends string> = `${K}` | `-${K}` | `+${K}`; type Exact<T, U, Y = unknown, N = never> = (<G>() => G extends T ? 1 : 2) extends (<G>() => G extends U ? 1 : 2) ? Y : N; type DepthCounter = [1, 2, 3, 4, 5, 6, never]; /** * Forces the IDE to display the type alias instead of the full type union. */ type ForceAlias<T> = T & {}; /** * A lazy way of doing `T[keyof T][number]`. */ type Elem<T> = T extends readonly (infer U)[] ? U : never; type PathHelper<T, K extends keyof T & string, TextField, DateField, GeoPointField, MultipleField, RelationField, MultipleRelationField, Other> = T[K] extends string ? TextField : T[K] extends readonly object[] ? MultipleRelationField : T[K] extends readonly unknown[] ? MultipleField : T[K] extends Date ? DateField : Exact<T[K], GeoPoint, 1, 2> extends 1 ? GeoPointField : T[K] extends object ? RelationField : Other; type Path<T, MaxDepth extends number> = ForceAlias<FullPath<T, MaxDepth>>; type FullPath<T, MaxDepth extends number, K extends keyof T = keyof T, D extends number = 0> = D extends MaxDepth ? never : K extends string ? PathHelper<T, K, `${K}` | `${K}:lower`, `${K}`, `${K}.${FullPath<T[K], MaxDepth, keyof T[K], DepthCounter[D]>}`, `${K}` | `${K}:each` | `${K}:length`, `${K}` | `${K}.${FullPath<T[K], MaxDepth, keyof T[K], DepthCounter[D]>}`, `${K}` | `${K}:each` | `${K}:length` | `${K}.${FullPath<Elem<T[K]>, MaxDepth, keyof Elem<T[K]>, DepthCounter[D]>}`, `${K}`> | `${string}_via_${string}.${string}` : never; type PathExpand<T, MaxDepth extends number> = ForceAlias<FullPathExpand<T, MaxDepth>>; type FullPathExpand<T, MaxDepth extends number, K extends keyof T = keyof T, D extends number = 0> = D extends MaxDepth ? never : K extends string ? PathHelper<T, K, never, never, never, never, `${K}` | `${K}.${FullPathExpand<T[K], MaxDepth, keyof T[K], DepthCounter[D]>}`, `${K}` | `${K}.${FullPathExpand<Elem<T[K]>, MaxDepth, keyof Elem<T[K]>, DepthCounter[D]>}`, never> | `${string}_via_${string}` | `${string}_via_${string}.${string}` : never; type PathFields<T, MaxDepth extends number> = ForceAlias<FullPathFields<T, MaxDepth>>; type FullPathFields<T, MaxDepth extends number, K extends keyof T = keyof T, D extends number = 0> = D extends MaxDepth ? never : K extends string ? PathHelper<T, K, `${K}` | `${K}:excerpt(${number},${boolean})` | `${K}:excerpt(${number}, ${boolean})`, `${K}`, `${K}` | `${K}.${FullPathFields<T[K], MaxDepth, keyof T[K], DepthCounter[D]>}`, `${K}`, `${K}` | `expand.${K}` | `expand.${K}.${FullPathFields<T[K], MaxDepth, keyof T[K], DepthCounter[D]>}`, `${K}` | `expand.${K}` | `expand.${K}.${FullPathFields<Elem<T[K]>, MaxDepth, keyof Elem<T[K]>, DepthCounter[D]>}`, `${K}`> | '*' | `${string}_via_${string}` | `expand.${string}_via_${string}` | `expand.${string}_via_${string}.${string}` : never; type PathSort<T, MaxDepth extends number> = ForceAlias<FullPathSort<T, MaxDepth>>; type FullPathSort<T, MaxDepth extends number, K extends keyof T = keyof T, D extends number = 0> = D extends MaxDepth ? never : K extends string ? '@random' | '@rowid' | SortKey<PathSortLoop<T, MaxDepth, K, D>> : never; type PathSortLoop<T, MaxDepth extends number, K extends keyof T = keyof T, D extends number = 0> = D extends MaxDepth ? never : K extends string ? PathHelper<T, K, `${K}`, `${K}`, `${K}.${PathSortLoop<T[K], MaxDepth, keyof T[K], DepthCounter[D]>}`, `${K}`, `${K}` | `${K}.${PathSortLoop<T[K], MaxDepth, keyof T[K], DepthCounter[D]>}`, `${K}` | `${K}.${PathSortLoop<Elem<T[K]>, MaxDepth, keyof Elem<T[K]>, DepthCounter[D]>}`, `${K}`> | `${string}_via_${string}.${string}` : never; type PathValueHelper<T, P extends string, MaxDepth extends number, D extends number> = P extends `${infer _Prefix}_via_${infer _Suffix}` ? unknown : P extends `${infer Key}.${infer Rest}` ? Key extends keyof T ? T[Key] extends readonly (infer E)[] ? PathValue<E, Rest, MaxDepth, DepthCounter[D]> : PathValue<T[Key], Rest, MaxDepth, DepthCounter[D]> : never : P extends `${infer Key}:${infer Modifier}` ? Key extends keyof T ? HandleModifier<T[Key], Modifier> : never : P extends keyof T ? T[P] extends object[] ? string : T[P] extends unknown[] ? T[P][number] : T[P] extends Date ? T[P] | DatetimeMacro : T[P] extends object ? string : T[P] : never; type PathValue<T, P extends string, MaxDepth extends number, D extends number = 0> = D extends MaxDepth ? never : PathValueHelper<T, P, MaxDepth, D>; type HandleModifier<V, Modifier extends string> = Modifier extends 'each' ? V extends number[] ? number : string : Modifier extends 'length' ? number : Modifier extends 'lower' ? string : never; interface QueryBuilderStart<T, MaxDepth extends number = 6, Once extends keyof QueryBuilderStart<T, MaxDepth> | '' = ''> extends QueryBuilder<T, MaxDepth> { /** * **_Starter_**, **_Once_** - This can only be used once, at the start. * * Accepts a single key or an array of keys. * * Selects which fields to return from PocketBase. `expand()` is not needed if `fields()` is used, we automatically include what to [expand](https://pocketbase.io/docs/working-with-relations/#expanding-relations). * * Modifiers: * - `*` targets all keys from the specific depth level. * - `:excerpt(maxLength, withEllipsis?)` returns a short plain text version of the field string value. * * @example * ```ts * const query = pbQuery<Post>() * .fields([ * 'title', // Basic field * 'content:excerpt(100,true)', // Field with excerpt modifier * 'author', // Relation ID field * 'expand.author', // Expanded relation field * 'expand.comments_via_post', // Back-relation expansion * ]) * .sort(['title', 'author']) * .build(pb.filter); * * console.log(query.expand); // Output: 'author,comments_via_post' * console.log(query.sort); // Output: 'title,author' * * const records = await pb.collection('posts').getList(1, 20, query); * * console.log(records); * // Output: * // [ * // { * // author: 'authorId', * // title: 'Football match this Saturday', * // content: 'Lorem ipsum dolor sit amet.', * // expand: { * // author: { * // name: 'John', * // }, * // comments_via_post: [ * // { ... }, * // ], * // }, * // }, * // ] * ``` * * @example * With the key `expand.*` we can't automatically include what to [expand](https://pocketbase.io/docs/working-with-relations/#expanding-relations), so it must be specified manually. * * ```ts * const query = pbQuery<Post>() * .fields(['title', 'expand.*']) * .expand(['author', 'comments_via_post']) * .build(pb.filter); * * console.log(query.fields); // Output: 'title,expand.*' * console.log(query.expand); // Output: 'author,comments_via_post' * ``` * * @since 0.3.0 */ fields<P extends PathFields<T, MaxDepth>>(keys: P | P[]): Omit<QueryBuilderStart<T, MaxDepth, Once | 'fields'>, Once | 'fields'>; /** * **_Starter_**, **_Once_** - This can only be used once, at the start. * * Accepts a single key or an array of keys. * * Expands information from related collections. `expand()` is not needed if `fields()` is used, we automatically include what to [expand](https://pocketbase.io/docs/working-with-relations/#expanding-relations). If used together with `fields()`, it overrides the automatic expansion. * * Notes: * - Supports up to 6-levels depth nested relations expansion. * - The expanded relations will be appended to the record under the [`expand`](https://pocketbase.io/docs/working-with-relations/#expanding-relations) property (e.g. `"expand": { "relField1": { ... }, ... }`). * - Only the relations to which the request user has permissions to view will be expanded. * * Read more about [expand](https://pocketbase.io/docs/working-with-relations/#expanding-relations) in the [official documentation](https://pocketbase.io/docs/working-with-relations/#expanding-relations). * * @example * ```ts * const query = pbQuery<Post>() * .expand([ * 'author', * 'comments_via_post', * ]) * .build(pb.filter); * * console.log(query.fields); // Output: '' * console.log(query.expand); // Output: 'author,comments_via_post' * * const records = await pb.collection('posts').getList(1, 20, query); * * console.log(records); * // Output: * // [ * // { * // expand: { * // author: { ... }, * // comments_via_post: [ * // { ... }, * // ], * // }, * // ..., * // }, * // ] * ``` * * @since 0.3.0 */ expand<P extends PathExpand<T, MaxDepth>>(keys: P | P[]): Omit<QueryBuilderStart<T, MaxDepth, Once | 'expand'>, Once | 'expand'>; } interface QueryBuilder<T, MaxDepth extends number = 6, Once extends keyof QueryBuilder<T, MaxDepth> | '' = ''> extends QueryBuilderEnd, SortMethod<T, MaxDepth, QueryBuilder<T, MaxDepth, Once | 'sort'>, Once | 'sort'> { /** * Matches records where `key` equals `value`. * * @example * ```ts * pbQuery<Post>().equal('author.name', 'Alice'); // name='Alice' * // This is case-sensitive. Use the `:lower` modifier for case-insensitive matching. * pbQuery<Post>().equal('author.name:lower', 'alice'); // name:lower='alice' * ``` */ equal<P extends Path<T, MaxDepth>>(key: P, value: PathValue<T, P, MaxDepth>): Omit<RestrictedQueryBuilder<T, MaxDepth, Once>, Once>; /** * Matches records where `key` is not equal to `value`. * * @example * ```ts * pbQuery<Post>().notEqual('author.name', 'Alice'); // name!='Alice' * // This is case-sensitive. Use the `:lower` modifier for case-insensitive matching. * pbQuery<Post>().notEqual('author.name:lower', 'alice'); // name:lower!='alice' * ``` */ notEqual<P extends Path<T, MaxDepth>>(key: P, value: PathValue<T, P, MaxDepth>): Omit<RestrictedQueryBuilder<T, MaxDepth, Once>, Once>; /** * Matches records where `key` is greater than `value`. * * [PocketBase's datetime macros](https://pocketbase.io/docs/api-rules-and-filters/#-macros) could be helpful when comparing dates: `@now`, `@yesterday`, `@tomorrow`, `@todayStart`, `@todayEnd`, `@monthStart`, `@monthEnd`, `@yearStart`, `@yearEnd`, [more...](https://pocketbase.io/docs/api-rules-and-filters/#-macros) * * @example * ```ts * pbQuery<User>().greaterThan('age', 21); // age>21 * pbQuery<User>().greaterThan('created', new Date('2021-01-01')); // created>'2021-01-01 00:00:00.000Z' * ``` */ greaterThan<P extends Path<T, MaxDepth>>(key: P, value: PathValue<T, P, MaxDepth>): Omit<RestrictedQueryBuilder<T, MaxDepth, Once>, Once>; /** * Matches records where `key` is greater than or equal to `value`. * * [PocketBase's datetime macros](https://pocketbase.io/docs/api-rules-and-filters/#-macros) could be helpful when comparing dates: `@now`, `@yesterday`, `@tomorrow`, `@todayStart`, `@todayEnd`, `@monthStart`, `@monthEnd`, `@yearStart`, `@yearEnd`, [more...](https://pocketbase.io/docs/api-rules-and-filters/#-macros) * * @example * ```ts * pbQuery<User>().greaterThanOrEqual('age', 18); // age>=18 * pbQuery<User>().greaterThanOrEqual('created', new Date('2021-01-01')); // created>='2021-01-01 00:00:00.000Z' * ``` */ greaterThanOrEqual<P extends Path<T, MaxDepth>>(key: P, value: PathValue<T, P, MaxDepth>): Omit<RestrictedQueryBuilder<T, MaxDepth, Once>, Once>; /** * Matches records where `key` is less than `value`. * * [PocketBase's datetime macros](https://pocketbase.io/docs/api-rules-and-filters/#-macros) could be helpful when comparing dates: `@now`, `@yesterday`, `@tomorrow`, `@todayStart`, `@todayEnd`, `@monthStart`, `@monthEnd`, `@yearStart`, `@yearEnd`, [more...](https://pocketbase.io/docs/api-rules-and-filters/#-macros) * * @example * ```ts * pbQuery<User>().lessThan('age', 50); // age<50 * pbQuery<User>().lessThan('created', new Date('2021-01-01')); // created<'2021-01-01 00:00:00.000Z' * ``` */ lessThan<P extends Path<T, MaxDepth>>(key: P, value: PathValue<T, P, MaxDepth>): Omit<RestrictedQueryBuilder<T, MaxDepth, Once>, Once>; /** * Matches records where `key` is less than or equal to `value`. * * [PocketBase's datetime macros](https://pocketbase.io/docs/api-rules-and-filters/#-macros) could be helpful when comparing dates: `@now`, `@yesterday`, `@tomorrow`, `@todayStart`, `@todayEnd`, `@monthStart`, `@monthEnd`, `@yearStart`, `@yearEnd`, [more...](https://pocketbase.io/docs/api-rules-and-filters/#-macros) * * @example * ```ts * pbQuery<User>().lessThanOrEqual('age', 65); // age<=65 * pbQuery<User>().lessThanOrEqual('created', new Date('2021-01-01')); // created<='2021-01-01 00:00:00.000Z' * ``` */ lessThanOrEqual<P extends Path<T, MaxDepth>>(key: P, value: PathValue<T, P, MaxDepth>): Omit<RestrictedQueryBuilder<T, MaxDepth, Once>, Once>; /** * Matches records where `key` contains `value`. * * It is case-insensitive, so the `:lower` [modifier](https://pocketbase.io/docs/api-rules-and-filters/#special-identifiers-and-modifiers) is unnecessary. * * @example Contains * ```ts * pbQuery<Post>().like('author.name', 'Joh'); // name~'Joh' / name~'%Joh%' * // If not specified, auto-wraps the value in `%` for wildcard matching. * ``` * * @example Starts with * ```ts * pbQuery<Post>().like('author.name', 'Joh%'); // name~'Joh%' * ``` * * @example Ends with * ```ts * pbQuery<Post>().like('author.name', '%Doe'); // name~'%Doe' * ``` */ like<P extends Path<T, MaxDepth>>(key: P, value: PathValue<T, P, MaxDepth>): Omit<RestrictedQueryBuilder<T, MaxDepth, Once>, Once>; /** * Matches records where `key` doesn't contain `value`. * * It is case-insensitive, so the `:lower` [modifier](https://pocketbase.io/docs/api-rules-and-filters/#special-identifiers-and-modifiers) is unnecessary. * * @example Doesn't contain * ```ts * pbQuery<Post>().notLike('author.name', 'Joh'); // name!~'Joh' / name!~'%Joh%' * // If not specified, auto-wraps the value in `%` for wildcard matching. * ``` * * @example Doesn't start with * ```ts * pbQuery<Post>().notLike('author.name', 'Joh%'); // name!~'Joh%' * ``` * * @example Doesn't end with * ```ts * pbQuery<Post>().notLike('author.name', '%Doe'); // name!~'%Doe' * ``` */ notLike<P extends Path<T, MaxDepth>>(key: P, value: PathValue<T, P, MaxDepth>): Omit<RestrictedQueryBuilder<T, MaxDepth, Once>, Once>; /** * Useful for queries involving multi-valued fields ([back-relations](https://pocketbase.io/docs/working-with-relations/#back-relations), [multiple relation](https://pocketbase.io/docs/collections/#relationfield), [multiple select](https://pocketbase.io/docs/collections/#selectfield), or [multiple file](https://pocketbase.io/docs/collections/#filefield)). * * Matches records where at least one of the values in the given `key` equals `value`. * * @example * ```ts * pbQuery<Book>().anyEqual('books_via_author.title', 'The Island'); // post_via_author.name?='The Island' * * // This is case-sensitive. Use the `:lower` modifier for case-insensitive matching. * pbQuery<Book>().anyEqual('books_via_author.title:lower', 'the island'); // post_via_author.name:lower?='the island' * ``` */ anyEqual<P extends Path<T, MaxDepth>>(key: P, value: PathValue<T, P, MaxDepth>): Omit<RestrictedQueryBuilder<T, MaxDepth, Once>, Once>; /** * Useful for queries involving multi-valued fields ([back-relations](https://pocketbase.io/docs/working-with-relations/#back-relations), [multiple relation](https://pocketbase.io/docs/collections/#relationfield), [multiple select](https://pocketbase.io/docs/collections/#selectfield), or [multiple file](https://pocketbase.io/docs/collections/#filefield)). * * Matches records where at least one of the values in the given `key` is not equal to `value`. * * @example * ```ts * pbQuery<Book>().anyNotEqual('books_via_author.title', 'The Island'); // post_via_author.name?!='The Island' * * // This is case-sensitive. Use the `:lower` modifier for case-insensitive matching. * pbQuery<Book>().anyNotEqual('books_via_author.title:lower', 'the island'); // post_via_author.name:lower?!='the island' * ``` */ anyNotEqual<P extends Path<T, MaxDepth>>(key: P, value: PathValue<T, P, MaxDepth>): Omit<RestrictedQueryBuilder<T, MaxDepth, Once>, Once>; /** * Useful for queries involving multi-valued fields ([back-relations](https://pocketbase.io/docs/working-with-relations/#back-relations), [multiple relation](https://pocketbase.io/docs/collections/#relationfield), [multiple select](https://pocketbase.io/docs/collections/#selectfield), or [multiple file](https://pocketbase.io/docs/collections/#filefield)). * * Matches records where at least one of the values in the given `key` is greater than `value`. * * @example * ```ts * pbQuery<User>().anyGreaterThan('age', 21); // age?>21 * ``` */ anyGreaterThan<P extends Path<T, MaxDepth>>(key: P, value: PathValue<T, P, MaxDepth>): Omit<RestrictedQueryBuilder<T, MaxDepth, Once>, Once>; /** * Useful for queries involving multi-valued fields ([back-relations](https://pocketbase.io/docs/working-with-relations/#back-relations), [multiple relation](https://pocketbase.io/docs/collections/#relationfield), [multiple select](https://pocketbase.io/docs/collections/#selectfield), or [multiple file](https://pocketbase.io/docs/collections/#filefield)). * * Matches records where at least one of the values in the given `key` is greater than or equal to `value`. * * @example * ```ts * pbQuery<User>().anyGreaterThanOrEqual('age', 18); // age?>=18 * ``` */ anyGreaterThanOrEqual<P extends Path<T, MaxDepth>>(key: P, value: PathValue<T, P, MaxDepth>): Omit<RestrictedQueryBuilder<T, MaxDepth, Once>, Once>; /** * Useful for queries involving multi-valued fields ([back-relations](https://pocketbase.io/docs/working-with-relations/#back-relations), [multiple relation](https://pocketbase.io/docs/collections/#relationfield), [multiple select](https://pocketbase.io/docs/collections/#selectfield), or [multiple file](https://pocketbase.io/docs/collections/#filefield)). * * Matches records where at least one of the values in the given `key` is less than `value`. * * @example * ```ts * pbQuery<User>().anyLessThan('age', 50); // age?<50 * ``` */ anyLessThan<P extends Path<T, MaxDepth>>(key: P, value: PathValue<T, P, MaxDepth>): Omit<RestrictedQueryBuilder<T, MaxDepth, Once>, Once>; /** * Useful for queries involving multi-valued fields ([back-relations](https://pocketbase.io/docs/working-with-relations/#back-relations), [multiple relation](https://pocketbase.io/docs/collections/#relationfield), [multiple select](https://pocketbase.io/docs/collections/#selectfield), or [multiple file](https://pocketbase.io/docs/collections/#filefield)). * * Matches records where at least one of the values in the given `key` is less than or equal to `value`. * * @example * ```ts * pbQuery<User>().anyLessThanOrEqual('age', 65); // age?<=65 * ``` */ anyLessThanOrEqual<P extends Path<T, MaxDepth>>(key: P, value: PathValue<T, P, MaxDepth>): Omit<RestrictedQueryBuilder<T, MaxDepth, Once>, Once>; /** * Useful for queries involving multi-valued fields ([back-relations](https://pocketbase.io/docs/working-with-relations/#back-relations), [multiple relation](https://pocketbase.io/docs/collections/#relationfield), [multiple select](https://pocketbase.io/docs/collections/#selectfield), or [multiple file](https://pocketbase.io/docs/collections/#filefield)). * * Matches records where at least one of the values in the given `key` contains `value`. * * It is case-insensitive, so the `:lower` [modifier](https://pocketbase.io/docs/api-rules-and-filters/#special-identifiers-and-modifiers) is unnecessary. * * @example Contains * ```ts * pbQuery<Post>().anyLike('author.name', 'Joh'); // name?~'Joh' / name?~'%Joh%' * // If not specified, auto-wraps the value in `%` for wildcard matching. * ``` * * @example Starts with * ```ts * pbQuery<Post>().anyLike('author.name', 'Joh%'); // name?~'Joh%' * ``` * * @example Ends with * ```ts * pbQuery<Post>().anyLike('author.name', '%Doe'); // name?~'%Doe' * ``` */ anyLike<P extends Path<T, MaxDepth>>(key: P, value: PathValue<T, P, MaxDepth>): Omit<RestrictedQueryBuilder<T, MaxDepth, Once>, Once>; /** * Useful for queries involving multi-valued fields ([back-relations](https://pocketbase.io/docs/working-with-relations/#back-relations), [multiple relation](https://pocketbase.io/docs/collections/#relationfield), [multiple select](https://pocketbase.io/docs/collections/#selectfield), or [multiple file](https://pocketbase.io/docs/collections/#filefield)). * * Matches records where at least one of the values in the given `key` doesn't contain `value`. * * It is case-insensitive, so the `:lower` [modifier](https://pocketbase.io/docs/api-rules-and-filters/#special-identifiers-and-modifiers) is unnecessary. * * @example Doesn't contain * ```ts * pbQuery<Post>().anyNotLike('author.name', 'Joh'); // name?!~'Joh' / name?!~'%Joh%' * // If not specified, auto-wraps the value in `%` for wildcard matching. * ``` * * @example Doesn't start with * ```ts * pbQuery<Post>().anyNotLike('author.name', 'Joh%'); // name?!~'Joh%' * ``` * * @example Doesn't end with * ```ts * pbQuery<Post>().anyNotLike('author.name', '%Doe'); // name?!~'%Doe' * ``` */ anyNotLike<P extends Path<T, MaxDepth>>(key: P, value: PathValue<T, P, MaxDepth>): Omit<RestrictedQueryBuilder<T, MaxDepth, Once>, Once>; /** * **_Helper_** * * Matches records where any of the `keys` contain `value`. * * It can be used to perform a full-text search (FTS). * * It is case-insensitive, so the `:lower` [modifier](https://pocketbase.io/docs/api-rules-and-filters/#special-identifiers-and-modifiers) is unnecessary. * * @example Full text search * ```ts * pbQuery<Post>().search(['title', 'content', 'tags', 'author.name'], 'Football'); // (title~'Football' || content~'Football' || tags~'Football' || author.name~'Football') * ``` * * @example Contains * ```ts * pbQuery<User>().search(['name', 'surname'], 'Joh'); // (name~'Joh' || surname~'Joh') / (name~'%Joh%' || surname~'%Joh%') If not specified, auto-wraps the value in `%` for wildcard matching. * ``` * * @example Starts with * ```ts * pbQuery<User>().search(['name', 'surname'], 'Joh%'); // (name~'Joh%' || surname~'Joh%') * ``` * * @example Ends with * ```ts * pbQuery<User>().search(['name', 'surname'], '%Doe'); // (name~'%Doe' || surname~'%Doe') * ``` */ search<P extends Path<T, MaxDepth>>(keys: P[], value: PathValue<T, P, MaxDepth>): Omit<RestrictedQueryBuilder<T, MaxDepth, Once>, Once>; /** * **_Helper_** * * Matches records where `key` is in `values`. * * @example * ```ts * pbQuery<Post>().in('id', ['id_1', 'id_2', 'id_3']); // (id='id_1' || id='id_2' || id='id_3') * ``` */ in<P extends Path<T, MaxDepth>>(key: P, values: PathValue<T, P, MaxDepth>[]): Omit<RestrictedQueryBuilder<T, MaxDepth, Once>, Once>; /** * **_Helper_** * * Matches records where `key` is not in `values`. * * @example * ```ts * pbQuery<User>().notIn('age', [18, 21, 30]); // (age!=18 && age!=21 && age!=30) * ``` */ notIn<P extends Path<T, MaxDepth>>(key: P, values: PathValue<T, P, MaxDepth>[]): Omit<RestrictedQueryBuilder<T, MaxDepth, Once>, Once>; /** * **_Helper_** * * Matches records where `key` is between `from` and `to`. * * [PocketBase's datetime macros](https://pocketbase.io/docs/api-rules-and-filters/#-macros) could be helpful when comparing dates: `@now`, `@yesterday`, `@tomorrow`, `@todayStart`, `@todayEnd`, `@monthStart`, `@monthEnd`, `@yearStart`, `@yearEnd`, [more...](https://pocketbase.io/docs/api-rules-and-filters/#-macros) * * @example * ```ts * pbQuery<User>().between('age', 18, 30); // (age>=18 && age<=30) * pbQuery<User>().between('created', new Date('2021-01-01'), '@now'); // (created>='2021-01-01 00:00:00.000Z' && created<=@now) * ``` */ between<P extends Path<T, MaxDepth>>(key: P, from: PathValue<T, P, MaxDepth>, to: PathValue<T, P, MaxDepth>): Omit<RestrictedQueryBuilder<T, MaxDepth, Once>, Once>; /** * **_Helper_** * * Matches records where `key` is not between `from` and `to`. * * [PocketBase's datetime macros](https://pocketbase.io/docs/api-rules-and-filters/#-macros) could be helpful when comparing dates: `@now`, `@yesterday`, `@tomorrow`, `@todayStart`, `@todayEnd`, `@monthStart`, `@monthEnd`, `@yearStart`, `@yearEnd`, [more...](https://pocketbase.io/docs/api-rules-and-filters/#-macros) * * @example * ```ts * pbQuery<User>().notBetween('age', 18, 30); // (age<18 || age>30) * pbQuery<User>().notBetween('created', new Date('2021-01-01'), '@yesterday'); // (created<'2021-01-01 00:00:00.000Z' || created>@yesterday) * ``` */ notBetween<P extends Path<T, MaxDepth>>(key: P, from: PathValue<T, P, MaxDepth>, to: PathValue<T, P, MaxDepth>): Omit<RestrictedQueryBuilder<T, MaxDepth, Once>, Once>; /** * **_Helper_** * * Matches records where `key` is null. * * @example * ```ts * pbQuery<User>().isNull('name'); // name='' * ``` */ isNull<P extends Path<T, MaxDepth>>(key: P): Omit<RestrictedQueryBuilder<T, MaxDepth, Once>, Once>; /** * **_Helper_** * * Matches records where `key` is not null. * * @example * ```ts * pbQuery<User>().isNotNull('name'); // name!='' * ``` */ isNotNull<P extends Path<T, MaxDepth>>(key: P): Omit<RestrictedQueryBuilder<T, MaxDepth, Once>, Once>; /** * **_Helper_** * * Executes a custom query. * * This helper is safe to use with `pb.filter()`, but we recommend using it as a last resort. * * If you have a special use case that might be useful to other developers, consider [opening an issue](https://github.com/sergio9929/pb-query/issues), and we may implement it as a new _helper_ in the future. * * @example * ```ts * pbQuery<User>().custom('geoDistance(address.lon, address.lat, 23.32, 42.69) < 25'); // geoDistance(address.lon, address.lat, 23.32, 42.69) < 25 * * // We recommend using Pocketbase's native `pb.filter()` function * pbQuery<User>().custom(pb.filter('geoDistance(address.lon, address.lat, {:lon}, {:lat}) < {:distance}', { lon: 23.32, lat: 42.69, distance: 25 })); // geoDistance(address.lon, address.lat, 23.32, 42.69) < 25 * ``` */ custom(raw: string): Omit<RestrictedQueryBuilder<T, MaxDepth, Once>, Once>; /** * Creates a logical group. * * @example * ```ts * pbQuery<Post>().group((q) => q.equal('status', 'active').or().equal('status', 'inactive')); // (status~'active' || status~'inactive') * ``` */ group(callback: (q: QueryBuilder<T, MaxDepth>) => Omit<RestrictedQueryBuilder<T, MaxDepth, Once>, Once> | Omit<QueryBuilder<T, MaxDepth, Once>, Once>): Omit<RestrictedQueryBuilder<T, MaxDepth, Once>, Once>; } interface RestrictedQueryBuilder<T, MaxDepth extends number = 6, Once extends keyof QueryBuilder<T, MaxDepth> | '' = ''> extends QueryBuilderEnd, SortMethod<T, MaxDepth, RestrictedQueryBuilder<T, MaxDepth, Once | 'sort'>, Once | 'sort'> { /** * Combines the previous and the next conditions with an `and` logical operator. * * @example * ```ts * pbQuery<User>().equal('name', 'Alice').and().equal('role', 'admin'); // name='Alice' && role='admin' * ``` */ and(): Omit<QueryBuilder<T, MaxDepth, Once>, Once>; /** * Combines the previous and the next conditions with an `or` logical operator. * * @example * ```ts * pbQuery<User>().equal('name', 'Alice').or().equal('name', 'Bob'); // name='Alice' || name='Bob' * ``` */ or(): Omit<QueryBuilder<T, MaxDepth, Once>, Once>; } interface QueryBuilderEnd { /** * Returns an object with `filter`, `fields` and `expand`. Read more in the [official documentation](https://pocketbase.io/docs/api-records/#listsearch-records). * * If a filter function is not provided (e.g. PocketBase's native `pb.filter`) as a parameter we return an object with the query string and values inside `filter`. * * @example * ```ts * const query = pbQuery<User>().equal('name', 'Alice').build(pb.filter); * * // You can also filter it later * const { filter } = pbQuery<User>().equal('name', 'Alice').build(); * console.log(filter); // { raw: 'name={:name1}', values: { name1: 'Alice' } } * console.log(pb.filter(filter.raw, filter.values)); // name='Alice' * ``` * * @example * ```ts * const query = pbQuery<Post>() * .fields([ * 'title', * 'content:excerpt(100,true)', * 'author', * 'expand.author.name', * 'expand.comments_via_post', * ]) // Optional * .search(['title', 'content', 'tags', 'author.name'], 'Football') * .sort(['title', '-created']) * .build(pb.filter); * * console.log(query.filter); // Output: "(title~'Football' || content~'Football' || tags~'Football' || author.name~'Football')" * console.log(query.fields); // Output: 'title,content:excerpt(100,true),author,expand.author,expand.comments_via_post' * console.log(query.expand); // Output: 'author,comments_via_post' * console.log(query.sort); // Output: 'title,-created' * * const records = await pb.collection('posts').getList(1, 20, query); * * console.log(records); * // Output: * // [ * // { * // author: 'authorId', * // title: 'Football match this Saturday', * // content: 'Lorem ipsum dolor sit amet.', * // expand: { * // author: { * // name: 'John', * // }, * // comments_via_post: [ * // { ... }, * // ], * // }, * // }, * // ] * ``` * * @since 0.3.0 */ build(): QueryResult<RawQueryObject>; build(filter: FilterFunction): QueryResult<string>; build(filter?: FilterFunction): QueryResult<RawQueryObject | string>; } interface SortMethod<T, MaxDepth extends number, Builder, Once extends PropertyKey> { /** * **_Once_** - This can only be used once. * * Accepts a single key or an array of keys. * * Sorts the results by the specified keys. * * Prefixes: * - `-`: Descending order. * - `+` (default): Ascending order. * * Macros: * - `@random`: Orders by [sqlite's random() function](https://sqlite.org/lang_corefunc.html#random). Useful for shuffling results. * - `@rowid`: **NOT THE SAME AS `id`**. Orders by [sqlite's internal `rowid`](https://www.sqlite.org/lang_createtable.html#rowid). [Can't](https://www.sqlite.org/rowidtable.html) be used with [View Collections](https://pocketbase.io/docs/collections/#view-collection). * * @example * ```ts * const query = pbQuery<Post>() * .sort(['title', '-created']) * .build(pb.filter); * * console.log(query.sort) // Output: 'title,-created' * ``` * * @since 0.3.0 */ sort<P extends PathSort<T, MaxDepth>>(keys: P | P[]): Omit<Builder, Once>; } //#endregion //#region src/query.d.ts declare function pbQuery<T = Record<string, unknown>, MaxDepth extends number = 6>(): QueryBuilderStart<T, MaxDepth>; //#endregion //#region src/utils.d.ts /** * We expose a filter function, but we recommend using the native `pb.filter()` function instead. * @deprecated Use native `pb.filter()`, not this. */ declare function filter(raw: string, params?: { [key: string]: unknown; }): string; //#endregion export { DatetimeMacro, FilterFunction, GeoPoint, OPERATORS, Path, PathExpand, PathFields, PathSort, QueryBuilder, RawQueryObject, RestrictedQueryBuilder, filter, pbQuery };