@generazioneai/genquery 0.1.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.
Files changed (76) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +218 -0
  3. package/dist/adapters/base.d.ts +32 -0
  4. package/dist/adapters/base.d.ts.map +1 -0
  5. package/dist/adapters/base.js +3 -0
  6. package/dist/adapters/base.js.map +1 -0
  7. package/dist/adapters/typeorm/adapter.d.ts +35 -0
  8. package/dist/adapters/typeorm/adapter.d.ts.map +1 -0
  9. package/dist/adapters/typeorm/adapter.js +111 -0
  10. package/dist/adapters/typeorm/adapter.js.map +1 -0
  11. package/dist/adapters/typeorm/aliases.d.ts +25 -0
  12. package/dist/adapters/typeorm/aliases.d.ts.map +1 -0
  13. package/dist/adapters/typeorm/aliases.js +47 -0
  14. package/dist/adapters/typeorm/aliases.js.map +1 -0
  15. package/dist/adapters/typeorm/create.d.ts +29 -0
  16. package/dist/adapters/typeorm/create.d.ts.map +1 -0
  17. package/dist/adapters/typeorm/create.js +29 -0
  18. package/dist/adapters/typeorm/create.js.map +1 -0
  19. package/dist/adapters/typeorm/escape.d.ts +5 -0
  20. package/dist/adapters/typeorm/escape.d.ts.map +1 -0
  21. package/dist/adapters/typeorm/escape.js +13 -0
  22. package/dist/adapters/typeorm/escape.js.map +1 -0
  23. package/dist/adapters/typeorm/index.d.ts +4 -0
  24. package/dist/adapters/typeorm/index.d.ts.map +1 -0
  25. package/dist/adapters/typeorm/index.js +10 -0
  26. package/dist/adapters/typeorm/index.js.map +1 -0
  27. package/dist/adapters/typeorm/joins.d.ts +29 -0
  28. package/dist/adapters/typeorm/joins.d.ts.map +1 -0
  29. package/dist/adapters/typeorm/joins.js +91 -0
  30. package/dist/adapters/typeorm/joins.js.map +1 -0
  31. package/dist/adapters/typeorm/params.d.ts +8 -0
  32. package/dist/adapters/typeorm/params.d.ts.map +1 -0
  33. package/dist/adapters/typeorm/params.js +15 -0
  34. package/dist/adapters/typeorm/params.js.map +1 -0
  35. package/dist/adapters/typeorm/schema-from-typeorm.d.ts +50 -0
  36. package/dist/adapters/typeorm/schema-from-typeorm.d.ts.map +1 -0
  37. package/dist/adapters/typeorm/schema-from-typeorm.js +170 -0
  38. package/dist/adapters/typeorm/schema-from-typeorm.js.map +1 -0
  39. package/dist/adapters/typeorm/where.d.ts +26 -0
  40. package/dist/adapters/typeorm/where.d.ts.map +1 -0
  41. package/dist/adapters/typeorm/where.js +297 -0
  42. package/dist/adapters/typeorm/where.js.map +1 -0
  43. package/dist/datetime.d.ts +4 -0
  44. package/dist/datetime.d.ts.map +1 -0
  45. package/dist/datetime.js +86 -0
  46. package/dist/datetime.js.map +1 -0
  47. package/dist/engine.d.ts +52 -0
  48. package/dist/engine.d.ts.map +1 -0
  49. package/dist/engine.js +52 -0
  50. package/dist/engine.js.map +1 -0
  51. package/dist/errors.d.ts +10 -0
  52. package/dist/errors.d.ts.map +1 -0
  53. package/dist/errors.js +16 -0
  54. package/dist/errors.js.map +1 -0
  55. package/dist/index.d.ts +9 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +28 -0
  58. package/dist/index.js.map +1 -0
  59. package/dist/parsed.d.ts +127 -0
  60. package/dist/parsed.d.ts.map +1 -0
  61. package/dist/parsed.js +8 -0
  62. package/dist/parsed.js.map +1 -0
  63. package/dist/parser.d.ts +5 -0
  64. package/dist/parser.d.ts.map +1 -0
  65. package/dist/parser.js +404 -0
  66. package/dist/parser.js.map +1 -0
  67. package/dist/schema.d.ts +57 -0
  68. package/dist/schema.d.ts.map +1 -0
  69. package/dist/schema.js +36 -0
  70. package/dist/schema.js.map +1 -0
  71. package/dist/types.d.ts +180 -0
  72. package/dist/types.d.ts.map +1 -0
  73. package/dist/types.js +15 -0
  74. package/dist/types.js.map +1 -0
  75. package/package.json +70 -0
  76. package/spec.md +221 -0
