@lakeql/query-builder 0.1.4 → 0.2.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/dist/index.d.mts +48 -37
- package/dist/index.mjs +1 -1
- package/package.json +7 -7
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { GraphQLResolveInfo } from "graphql";
|
|
2
2
|
import { CompiledQuery, ExpressionBuilder, ExpressionWrapper, SelectExpression, SqlBool } from "kysely";
|
|
3
|
-
|
|
4
3
|
//#region src/index.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* Logical operators for combining filter conditions.
|
|
@@ -33,13 +32,17 @@ interface FieldOptions {
|
|
|
33
32
|
/**
|
|
34
33
|
* Defines the sort order for a query result.
|
|
35
34
|
*
|
|
36
|
-
* @template TableDefinition - The table type used to constrain the `field` to valid column names.
|
|
35
|
+
* @template TableDefinition - The table type used to constrain the `field` to valid column names. See [Introduction](/docs/query-builder/overview/introduction) for details.
|
|
37
36
|
*/
|
|
38
37
|
interface SortInput<TableDefinition> {
|
|
39
|
-
/**
|
|
38
|
+
/**
|
|
39
|
+
* Column name to sort by. Constrained to valid column names from the table type.
|
|
40
|
+
*
|
|
41
|
+
* @see {@link https://kysely-org.github.io/kysely-apidoc/types/SelectExpression.html | Kysely API Docs - SelectExpression}
|
|
42
|
+
*/
|
|
40
43
|
field: SelectExpression<KyselyDatabase<TableDefinition>, keyof KyselyDatabase<TableDefinition>>;
|
|
41
|
-
/** Sort direction
|
|
42
|
-
direction:
|
|
44
|
+
/** Sort direction */
|
|
45
|
+
direction: "asc" | "desc";
|
|
43
46
|
}
|
|
44
47
|
/**
|
|
45
48
|
* Controls offset-based pagination.
|
|
@@ -73,7 +76,7 @@ interface KyselyDatabase<TableDefinition> {
|
|
|
73
76
|
/**
|
|
74
77
|
* Parameters for generating a Trino-compatible SQL query from GraphQL resolve info.
|
|
75
78
|
*
|
|
76
|
-
* @template TableDefinition - The table type definition used by Kysely for type-safe column references.
|
|
79
|
+
* @template TableDefinition - The table type definition used by Kysely for type-safe column references. See [Introduction](/docs/query-builder/overview/introduction) for details.
|
|
77
80
|
*/
|
|
78
81
|
interface GenerateQueryProps<TableDefinition> {
|
|
79
82
|
/** The Trino catalog name (e.g. "hive"). */
|
|
@@ -82,7 +85,11 @@ interface GenerateQueryProps<TableDefinition> {
|
|
|
82
85
|
schema: string;
|
|
83
86
|
/** The table name to query. */
|
|
84
87
|
table: string;
|
|
85
|
-
/**
|
|
88
|
+
/**
|
|
89
|
+
* The columns to select, derived from GraphQL field selections.
|
|
90
|
+
*
|
|
91
|
+
* @see {@link https://kysely-org.github.io/kysely-apidoc/types/SelectExpression.html | Kysely API Docs - SelectExpression}
|
|
92
|
+
*/
|
|
86
93
|
selectFields: SelectExpression<KyselyDatabase<TableDefinition>, keyof KyselyDatabase<TableDefinition>>[];
|
|
87
94
|
/** The user-provided filter query (WHERE clause). */
|
|
88
95
|
userQuery: Where;
|
|
@@ -97,9 +104,16 @@ interface GenerateQueryProps<TableDefinition> {
|
|
|
97
104
|
}
|
|
98
105
|
/**
|
|
99
106
|
* Parameters for building a single field comparison expression.
|
|
107
|
+
*
|
|
108
|
+
* @template TableDefinition - The table type definition. See [Introduction](/docs/query-builder/overview/introduction) for details.
|
|
100
109
|
*/
|
|
101
110
|
interface GetFieldQueryProps<TableDefinition> {
|
|
102
|
-
/**
|
|
111
|
+
/**
|
|
112
|
+
* The Kysely expression builder for constructing SQL expressions.
|
|
113
|
+
*
|
|
114
|
+
* @see {@link https://kysely.dev/docs/recipes/expressions | Kysely Docs - Expressions}
|
|
115
|
+
* @see {@link https://kysely-org.github.io/kysely-apidoc/interfaces/ExpressionBuilder.html | Kysely API Docs - ExpressionBuilder}
|
|
116
|
+
*/
|
|
103
117
|
eb: ExpressionBuilder<KyselyDatabase<TableDefinition>, "tablename">;
|
|
104
118
|
/** The column name to filter on. */
|
|
105
119
|
fieldName: string;
|
|
@@ -112,14 +126,9 @@ interface GetFieldQueryProps<TableDefinition> {
|
|
|
112
126
|
* Builds a single field comparison expression for use in WHERE clauses.
|
|
113
127
|
*
|
|
114
128
|
* @param props - The field query parameters.
|
|
115
|
-
* @returns A Kysely
|
|
129
|
+
* @returns A Kysely [ExpressionWrapper](https://kysely-org.github.io/kysely-apidoc/classes/ExpressionWrapper.html) representing the comparison.
|
|
116
130
|
*/
|
|
117
|
-
declare function getFieldQuery<TableDefinition>({
|
|
118
|
-
eb,
|
|
119
|
-
fieldName,
|
|
120
|
-
operator,
|
|
121
|
-
value
|
|
122
|
-
}: GetFieldQueryProps<TableDefinition>): ExpressionWrapper<KyselyDatabase<TableDefinition>, "tablename", SqlBool>;
|
|
131
|
+
declare function getFieldQuery<TableDefinition>({ eb, fieldName, operator, value }: GetFieldQueryProps<TableDefinition>): ExpressionWrapper<KyselyDatabase<TableDefinition>, "tablename", SqlBool>;
|
|
123
132
|
/**
|
|
124
133
|
* Normalizes filter objects by ensuring each field is in its own object within and/or arrays.
|
|
125
134
|
*
|
|
@@ -143,33 +152,26 @@ declare function normalizeUserQuery(userQuery: Where): Where;
|
|
|
143
152
|
/**
|
|
144
153
|
* Generates a compiled SQL query with pagination metadata for Trino.
|
|
145
154
|
*
|
|
146
|
-
* Returns a Kysely `CompiledQuery` containing the generated SQL and parameters.
|
|
147
155
|
* The query uses two CTEs internally:
|
|
148
156
|
* - `total_count` — provides `total_records` (total matching rows before paging)
|
|
149
157
|
* - `records` — the actual rows based on `selectFields`, filtering, sorting, and paging
|
|
150
158
|
*
|
|
151
159
|
* The compiled query is executed by the Trino client which returns the row data.
|
|
152
160
|
*
|
|
153
|
-
* @returns A
|
|
154
|
-
*/
|
|
155
|
-
declare function generateQuery<TableDefinition>({
|
|
156
|
-
catalog,
|
|
157
|
-
schema,
|
|
158
|
-
table,
|
|
159
|
-
selectFields,
|
|
160
|
-
userQuery,
|
|
161
|
-
sorting,
|
|
162
|
-
paging,
|
|
163
|
-
transformFields,
|
|
164
|
-
dateFields
|
|
165
|
-
}: GenerateQueryProps<TableDefinition>): CompiledQuery<{
|
|
161
|
+
* @returns A [CompiledQuery](https://kysely-org.github.io/kysely-apidoc/interfaces/CompiledQuery.html) ready for execution via the Trino client.
|
|
162
|
+
*/
|
|
163
|
+
declare function generateQuery<TableDefinition>({ catalog, schema, table, selectFields, userQuery, sorting, paging, transformFields, dateFields }: GenerateQueryProps<TableDefinition>): CompiledQuery<{
|
|
166
164
|
total_records: number;
|
|
167
165
|
}>;
|
|
168
166
|
/**
|
|
169
167
|
* Parameters for formatting a compiled SQL query.
|
|
170
168
|
*/
|
|
171
169
|
interface FormatQueryProps<T> {
|
|
172
|
-
/**
|
|
170
|
+
/**
|
|
171
|
+
* The compiled Kysely query to format.
|
|
172
|
+
*
|
|
173
|
+
* @see {@link https://kysely-org.github.io/kysely-apidoc/interfaces/CompiledQuery.html | Kysely API Docs - CompiledQuery}
|
|
174
|
+
*/
|
|
173
175
|
query: CompiledQuery<T>;
|
|
174
176
|
}
|
|
175
177
|
/**
|
|
@@ -180,16 +182,25 @@ interface FormatQueryProps<T> {
|
|
|
180
182
|
* @param props - The format query parameters.
|
|
181
183
|
* @returns The formatted SQL string with interpolated parameters.
|
|
182
184
|
*/
|
|
183
|
-
declare function formatQuery<T>({
|
|
184
|
-
|
|
185
|
-
|
|
185
|
+
declare function formatQuery<T>({ query }: FormatQueryProps<T>): string;
|
|
186
|
+
interface GetSelectFieldProps {
|
|
187
|
+
/**
|
|
188
|
+
* The GraphQL resolve info from the resolver.
|
|
189
|
+
*
|
|
190
|
+
* @see {@link https://the-guild.dev/graphql/tools/docs/api/utils/src/interfaces/graphqlresolveinfo | GraphQL Tools - GraphQLResolveInfo}
|
|
191
|
+
*/
|
|
192
|
+
graphqlInfo: GraphQLResolveInfo;
|
|
193
|
+
/**
|
|
194
|
+
* When true, looks inside the `nodes` selection (for Connection types).
|
|
195
|
+
* @default false
|
|
196
|
+
*/
|
|
197
|
+
withNodes?: boolean;
|
|
198
|
+
}
|
|
186
199
|
/**
|
|
187
200
|
* Extracts selected field names from a GraphQL resolve info object.
|
|
188
201
|
*
|
|
189
|
-
* @param graphqlInfo - The GraphQL resolve info from the resolver.
|
|
190
|
-
* @param withNodes - When true, looks inside the `nodes` selection (for Connection types).
|
|
191
202
|
* @returns An array of field names to use as SELECT columns.
|
|
192
203
|
*/
|
|
193
|
-
declare function getSelectFields<T>(graphqlInfo
|
|
204
|
+
declare function getSelectFields<T>({ graphqlInfo, withNodes }: GetSelectFieldProps): GenerateQueryProps<T>["selectFields"];
|
|
194
205
|
//#endregion
|
|
195
|
-
export { Field, FieldOptions, FormatQueryProps, GenerateQueryProps, GetFieldQueryProps, PagingInput, SortInput, Where, WhereOperator, formatQuery, generateQuery, getFieldQuery, getSelectFields, normalizeFilter, normalizeUserQuery };
|
|
206
|
+
export { Field, FieldOptions, FormatQueryProps, GenerateQueryProps, GetFieldQueryProps, GetSelectFieldProps, PagingInput, SortInput, Where, WhereOperator, formatQuery, generateQuery, getFieldQuery, getSelectFields, normalizeFilter, normalizeUserQuery };
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{GraphQLError as e}from"graphql";import{DummyDriver as t,Kysely as n,PostgresAdapter as r,PostgresIntrospector as i,PostgresQueryCompiler as a,sql as o}from"kysely";import{format as s}from"sql-formatter";const c=()=>new n({dialect:{createAdapter(){return new r},createDriver(){return new t},createIntrospector(e){return new i(e)},createQueryCompiler(){return new a}}});let l=function(e){return e.AND=`and`,e.OR=`or`,e}({});const u={eq:`=`,gt:`>`,gte:`>=`,in:`in`,is:`=`,isnot:`!=`,lt:`<`,lte:`<=`,neq:`!=`,notin:`not in`};function d({eb:e,fieldName:t,operator:n,value:r}){let i=t,a=n.toLowerCase();switch(a){case`eq`:case`neq`:case`lt`:case`lte`:case`gt`:case`gte`:case`is`:case`isnot`:return e(o.id(i),u[a]??`=`,r);case`in`:case`notin`:return e(o.id(i),u[a]??`=`,r);case`like`:return e(o.id(i),`like`,`%${r}%`);case`notlike`:return e.not(e(o.id(i),`like`,`%${r}%`));case`startswith`:return e(o.id(i),`like`,`${r}%`);case`notstartswith`:return e.not(e(o.id(i),`like`,`${r}%`));case`endswith`:return e(o.id(i),`like`,`%${r}`);case`notendswith`:return e.not(e(o.id(i),`like`,`%${r}`));default:return e(o.id(i),`=`,r)}}function f({eb:e,query:t,transformFields:n}){let r=[];for(let[i,a]of Object.entries(t))switch(i.toLowerCase()){case`and`:for(let t of a){let i=f({eb:e,query:t,transformFields:n});i!==void 0&&r.push(i)}return e.and(r);case`or`:for(let t of a){let i=f({eb:e,query:t,transformFields:n});i&&r.push(i)}return e.or(r);default:{let[t,r]=Object.entries(a).flat();return d({eb:e,fieldName:n?.[i]??i,operator:t,value:r})}}}function p(e){if(!e)return e;let t={...e};return t.and&&Array.isArray(t.and)&&(t.and=t.and.flatMap(e=>{let t=e,n=Object.keys(t),r=n.filter(e=>e===`and`||e===`or`),i=n.filter(e=>e!==`and`&&e!==`or`);if(i.length>0&&r.length>0){let e=i.map(e=>({[e]:t[e]})),n=r.map(e=>({[e]:p({[e]:t[e]})[e]}));return[...e,...n]}if(i.length>1&&r.length===0)return i.map(e=>({[e]:t[e]}));if(r.length>0){let e={...t};for(let n of r)e[n]=p({[n]:t[n]})[n];return e}return e})),t.or&&Array.isArray(t.or)&&(t.or=t.or.flatMap(e=>{let t=e,n=Object.keys(t),r=n.filter(e=>e===`and`||e===`or`),i=n.filter(e=>e!==`and`&&e!==`or`);if(i.length>0&&r.length>0){let e=i.map(e=>({[e]:t[e]})),n=r.map(e=>({[e]:p({[e]:t[e]})[e]}));return[...e,...n]}if(i.length>1&&r.length===0)return i.map(e=>({[e]:t[e]}));if(r.length>0){let e={...t};for(let n of r)e[n]=p({[n]:t[n]})[n];return e}return e})),t}function m(e){let t=Object.keys(e);return t.length===1&&(t[0]===`and`||t[0]===`or`)?e:{and:Object.entries(e).map(([e,t])=>({[e]:t}))}}function h({catalog:e,schema:t,table:n,selectFields:r,userQuery:i,sorting:a,paging:s,transformFields:l,dateFields:u=[]}){let d=c(),{ref:h}=d.dynamic;return d.with(`total_count`,r=>r.selectFrom(o.id(e,t,n)).select(({fn:e})=>e.countAll().as(`total_records`)).$if(Object.keys(i).length>0,e=>e.where(e=>f({eb:e,query:p(m(i)),transformFields:l})??e.and([])))).with(`records`,c=>{let d=c.selectFrom(o.id(e,t,n)).select(r.map(e=>u.includes(e)?l?.[e]?o.raw(`to_unixtime(${l[e]})`).as(l[e]):o.raw(`to_unixtime(${e})`).as(e):l?.[e]?o.id(l[e]).as(l[e]):typeof e==`string`?h(e):e)).$if(Object.keys(i).length>0,e=>e.where(e=>f({eb:e,query:p(m(i)),transformFields:l})??e.and([])));for(let e of a){let t=l?.[e.field]||e.field;d=d.orderBy(t,e.direction.toLowerCase())}return d=!s?.offset||s.offset<=0?d.modifyEnd(o.raw(`FETCH FIRST ${s?.limit??100} ROWS ONLY`)):d.offset(s.offset??100).modifyEnd(o.raw(`FETCH NEXT ${s.limit??100} ROWS ONLY`)),d}).selectFrom(`total_count`).selectAll().fullJoin(`records`,e=>e.onTrue()).compile()}function g({query:e}){let t={};for(let[n,r]of e.parameters.entries())t[n+1]=String(typeof r==`string`?`'${r}'`:r);return s(e.sql,{keywordCase:`upper`,language:`postgresql`,params:t,tabWidth:0,useTabs:!1})}function _(t,n=!1){return t.fieldNodes.flatMap(t=>{let r=t.selectionSet?.selections,i=[];if(n){let t=r.find(e=>e.name.value===`nodes`);if(!t)throw new e("No fields to return defined. Please specify at least one field inside `nodes`.");i=t.selectionSet?.selections??[]}else i=t.selectionSet?.selections??[];return i.flatMap(e=>e.name.value)})}export{l as WhereOperator,g as formatQuery,h as generateQuery,d as getFieldQuery,_ as getSelectFields,p as normalizeFilter,m as normalizeUserQuery};
|
|
1
|
+
import{GraphQLError as e}from"graphql";import{DummyDriver as t,Kysely as n,PostgresAdapter as r,PostgresIntrospector as i,PostgresQueryCompiler as a,sql as o}from"kysely";import{format as s}from"sql-formatter";const c=()=>new n({dialect:{createAdapter(){return new r},createDriver(){return new t},createIntrospector(e){return new i(e)},createQueryCompiler(){return new a}}});let l=function(e){return e.AND=`and`,e.OR=`or`,e}({});const u={eq:`=`,gt:`>`,gte:`>=`,in:`in`,is:`=`,isnot:`!=`,lt:`<`,lte:`<=`,neq:`!=`,notin:`not in`};function d({eb:e,fieldName:t,operator:n,value:r}){let i=t,a=n.toLowerCase();switch(a){case`eq`:case`neq`:case`lt`:case`lte`:case`gt`:case`gte`:case`is`:case`isnot`:return e(o.id(i),u[a]??`=`,r);case`in`:case`notin`:return e(o.id(i),u[a]??`=`,r);case`like`:return e(o.id(i),`like`,`%${r}%`);case`notlike`:return e.not(e(o.id(i),`like`,`%${r}%`));case`startswith`:return e(o.id(i),`like`,`${r}%`);case`notstartswith`:return e.not(e(o.id(i),`like`,`${r}%`));case`endswith`:return e(o.id(i),`like`,`%${r}`);case`notendswith`:return e.not(e(o.id(i),`like`,`%${r}`));default:return e(o.id(i),`=`,r)}}function f({eb:e,query:t,transformFields:n}){let r=[];for(let[i,a]of Object.entries(t))switch(i.toLowerCase()){case`and`:for(let t of a){let i=f({eb:e,query:t,transformFields:n});i!==void 0&&r.push(i)}return e.and(r);case`or`:for(let t of a){let i=f({eb:e,query:t,transformFields:n});i&&r.push(i)}return e.or(r);default:{let[t,r]=Object.entries(a).flat();return d({eb:e,fieldName:n?.[i]??i,operator:t,value:r})}}}function p(e){if(!e)return e;let t={...e};return t.and&&Array.isArray(t.and)&&(t.and=t.and.flatMap(e=>{let t=e,n=Object.keys(t),r=n.filter(e=>e===`and`||e===`or`),i=n.filter(e=>e!==`and`&&e!==`or`);if(i.length>0&&r.length>0){let e=i.map(e=>({[e]:t[e]})),n=r.map(e=>({[e]:p({[e]:t[e]})[e]}));return[...e,...n]}if(i.length>1&&r.length===0)return i.map(e=>({[e]:t[e]}));if(r.length>0){let e={...t};for(let n of r)e[n]=p({[n]:t[n]})[n];return e}return e})),t.or&&Array.isArray(t.or)&&(t.or=t.or.flatMap(e=>{let t=e,n=Object.keys(t),r=n.filter(e=>e===`and`||e===`or`),i=n.filter(e=>e!==`and`&&e!==`or`);if(i.length>0&&r.length>0){let e=i.map(e=>({[e]:t[e]})),n=r.map(e=>({[e]:p({[e]:t[e]})[e]}));return[...e,...n]}if(i.length>1&&r.length===0)return i.map(e=>({[e]:t[e]}));if(r.length>0){let e={...t};for(let n of r)e[n]=p({[n]:t[n]})[n];return e}return e})),t}function m(e){let t=Object.keys(e);return t.length===1&&(t[0]===`and`||t[0]===`or`)?e:{and:Object.entries(e).map(([e,t])=>({[e]:t}))}}function h({catalog:e,schema:t,table:n,selectFields:r,userQuery:i,sorting:a,paging:s,transformFields:l,dateFields:u=[]}){let d=c(),{ref:h}=d.dynamic;return d.with(`total_count`,r=>r.selectFrom(o.id(e,t,n)).select(({fn:e})=>e.countAll().as(`total_records`)).$if(Object.keys(i).length>0,e=>e.where(e=>f({eb:e,query:p(m(i)),transformFields:l})??e.and([])))).with(`records`,c=>{let d=c.selectFrom(o.id(e,t,n)).select(r.map(e=>u.includes(e)?l?.[e]?o.raw(`to_unixtime(${l[e]})`).as(l[e]):o.raw(`to_unixtime(${e})`).as(e):l?.[e]?o.id(l[e]).as(l[e]):typeof e==`string`?h(e):e)).$if(Object.keys(i).length>0,e=>e.where(e=>f({eb:e,query:p(m(i)),transformFields:l})??e.and([])));for(let e of a){let t=l?.[e.field]||e.field;d=d.orderBy(t,e.direction.toLowerCase())}return d=!s?.offset||s.offset<=0?d.modifyEnd(o.raw(`FETCH FIRST ${s?.limit??100} ROWS ONLY`)):d.offset(s.offset??100).modifyEnd(o.raw(`FETCH NEXT ${s.limit??100} ROWS ONLY`)),d}).selectFrom(`total_count`).selectAll().fullJoin(`records`,e=>e.onTrue()).compile()}function g({query:e}){let t={};for(let[n,r]of e.parameters.entries())t[n+1]=String(typeof r==`string`?`'${r}'`:r);return s(e.sql,{keywordCase:`upper`,language:`postgresql`,params:t,tabWidth:0,useTabs:!1})}function _({graphqlInfo:t,withNodes:n=!1}){return t.fieldNodes.flatMap(t=>{let r=t.selectionSet?.selections,i=[];if(n){let t=r.find(e=>e.name.value===`nodes`);if(!t)throw new e("No fields to return defined. Please specify at least one field inside `nodes`.");i=t.selectionSet?.selections??[]}else i=t.selectionSet?.selections??[];return i.flatMap(e=>e.name.value)})}export{l as WhereOperator,g as formatQuery,h as generateQuery,d as getFieldQuery,_ as getSelectFields,p as normalizeFilter,m as normalizeUserQuery};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lakeql/query-builder",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Kysely-based SQL query builder that translates GraphQL queries into Trino SQL",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"graphql",
|
|
@@ -41,16 +41,16 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"date-fns": "4.4.0",
|
|
44
|
-
"kysely": "0.29.
|
|
45
|
-
"sql-formatter": "15.8.
|
|
44
|
+
"kysely": "0.29.4",
|
|
45
|
+
"sql-formatter": "15.8.2"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@pothos/core": "4.
|
|
49
|
-
"@types/node": "24.13.
|
|
48
|
+
"@pothos/core": "4.13.1",
|
|
49
|
+
"@types/node": "24.13.3",
|
|
50
50
|
"graphql": "16.14.2",
|
|
51
|
-
"tsdown": "0.22.
|
|
51
|
+
"tsdown": "0.22.13",
|
|
52
52
|
"typescript": "6.0.3",
|
|
53
|
-
"vitest": "4.1.
|
|
53
|
+
"vitest": "4.1.10",
|
|
54
54
|
"@lakeql/tsconfig": "0.1.0"
|
|
55
55
|
},
|
|
56
56
|
"scripts": {
|