WIP revises buildQuery to support required graphql where input

This commit is contained in:
James
2020-04-05 13:23:58 -04:00
parent 84b8618287
commit 2c2e594fc3
4 changed files with 59 additions and 38 deletions

View File

@@ -131,12 +131,12 @@ const buildWhereInputType = ({ name, fields, parent }) => {
const fieldName = formatName(name);
return new GraphQLInputObjectType({
name: `${fieldName}Where`,
name: `${fieldName}_where`,
fields: {
...fieldTypes,
OR: {
type: new GraphQLList(new GraphQLInputObjectType({
name: `${fieldName}WhereOr`,
name: `${fieldName}_where_or`,
fields: {
...fieldTypes,
},
@@ -144,7 +144,7 @@ const buildWhereInputType = ({ name, fields, parent }) => {
},
AND: {
type: new GraphQLList(new GraphQLInputObjectType({
name: `${fieldName}WhereAnd`,
name: `${fieldName}_where_and`,
fields: {
...fieldTypes,
},

View File

@@ -101,7 +101,7 @@ function getBuildObjectType(context) {
),
}),
select: (field) => {
const fullName = combineParentName(parent, field.label);
const fullName = combineParentName(parent, field.name);
const type = new GraphQLEnumType({
name: fullName,

View File

@@ -2,7 +2,7 @@ const { GraphQLInputObjectType } = require('graphql');
const combineParentName = require('../utilities/combineParentName');
const withOperators = (fieldName, type, parent, operators) => {
const name = `${combineParentName(parent, fieldName)}Operator`;
const name = `${combineParentName(parent, fieldName)}_operator`;
return new GraphQLInputObjectType({
name,
fields: operators.reduce((fields, operator) => {

View File

@@ -1,30 +1,27 @@
const mongoose = require('mongoose');
const validOperators = ['like', 'in', 'all', 'nin', 'gte', 'gt', 'lte', 'lt', 'ne'];
function addSearchParam(key, value, searchParams, schema) {
if (schema.paths[key]) {
if (typeof value === 'object') {
return {
...searchParams,
[key]: {
...searchParams[key],
...value,
},
};
}
const validOperators = ['like', 'in', 'all', 'not_in', 'greater_than_equal', 'greater_than', 'less_than_equal', 'less_than', 'not_equal', 'equals'];
function addSearchParam(key, value, searchParams) {
if (typeof value === 'object') {
return {
...searchParams,
[key]: value,
[key]: {
...searchParams[key],
...value,
},
};
}
return searchParams;
return {
...searchParams,
[key]: value,
};
}
class ParamParser {
constructor(model, rawParams, locale) {
this.parse = this.parse.bind(this);
this.model = model;
this.rawParams = rawParams;
this.locale = locale;
@@ -41,15 +38,30 @@ class ParamParser {
// Entry point to the ParamParser class
async parse() {
Object.keys(this.rawParams).forEach(async (key) => {
// If rawParams[key] is an object, that means there are operators present.
// Need to loop through keys on rawParams[key] to call addSearchParam on each operator found
if (typeof this.rawParams[key] === 'object') {
Object.keys(this.rawParams[key])
.forEach(async (operator) => {
const [searchParamKey, searchParamValue] = await this.buildSearchParam(this.model.schema, key, this.rawParams[key][operator], operator);
this.query.searchParams = addSearchParam(searchParamKey, searchParamValue, this.query.searchParams, this.model.schema);
});
// Otherwise there are no operators present
if (key === 'where') {
// We now need to determine if the whereKey is an AND, OR, or a schema path
Object.keys(this.rawParams[key]).forEach(async (rawRelationOrPath) => {
const relationOrPath = rawRelationOrPath.toLowerCase();
if (relationOrPath === 'and') {
this.query.searchParams = addSearchParam('$and', {}, this.query.searchParams);
} else if (relationOrPath === 'or') {
this.query.searchParams = addSearchParam('$or', {}, this.query.searchParams);
} else {
// It's a path - and there can be multiple comparisons on a single path.
// For example - title like 'test' and title not equal to 'tester'
// So we need to loop on keys again here to grab operators
const pathWhere = this.rawParams[key][relationOrPath];
if (typeof pathWhere === 'object') {
Object.keys(pathWhere).forEach(async (operator) => {
const [searchParamKey, searchParamValue] = await this.buildSearchParam(this.model.schema, relationOrPath, pathWhere[operator], operator);
this.query.searchParams = addSearchParam(searchParamKey, searchParamValue, this.query.searchParams);
});
}
}
});
} else {
const [searchParamKey, searchParamValue] = await this.buildSearchParam(this.model.schema, key, this.rawParams[key]);
if (searchParamKey === 'sort') {
@@ -58,11 +70,12 @@ class ParamParser {
this.query.searchParams = addSearchParam(searchParamKey, searchParamValue, this.query.searchParams, this.model.schema);
}
}
});
}, this);
return this.query;
}
// Checks to see
async buildSearchParam(schema, key, val, operator) {
let schemaObject = schema.obj[key];
const localizedKey = this.getLocalizedKey(key, schemaObject);
@@ -116,10 +129,10 @@ class ParamParser {
if (operator && validOperators.includes(operator)) {
switch (operator) {
case 'gte':
case 'lte':
case 'lt':
case 'gt':
case 'greater_than_equal':
case 'less_than_equal':
case 'less_than':
case 'greater_than':
formattedValue = {
[`$${operator}`]: val,
};
@@ -128,19 +141,24 @@ class ParamParser {
case 'in':
case 'all':
case 'nin':
case 'not_in':
formattedValue = {
[`$${operator}`]: val.split(','),
};
break;
default:
case 'like':
formattedValue = {
$regex: val,
$options: '-i',
};
break;
default:
formattedValue = val;
break;
}
}
@@ -153,11 +171,14 @@ class ParamParser {
// which can then be used in subsequent Mongoose queries.
function buildQueryPlugin(schema) {
const modifiedSchema = schema;
modifiedSchema.statics.apiQuery = async (rawParams, locale) => {
async function apiQuery(rawParams, locale) {
const paramParser = new ParamParser(this, rawParams, locale);
const params = await paramParser.parse();
return params.searchParams;
};
}
modifiedSchema.statics.apiQuery = apiQuery;
}
module.exports = buildQueryPlugin;