package/dist/schema.js ADDED
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ /**
3
+ * Schema describes what fields and relations exist on each entity, so the
4
+ * parser can validate queries and the adapter can produce correct joins and
5
+ * conditions. The schema is intentionally ORM-agnostic.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.SchemaError = void 0;
9
+ exports.getEntity = getEntity;
10
+ exports.getField = getField;
11
+ exports.getRelation = getRelation;
12
+ exports.primaryKeyOf = primaryKeyOf;
13
+ class SchemaError extends Error {
14
+ constructor(message) {
15
+ super(message);
16
+ this.name = "SchemaError";
17
+ }
18
+ }
19
+ exports.SchemaError = SchemaError;
20
+ function getEntity(schema, name) {
21
+ const entity = schema.entities[name];
22
+ if (!entity) {
23
+ throw new SchemaError(`Unknown entity '${name}' in schema`);
24
+ }
25
+ return entity;
26
+ }
27
+ function getField(schema, entityName, field) {
28
+ return getEntity(schema, entityName).fields[field];
29
+ }
30
+ function getRelation(schema, entityName, field) {
31
+ return getEntity(schema, entityName).relations?.[field];
32
+ }
33
+ function primaryKeyOf(entity) {
34
+ return entity.primaryKey ?? "id";
35
+ }
36
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;;AA2DH,8BAMC;AAED,4BAMC;AAED,kCAMC;AAED,oCAEC;AAjCD,MAAa,WAAY,SAAQ,KAAK;IACpC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF;AALD,kCAKC;AAED,SAAgB,SAAS,CAAC,MAAc,EAAE,IAAY;IACpD,MAAM,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,WAAW,CAAC,mBAAmB,IAAI,aAAa,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAgB,QAAQ,CACtB,MAAc,EACd,UAAkB,EAClB,KAAa;IAEb,OAAO,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACrD,CAAC;AAED,SAAgB,WAAW,CACzB,MAAc,EACd,UAAkB,EAClB,KAAa;IAEb,OAAO,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,CAAC;AAC1D,CAAC;AAED,SAAgB,YAAY,CAAC,MAAwB;IACnD,OAAO,MAAM,CAAC,UAAU,IAAI,IAAI,CAAC;AACnC,CAAC"}
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Raw input types as accepted from the wire.
3
+ *
4
+ * These mirror the spec literally. Anything received from the frontend should
5
+ * conform to `GenQueryInput`. Use the parser to validate + normalize into the
6
+ * `Parsed*` types defined in `./parsed.ts`.
7
+ *
8
+ * The input types are generic on an entity type `T`. With `T = unknown`
9
+ * (default) you get the loose, untyped form. Pass a concrete entity class
10
+ * (`GenQueryInput<User>`) to get autocomplete and value-shape checking for
11
+ * fields and relations.
12
+ */
13
+ export type SortOrder = "asc" | "desc";
14
+ export type StringSearchMode = "splitword" | "exact" | "nativeregex";
15
+ export type NumericOp = ">" | "<" | ">=" | "<=" | "==";
16
+ /** Timezone offset. "Z" (UTC) or "+HH:MM" / "-HH:MM" (also "+HHMM" / "+HH"). */
17
+ export type OffsetInput = string;
18
+ export interface DateTimeObjectInput {
19
+ year?: number;
20
+ month?: number;
21
+ day?: number;
22
+ hours?: number;
23
+ minutes?: number;
24
+ seconds?: number;
25
+ offset?: OffsetInput;
26
+ }
27
+ export type DateTimeInput = string | DateTimeObjectInput;
28
+ export interface NumericComparisonInput {
29
+ operation?: NumericOp;
30
+ value: number;
31
+ }
32
+ /**
33
+ * Null-presence check, valid on any primitive field type (string, number,
34
+ * boolean, date, enum).
35
+ *
36
+ * { isNull: true } → IS NULL
37
+ * { isNull: false } → IS NOT NULL
38
+ */
39
+ export interface NullCheckInput {
40
+ isNull: boolean;
41
+ }
42
+ /**
43
+ * Empty-presence check, valid only on string fields. "Empty" means NULL or
44
+ * the empty string `''`.
45
+ *
46
+ * { isEmpty: true } → (col IS NULL OR col = '')
47
+ * { isEmpty: false } → (col IS NOT NULL AND col <> '')
48
+ */
49
+ export interface EmptyCheckInput {
50
+ isEmpty: boolean;
51
+ }
52
+ export interface StringSearchObjectInput {
53
+ mode?: StringSearchMode;
54
+ contained?: boolean;
55
+ /** Case-sensitive comparison. Defaults to `false` (case-insensitive). */
56
+ caseSensitive?: boolean;
57
+ value: string;
58
+ }
59
+ export type StringSearchInput = string | StringSearchObjectInput | NullCheckInput | EmptyCheckInput;
60
+ export interface DateRangeInput {
61
+ before?: DateTimeInput;
62
+ after?: DateTimeInput;
63
+ }
64
+ export type DateSearchInput = DateTimeInput | DateRangeInput | NullCheckInput;
65
+ export type NumberSearchInput = number | NumericComparisonInput | NullCheckInput;
66
+ export type BoolSearchInput = boolean | NullCheckInput;
67
+ /** Detects the `any` type, which otherwise satisfies every conditional. */
68
+ type IsAny<T> = 0 extends 1 & T ? true : false;
69
+ /** True iff T is `unknown` or `any` — i.e. caller didn't constrain. */
70
+ type IsLoose<T> = IsAny<T> extends true ? true : unknown extends T ? true : false;
71
+ type Prim = string | number | boolean | Date;
72
+ /**
73
+ * Picks the right search value shape for a single property's TS type.
74
+ *
75
+ * For string literal unions (enum-style), the value is strictly constrained
76
+ * to the union members — both the enum member form (`UserRoles.admin`) and the
77
+ * matching string literal (`"admin"`) compile. Arbitrary `string` variables
78
+ * are rejected at compile time; cast them if you really need to pass them.
79
+ */
80
+ type SearchValueFor<V> = [
81
+ NonNullable<V>
82
+ ] extends [Date] ? DateSearchInput : [
83
+ NonNullable<V>
84
+ ] extends [string] ? [string] extends [NonNullable<V>] ? StringSearchInput : NonNullable<V> | `${NonNullable<V> & string}` | NullCheckInput : [
85
+ NonNullable<V>
86
+ ] extends [number] ? NumberSearchInput : [
87
+ NonNullable<V>
88
+ ] extends [boolean] ? BoolSearchInput : [
89
+ NonNullable<V>
90
+ ] extends [(infer U)[]] ? RelationFilterInput<NonNullable<U>> : [
91
+ NonNullable<V>
92
+ ] extends [object] ? RelationFilterInput<NonNullable<V>> : unknown;
93
+ /**
94
+ * Wrapper for relation filters with explicit cardinality operators. The short
95
+ * form (a plain `SearchByInput`) is treated as implicit `some`.
96
+ *
97
+ * posts: { title: "x" } // implicit some
98
+ * posts: { some: { title: "x" } } // explicit some
99
+ * posts: { every: { published: true } }
100
+ * posts: { none: { draft: true } }
101
+ * posts: { some: { ... }, none: { ... } } // multiple ops, AND-ed
102
+ */
103
+ export type RelationFilterInput<T = unknown> = SearchByInput<T> | {
104
+ some?: SearchByInput<T>;
105
+ every?: SearchByInput<T>;
106
+ none?: SearchByInput<T>;
107
+ };
108
+ /** Keys of T whose value is a primitive (string/number/boolean/Date). */
109
+ type FieldKeysOf<T> = {
110
+ [K in keyof T]-?: [NonNullable<T[K]>] extends [Prim] ? K : never;
111
+ }[keyof T];
112
+ /** Keys of T whose value is a relation (array of object, or object). */
113
+ type RelationKeysOf<T> = {
114
+ [K in keyof T]-?: [NonNullable<T[K]>] extends [Prim] ? never : [NonNullable<T[K]>] extends [object] ? K : never;
115
+ }[keyof T];
116
+ /** Strips relation arrays down to their element type. */
117
+ type RelationTargetOf<T, K extends keyof T> = [
118
+ NonNullable<T[K]>
119
+ ] extends [(infer U)[]] ? NonNullable<U> : [
120
+ NonNullable<T[K]>
121
+ ] extends [object] ? NonNullable<T[K]> : never;
122
+ /**
123
+ * `searchBy` is a recursive object whose keys are field names of the current
124
+ * entity, relation names of the current entity, or the literal `OR`.
125
+ *
126
+ * Values depend on the kind of field:
127
+ * - string field: `StringSearchInput`
128
+ * - number field: `NumberSearchInput`
129
+ * - boolean field: `BoolSearchInput`
130
+ * - date field: `DateSearchInput`
131
+ * - relation: a nested `SearchByInput` against the related entity
132
+ *
133
+ * The `OR` key is special: it takes an array of full `SearchByInput`s; matches
134
+ * if any of them matches. All non-`OR` entries combine with AND.
135
+ */
136
+ export type SearchByInput<T = unknown> = IsLoose<T> extends true ? LooseSearchByInput : TypedSearchByInput<T>;
137
+ export type LooseSearchByInput = {
138
+ OR?: LooseSearchByInput[];
139
+ [field: string]: unknown;
140
+ };
141
+ export type TypedSearchByInput<T> = {
142
+ OR?: TypedSearchByInput<T>[];
143
+ } & {
144
+ [K in Exclude<keyof T, "OR"> as [NonNullable<T[K]>] extends [Prim | object] ? K : never]?: SearchValueFor<T[K]>;
145
+ };
146
+ export interface OrderByObjectInput<TField extends string = string> {
147
+ field: TField;
148
+ order?: SortOrder;
149
+ }
150
+ export type OrderByInput<T = unknown> = IsLoose<T> extends true ? string | OrderByObjectInput<string> : FieldKeysOf<T> & string extends infer F extends string ? F | OrderByObjectInput<F> : never;
151
+ export type IncludeFieldSpec = boolean;
152
+ export type IncludeRelationSpec<U = unknown> = IsLoose<U> extends true ? "all" | {
153
+ [field: string]: IncludeFieldSpec;
154
+ } : "all" | {
155
+ [K in FieldKeysOf<U>]?: IncludeFieldSpec;
156
+ };
157
+ export type IncludeInput<T = unknown> = IsLoose<T> extends true ? "none" | "all" | {
158
+ [relation: string]: IncludeRelationSpec;
159
+ } : "none" | "all" | {
160
+ [K in RelationKeysOf<T>]?: IncludeRelationSpec<RelationTargetOf<T, K>>;
161
+ };
162
+ export type SelectInput<T = unknown> = IsLoose<T> extends true ? "none" | "all" | {
163
+ [field: string]: boolean;
164
+ } : "none" | "all" | {
165
+ [K in FieldKeysOf<T>]?: boolean;
166
+ };
167
+ export interface PaginationObjectInput {
168
+ page?: number;
169
+ perPage?: number;
170
+ }
171
+ export type PaginationInput = "all" | "first" | PaginationObjectInput;
172
+ export interface GenQueryInput<T = unknown> {
173
+ orderBy?: OrderByInput<T>;
174
+ searchBy?: SearchByInput<T>;
175
+ include?: IncludeInput<T>;
176
+ select?: SelectInput<T>;
177
+ pagination?: PaginationInput;
178
+ }
179
+ export {};
180
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ /**
3
+ * Raw input types as accepted from the wire.
4
+ *
5
+ * These mirror the spec literally. Anything received from the frontend should
6
+ * conform to `GenQueryInput`. Use the parser to validate + normalize into the
7
+ * `Parsed*` types defined in `./parsed.ts`.
8
+ *
9
+ * The input types are generic on an entity type `T`. With `T = unknown`
10
+ * (default) you get the loose, untyped form. Pass a concrete entity class
11
+ * (`GenQueryInput<User>`) to get autocomplete and value-shape checking for
12
+ * fields and relations.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG"}
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@generazioneai/genquery",
3
+ "publishConfig": {
4
+ "access": "public"
5
+ },
6
+ "version": "0.1.0",
7
+ "description": "ORM-agnostic JSON query language with pluggable adapters (TypeORM, ...)",
8
+ "license": "BSD-3-Clause",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/GenerazioneAI-SRL/genquery.git"
12
+ },
13
+ "homepage": "https://github.com/GenerazioneAI-SRL/genquery#readme",
14
+ "bugs": {
15
+ "url": "https://github.com/GenerazioneAI-SRL/genquery/issues"
16
+ },
17
+ "keywords": [
18
+ "query",
19
+ "query-builder",
20
+ "orm",
21
+ "typeorm",
22
+ "json-query",
23
+ "dsl",
24
+ "search",
25
+ "filter",
26
+ "pagination"
27
+ ],
28
+ "main": "dist/index.js",
29
+ "types": "dist/index.d.ts",
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "import": "./dist/index.js",
34
+ "require": "./dist/index.js"
35
+ },
36
+ "./typeorm": {
37
+ "types": "./dist/adapters/typeorm/index.d.ts",
38
+ "import": "./dist/adapters/typeorm/index.js",
39
+ "require": "./dist/adapters/typeorm/index.js"
40
+ }
41
+ },
42
+ "files": [
43
+ "dist",
44
+ "README.md",
45
+ "LICENSE",
46
+ "spec.md"
47
+ ],
48
+ "scripts": {
49
+ "build": "tsc",
50
+ "build:dev": "tsc && rm -rf node_modules/typeorm",
51
+ "typecheck": "tsc --noEmit",
52
+ "test": "node --test dist/tests",
53
+ "prepublishOnly": "npm run build"
54
+ },
55
+ "peerDependencies": {
56
+ "typeorm": ">=0.3.0"
57
+ },
58
+ "peerDependenciesMeta": {
59
+ "typeorm": {
60
+ "optional": true
61
+ }
62
+ },
63
+ "devDependencies": {
64
+ "@types/node": "^20.0.0",
65
+ "reflect-metadata": "^0.2.2",
66
+ "sqlite3": "^5.1.7",
67
+ "typeorm": "^0.3.20",
68
+ "typescript": "^5.4.0"
69
+ }
70
+ }
package/spec.md ADDED
@@ -0,0 +1,221 @@
1
+ # Spec
2
+
3
+ This is somewhat of a "spec" that defines the query language.
4
+
5
+ It's not a proper spec, more like an example than a spec, but it's fairly comprehensive.
6
+
7
+ ## OR
8
+ `|` means or in the definition, `"asc" | "desc"` means either "asc" or "desc".
9
+ So `string | {}` means either a string or an empty object.
10
+
11
+ ## Default
12
+ `@` means that that is a default value.
13
+ This means that in `"asc" | @"desc"`, `"desc"` is default if the option is not specified.
14
+
15
+ ## Non optional
16
+ `*` means that that is not an optional value, it's mandatory inside of the object it is defined into.
17
+
18
+ ## Array types
19
+ When a type is specified like this: `[type]` it means that it's an array composed of many `type`.
20
+
21
+ ## type definitions:
22
+ ```json
23
+ offsetType: string | @"Z",
24
+
25
+ dateTimeType: string (ISO 8601) | {
26
+ year: int,
27
+ month: int,
28
+ day: int,
29
+ hours: int,
30
+ minutes: int,
31
+ seconds: int,
32
+ offset: offsetType
33
+ },
34
+
35
+ numericComparisonType: {
36
+ operation: ">" | "<" | ">=" | "<=" | @"=="
37
+ *value: number
38
+ },
39
+
40
+ // Presence check. Usable as the value of any primitive field
41
+ // (string, number, boolean, date, enum) in `searchBy`.
42
+ //
43
+ // { isNull: true } → IS NULL (NULLABLE fields only)
44
+ // { isNull: false } → IS NOT NULL (NULLABLE fields only)
45
+ // { isEmpty: true } → IS NULL OR = '' (STRING fields only)
46
+ // { isEmpty: false } → IS NOT NULL AND <> '' (STRING fields only)
47
+ //
48
+ // Both keys may appear in the same object; they are AND-ed. The useful
49
+ // combination is `{ isNull: false, isEmpty: true }` which matches rows
50
+ // that are not NULL but are the empty string. At least one of the two
51
+ // keys must be present.
52
+ //
53
+ // Constraints (rejected at parse time):
54
+ // - `isNull` on a field whose schema entry has `nullable: false`
55
+ // (the default) — non-nullable fields can never be NULL, so the
56
+ // check is always degenerate.
57
+ // - `isEmpty` on a non-string field.
58
+ presenceCheckType: {
59
+ isNull: bool,
60
+ isEmpty: bool,
61
+ },
62
+ ```
63
+
64
+ In the dateTimeType object when it's not an iso string all fields are optional. If not specified offset will have "Z" as default
65
+
66
+ ## String search modes
67
+
68
+ Strings may be searched using different search modes.
69
+
70
+ ### Splitword (current default)
71
+
72
+ Any search string will be split by whitespace and searched against every piece of the now split query.
73
+
74
+ For example given this query to find a user in our table:
75
+
76
+ ```json
77
+ {
78
+ searchBy: {
79
+ firstname: "mario rossi",
80
+ lastname: "mario rossi",
81
+ }
82
+ }
83
+ ```
84
+
85
+ with splitword the result of the query would include anything that respect ALL the following conditions:
86
+
87
+ - firstname: is either "mario" or "rossi"
88
+ - lastname is either "mario" or "rossi"
89
+
90
+ so the following user would all be found by the query:
91
+ ```json
92
+ [
93
+ {
94
+ firstname: "mario",
95
+ lastname: "mario"
96
+ },
97
+ {
98
+ firstname: "mario",
99
+ lastname: "rossi"
100
+ },
101
+ {
102
+ firstname: "rossi",
103
+ lastname: "rossi"
104
+ },
105
+ {
106
+ firstname: "rossi",
107
+ lastname: "mario"
108
+ }
109
+ ]
110
+ ```
111
+
112
+ Of course here `firstname` and `lastname` are the same but this might not always be the case.
113
+
114
+ ### exact
115
+
116
+ Just as it sounds, the string that is specified is the string that gets found, nothing more, nothing less.
117
+
118
+ ### nativeregex
119
+
120
+ A string is searched against a specified regex. The regex is in the language natively supported by the ORM or the database, so no kind of processing is done on it.
121
+
122
+ # example:
123
+ ```json
124
+ {
125
+ orderBy: string | { // if orderBy is a string then default order is "desc" just as described below
126
+
127
+ *field: string, // field: nameOfTheFieldToPerformSortingOperationsOn
128
+ order: "asc" | @"desc",
129
+
130
+ },
131
+
132
+ searchBy: {
133
+
134
+ // Any primitive field (string, number, boolean, date, enum) accepts
135
+ // `presenceCheckType` as an alternative value. `isEmpty` inside it is
136
+ // string-only — passing it on a non-string field is rejected.
137
+
138
+ // string
139
+
140
+
141
+ exampleStringField: string | presenceCheckType | { // when only a string is specified, default values apply here
142
+ mode: @"splitword" | "exact" | "nativeregex"
143
+
144
+ contained: true | @false, // This means that the query should match as part of a string, so "something" would also match "somethingelse"
145
+
146
+ caseSensitive: true | @false, // When false (default), comparison is case-insensitive. Applies to all modes including nativeregex.
147
+
148
+ *value: "something", // a value
149
+ },
150
+
151
+ firstname: "mario rossi",
152
+ lastname: "mario rossi", // remember splitword
153
+
154
+ fiscalCode: {
155
+ type: "exact"
156
+ value: "AFISCALCODE123"
157
+ },
158
+
159
+ // date
160
+
161
+ date: dateTimeType | presenceCheckType | { before: dateTimeType, after: dateTimeType }, // either before or after can be omitted from the object
162
+
163
+ // relations
164
+
165
+ // Implicit `some` — matches if at least one related row satisfies the filter.
166
+ relationName: {
167
+ otherField: "lorem ipsum"
168
+ },
169
+
170
+ // Explicit cardinality operators. `some` / `every` / `none` can appear
171
+ // alone or combined (multiple ops are AND-ed). Equivalent to Prisma's
172
+ // relation filters.
173
+ relationName: {
174
+ some: { otherField: "x" },
175
+ every: { published: true },
176
+ none: { draft: true }
177
+ },
178
+
179
+ // bool
180
+
181
+ exampleBoolField: true | presenceCheckType,
182
+
183
+ // number
184
+
185
+ exampleNumberField: number | presenceCheckType | {
186
+ operation: ">" | "<" | ">=" | "<=" | @"==",
187
+ *value: number
188
+ },
189
+
190
+ // enum
191
+
192
+ // An enum field must be a string value that matches one of the allowed
193
+ // values declared in the schema. The parser rejects anything else.
194
+ exampleEnumField: "allowedValue1" | "allowedValue2" | ... | presenceCheckType,
195
+
196
+ // OR
197
+
198
+ OR: [
199
+ searchBy, // recursive
200
+ searchBy, // here all the searchBy(s) are evaluated similar to prisma's OR
201
+ searchBy,
202
+ ]
203
+ },
204
+
205
+ include: @"none" | "all" | { // which relations to include in the query result
206
+ relationField: {
207
+ firstname: true // only firstname will be included from relationField
208
+ },
209
+ otherRelation: "all", // all fields will be included from otherRelation
210
+ },
211
+
212
+ select: "none" | @"all" | { // which fields to include in the query result
213
+ someField: true, // only field 'someField' will be included in the query this way
214
+ },
215
+
216
+ pagination: @"all" | "first" | { // using first makes this a "findFirst" type query, equivalent to page: 0 and perPage: 1.
217
+ page: int | @0,
218
+ perPage: int | @20,
219
+ }
220
+ }
221
+ ```