Merge pull request #422 from trouble/query-exists

This commit is contained in:
Elliot DeNolf
2020-10-16 21:45:41 -04:00
committed by GitHub
5 changed files with 128 additions and 30 deletions

View File

@@ -19,6 +19,10 @@ const base = [
label: 'is not in',
value: 'not_in',
},
{
label: 'exists',
value: 'exists',
},
];
const numeric = [

View File

@@ -97,6 +97,79 @@ describe('GrahpQL Resolvers', () => {
expect(retrievedId).toStrictEqual(id);
});
it('should query exists - true', async () => {
const title = 'gql read';
const description = 'description';
const summary = 'summary';
// language=graphQL
const query = `mutation {
createLocalizedPost(data: {title: "${title}", description: "${description}", summary: "${summary}", priority: 10}) {
id
title
description
priority
createdAt
updatedAt
}
}`;
const response = await client.request(query);
const { id } = response.createLocalizedPost;
// language=graphQL
const readQuery = `query {
LocalizedPosts(where: { summary: { exists: true }}) {
docs {
id
description
summary
}
}
}`;
const readResponse = await client.request(readQuery);
const retrievedId = readResponse.LocalizedPosts.docs[0].id;
expect(readResponse.LocalizedPosts.docs).toHaveLength(1);
expect(retrievedId).toStrictEqual(id);
});
it('should query exists - false', async () => {
const title = 'gql read';
const description = 'description';
// language=graphQL
const query = `mutation {
createLocalizedPost(data: {title: "${title}", description: "${description}", priority: 10}) {
id
title
description
priority
createdAt
updatedAt
}
}`;
const response = await client.request(query);
const { id } = response.createLocalizedPost;
// language=graphQL
const readQuery = `query {
LocalizedPosts(where: { summary: { exists: false }}) {
docs {
id
summary
}
}
}`;
const readResponse = await client.request(readQuery);
const retrievedDoc = readResponse.LocalizedPosts.docs[0];
expect(readResponse.LocalizedPosts.docs.length).toBeGreaterThan(0);
expect(retrievedDoc.id).toStrictEqual(id);
expect(retrievedDoc.summary).toBeNull();
});
});
describe('Update', () => {

View File

@@ -62,15 +62,21 @@ const buildWhereInputType = (name, fields, parentName) => {
return nestedPaths;
};
const operators = {
equality: ['equals', 'not_equals'],
contains: ['in', 'not_in', 'all'],
comparison: ['greater_than_equal', 'greater_than', 'less_than_equal', 'less_than'],
};
const fieldToSchemaMap = {
number: (field) => {
const type = GraphQLFloat;
return {
type: withOperators(
field.name,
field,
type,
parentName,
['equals', 'greater_than_equal', 'greater_than', 'less_than_equal', 'less_than', 'not_equals'],
[...operators.equality, ...operators.comparison],
),
};
},
@@ -78,10 +84,11 @@ const buildWhereInputType = (name, fields, parentName) => {
const type = GraphQLString;
return {
type: withOperators(
field.name,
field,
type,
parentName,
['equals', 'like', 'not_equals'],
[...operators.equality, 'like'],
),
};
},
@@ -89,10 +96,10 @@ const buildWhereInputType = (name, fields, parentName) => {
const type = EmailAddressResolver;
return {
type: withOperators(
field.name,
field,
type,
parentName,
['equals', 'like', 'not_equals'],
[...operators.equality, 'like'],
),
};
},
@@ -100,10 +107,10 @@ const buildWhereInputType = (name, fields, parentName) => {
const type = GraphQLString;
return {
type: withOperators(
field.name,
field,
type,
parentName,
['equals', 'like', 'not_equals'],
[...operators.equality, 'like'],
),
};
},
@@ -111,10 +118,10 @@ const buildWhereInputType = (name, fields, parentName) => {
const type = GraphQLJSON;
return {
type: withOperators(
field.name,
field,
type,
parentName,
['equals', 'like', 'not_equals'],
[...operators.equality, 'like'],
),
};
},
@@ -122,16 +129,16 @@ const buildWhereInputType = (name, fields, parentName) => {
const type = GraphQLString;
return {
type: withOperators(
field.name,
field,
type,
parentName,
['equals', 'like', 'not_equals'],
[...operators.equality, 'like'],
),
};
},
radio: (field) => ({
type: withOperators(
field.name,
field,
new GraphQLEnumType({
name: `${combineParentName(parentName, field.name)}_Input`,
values: field.options.reduce((values, option) => ({
@@ -142,26 +149,26 @@ const buildWhereInputType = (name, fields, parentName) => {
}), {}),
}),
parentName,
['like', 'equals', 'not_equals'],
[...operators.equality, 'like'],
),
}),
date: (field) => {
const type = DateTimeResolver;
return {
type: withOperators(
field.name,
field,
type,
parentName,
['equals', 'like', 'not_equals', 'greater_than_equal', 'greater_than', 'less_than_equal', 'less_than'],
[...operators.equality, ...operators.comparison, 'like'],
),
};
},
relationship: (field) => {
let type = withOperators(
field.name,
field,
GraphQLString,
parentName,
['in', 'not_in', 'all', 'equals', 'not_equals'],
[...operators.equality, ...operators.contains],
);
if (Array.isArray(field.relationTo)) {
@@ -194,23 +201,23 @@ const buildWhereInputType = (name, fields, parentName) => {
},
upload: (field) => ({
type: withOperators(
field.name,
field,
GraphQLString,
parentName,
['equals', 'not_equals'],
[...operators.equality],
),
}),
checkbox: (field) => ({
type: withOperators(
field.name,
field,
GraphQLBoolean,
parentName,
['equals', 'not_equals'],
[...operators.equality],
),
}),
select: (field) => ({
type: withOperators(
field.name,
field,
new GraphQLEnumType({
name: `${combineParentName(parentName, field.name)}_Input`,
values: field.options.reduce((values, option) => {
@@ -236,7 +243,7 @@ const buildWhereInputType = (name, fields, parentName) => {
}, {}),
}),
parentName,
['in', 'not_in', 'all', 'equals', 'not_equals'],
[...operators.equality, ...operators.contains],
),
}),
array: (field) => recursivelyBuildNestedPaths(field),
@@ -296,10 +303,10 @@ const buildWhereInputType = (name, fields, parentName) => {
fieldTypes.id = {
type: withOperators(
'id',
{ name: 'id' },
GraphQLString,
parentName,
['equals', 'not_equals', 'in', 'not_in'],
[...operators.equality, ...operators.contains],
),
};

View File

@@ -1,17 +1,27 @@
const { GraphQLList, GraphQLInputObjectType } = require('graphql');
const { GraphQLList, GraphQLInputObjectType, GraphQLBoolean } = require('graphql');
const combineParentName = require('../utilities/combineParentName');
const withOperators = (fieldName, type, parent, operators) => {
const name = `${combineParentName(parent, fieldName)}_operator`;
const withOperators = (field, type, parent, operators) => {
const name = `${combineParentName(parent, field.name)}_operator`;
const listOperators = ['in', 'not_in', 'all'];
if (!field.required) operators.push('exists');
return new GraphQLInputObjectType({
name,
fields: operators.reduce((fields, operator) => {
let gqlType;
if (listOperators.indexOf(operator) > -1) {
gqlType = new GraphQLList(type);
} else if (operator === 'exists') {
gqlType = GraphQLBoolean;
} else {
gqlType = type;
}
return {
...fields,
[operator]: {
type: listOperators.indexOf(operator) > -1 ? new GraphQLList(type) : type,
type: gqlType,
},
};
}, {}),

View File

@@ -2,7 +2,7 @@
/* eslint-disable no-restricted-syntax */
const mongoose = require('mongoose');
const validOperators = ['like', 'in', 'all', 'not_in', 'greater_than_equal', 'greater_than', 'less_than_equal', 'less_than', 'not_equals', 'equals'];
const validOperators = ['like', 'in', 'all', 'not_in', 'greater_than_equal', 'greater_than', 'less_than_equal', 'less_than', 'not_equals', 'equals', 'exists'];
function addSearchParam(key, value, searchParams) {
return {
@@ -216,6 +216,10 @@ class ParamParser {
break;
case 'exists':
formattedValue = { $exists: (val === 'true' || val === true) };
break;
default:
formattedValue = val;
break;