Merge pull request #422 from trouble/query-exists
This commit is contained in:
@@ -19,6 +19,10 @@ const base = [
|
||||
label: 'is not in',
|
||||
value: 'not_in',
|
||||
},
|
||||
{
|
||||
label: 'exists',
|
||||
value: 'exists',
|
||||
},
|
||||
];
|
||||
|
||||
const numeric = [
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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],
|
||||
),
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
};
|
||||
}, {}),
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user