diff --git a/.env b/.env new file mode 100644 index 0000000..2fb9c8e --- /dev/null +++ b/.env @@ -0,0 +1,4 @@ +DATABASE_HOST=localhost +DATABASE_NAME=postgres +DATABASE_USER=postgres +DATABAE_PASSWORD=password diff --git a/.gitignore b/.gitignore index 58bdf6e..02bf714 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,4 @@ yarn-error.log* *.tgz -.env - /tmp diff --git a/src/resolvers/filters.ts b/src/resolvers/filters.ts index 4262a57..b7ff213 100644 --- a/src/resolvers/filters.ts +++ b/src/resolvers/filters.ts @@ -10,7 +10,6 @@ export const SPECIAL_FILTERS: Record = { GTE: '?? >= ?', LT: '?? < ?', LTE: '?? <= ?', - SOME: 'SOME', }; export type WhereNode = { @@ -89,7 +88,7 @@ const applyWhere = (node: WhereNode, where: Where, ops: QueryBuilderOps, joins: if (specialFilter) { const [, actualKey, filter] = specialFilter; - if (filter === 'SOME') { + if (filter === 'SOME' || filter === 'NONE') { const reverseRelation = node.model.getReverseRelation(actualKey); const rootTableAlias = `${node.tableAlias}__W__${key}`; const targetModel = reverseRelation.targetModel; @@ -107,7 +106,7 @@ const applyWhere = (node: WhereNode, where: Where, ops: QueryBuilderOps, joins: // TODO: make this work with subtypes ops.push((query) => - query.whereExists((subQuery) => { + query[filter === 'SOME' ? 'whereExists' : 'whereNotExists']((subQuery) => { void subQuery .from(`${targetModel.name} as ${aliases.getShort(tableAlias)}`) .whereRaw(`?? = ??`, [ diff --git a/src/schema/generate.ts b/src/schema/generate.ts index bf08448..a4185bc 100644 --- a/src/schema/generate.ts +++ b/src/schema/generate.ts @@ -85,10 +85,16 @@ export const generateDefinitions = ({ })), ...model.reverseRelations .filter(({ field: { reverseFilterable } }) => reverseFilterable) - .map((relation) => ({ - name: `${relation.name}_SOME`, - type: `${relation.targetModel.name}Where`, - })), + .flatMap((relation) => [ + { + name: `${relation.name}_SOME`, + type: `${relation.targetModel.name}Where`, + }, + { + name: `${relation.name}_NONE`, + type: `${relation.targetModel.name}Where`, + }, + ]), ]), input( `${model.name}WhereUnique`, diff --git a/tests/api/__snapshots__/query.spec.ts.snap b/tests/api/__snapshots__/query.spec.ts.snap index bde623f..03fbe13 100644 --- a/tests/api/__snapshots__/query.spec.ts.snap +++ b/tests/api/__snapshots__/query.spec.ts.snap @@ -58,6 +58,38 @@ exports[`query processes reverseFilters correctly 1`] = ` ], }, ], + "noneFloat0": [ + { + "id": "ba5d94a8-0035-4e45-9258-2f7676eb8d18", + "manyObjects": [ + { + "float": 0.5, + }, + ], + }, + ], + "noneFloat0_5": [], + "noneFloat2": [ + { + "id": "226a20e8-5c18-4423-99ca-eb0df6ff4fdd", + "manyObjects": [ + { + "float": 0.5, + }, + { + "float": 0, + }, + ], + }, + { + "id": "ba5d94a8-0035-4e45-9258-2f7676eb8d18", + "manyObjects": [ + { + "float": 0.5, + }, + ], + }, + ], "withFloat0": [ { "id": "226a20e8-5c18-4423-99ca-eb0df6ff4fdd", diff --git a/tests/api/query.spec.ts b/tests/api/query.spec.ts index 1655e5a..9734ed9 100644 --- a/tests/api/query.spec.ts +++ b/tests/api/query.spec.ts @@ -49,6 +49,24 @@ describe('query', () => { float } } + noneFloat0: anotherObjects(where: { manyObjects_NONE: { float: 0 } }) { + id + manyObjects { + float + } + } + noneFloat0_5: anotherObjects(where: { manyObjects_NONE: { float: 0.5 } }) { + id + manyObjects { + float + } + } + noneFloat2: anotherObjects(where: { manyObjects_NONE: { float: 2 } }) { + id + manyObjects { + float + } + } } `) ).toMatchSnapshot(); diff --git a/tests/generated/api/index.ts b/tests/generated/api/index.ts index b8acfe8..9c34bdc 100644 --- a/tests/generated/api/index.ts +++ b/tests/generated/api/index.ts @@ -56,6 +56,7 @@ export type AnotherObjectOrderBy = { export type AnotherObjectWhere = { deleted?: InputMaybe>; id?: InputMaybe>; + manyObjects_NONE?: InputMaybe; manyObjects_SOME?: InputMaybe; }; diff --git a/tests/generated/client/index.ts b/tests/generated/client/index.ts index 5a06e0a..5d36240 100644 --- a/tests/generated/client/index.ts +++ b/tests/generated/client/index.ts @@ -53,6 +53,7 @@ export type AnotherObjectOrderBy = { export type AnotherObjectWhere = { deleted?: InputMaybe>; id?: InputMaybe>; + manyObjects_NONE?: InputMaybe; manyObjects_SOME?: InputMaybe; }; @@ -856,7 +857,7 @@ export type SomeQueryQuery = { manyObjects: Array<{ __typename: 'SomeObject', id export type ReverseFiltersQueryQueryVariables = Exact<{ [key: string]: never; }>; -export type ReverseFiltersQueryQuery = { first: Array<{ __typename: 'AnotherObject', id: string, manyObjects: Array<{ __typename: 'SomeObject', float: number }> }>, second: Array<{ __typename: 'AnotherObject', id: string, manyObjects: Array<{ __typename: 'SomeObject', float: number }> }> }; +export type ReverseFiltersQueryQuery = { all: Array<{ __typename: 'AnotherObject', id: string, manyObjects: Array<{ __typename: 'SomeObject', float: number }> }>, withFloat0: Array<{ __typename: 'AnotherObject', id: string, manyObjects: Array<{ __typename: 'SomeObject', float: number }> }>, withFloat0_5: Array<{ __typename: 'AnotherObject', id: string, manyObjects: Array<{ __typename: 'SomeObject', float: number }> }>, noneFloat0: Array<{ __typename: 'AnotherObject', id: string, manyObjects: Array<{ __typename: 'SomeObject', float: number }> }>, noneFloat0_5: Array<{ __typename: 'AnotherObject', id: string, manyObjects: Array<{ __typename: 'SomeObject', float: number }> }> }; export type DeleteAnotherObjectMutationMutationVariables = Exact<{ id: Scalars['ID']['input']; @@ -1050,10 +1051,16 @@ export namespace SomeQuery { export namespace ReverseFiltersQuery { export type Variables = ReverseFiltersQueryQueryVariables; export type query = ReverseFiltersQueryQuery; - export type first = NonNullable<(NonNullable)[number]>; - export type manyObjects = NonNullable<(NonNullable)[number]>['manyObjects']>)[number]>; - export type second = NonNullable<(NonNullable)[number]>; - export type _manyObjects = NonNullable<(NonNullable)[number]>['manyObjects']>)[number]>; + export type all = NonNullable<(NonNullable)[number]>; + export type manyObjects = NonNullable<(NonNullable)[number]>['manyObjects']>)[number]>; + export type withFloat0 = NonNullable<(NonNullable)[number]>; + export type _manyObjects = NonNullable<(NonNullable)[number]>['manyObjects']>)[number]>; + export type withFloat0_5 = NonNullable<(NonNullable)[number]>; + export type __manyObjects = NonNullable<(NonNullable)[number]>['manyObjects']>)[number]>; + export type noneFloat0 = NonNullable<(NonNullable)[number]>; + export type ___manyObjects = NonNullable<(NonNullable)[number]>['manyObjects']>)[number]>; + export type noneFloat0_5 = NonNullable<(NonNullable)[number]>; + export type ____manyObjects = NonNullable<(NonNullable)[number]>['manyObjects']>)[number]>; } export namespace DeleteAnotherObjectMutation { diff --git a/tests/generated/schema.graphql b/tests/generated/schema.graphql index eb22ffd..d67ad5b 100644 --- a/tests/generated/schema.graphql +++ b/tests/generated/schema.graphql @@ -18,6 +18,7 @@ input AnotherObjectWhere { id: [ID!] deleted: [Boolean!] manyObjects_SOME: SomeObjectWhere + manyObjects_NONE: SomeObjectWhere } input AnotherObjectWhereUnique {