@neo4j/graphql
Version:
A GraphQL to Cypher query execution layer for Neo4j and JavaScript GraphQL implementations
232 lines • 10.2 kB
JavaScript
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.getWhereFieldsForAttributes = getWhereFieldsForAttributes;
const constants_1 = require("../constants");
const ConcreteEntityAdapter_1 = require("../schema-model/entity/model-adapters/ConcreteEntityAdapter");
const augment_where_input_1 = require("./generation/augment-where-input");
const get_input_filter_from_attribute_type_1 = require("./generation/get-input-filter-from-attribute-type");
const utils_1 = require("./generation/utils");
const to_compose_1 = require("./to-compose");
// TODO: refactoring needed!
// isWhereField, isFilterable, ... extracted out into attributes category
// even more now as Cypher filters and generic input object are added in the mix
function getWhereFieldsForAttributes({ attributes, userDefinedFieldDirectives, features, ignoreCypherFieldFilters, composer, }) {
const result = {};
// Add the where fields for each attribute
for (const field of attributes) {
const userDefinedDirectivesOnField = userDefinedFieldDirectives?.get(field.name);
const deprecatedDirectives = (0, to_compose_1.graphqlDirectivesToCompose)((userDefinedDirectivesOnField ?? []).filter((directive) => directive.name.value === constants_1.DEPRECATED));
if (field.annotations.cypher) {
// If the field is a cypher field and ignoreCypherFieldFilters is true, skip it
if (ignoreCypherFieldFilters === true) {
continue;
}
// If the field is a cypher field with arguments, skip it
if (field.args.length > 0) {
continue;
}
if (field.annotations.cypher.targetEntity) {
const targetEntityAdapter = new ConcreteEntityAdapter_1.ConcreteEntityAdapter(field.annotations.cypher.targetEntity);
const type = targetEntityAdapter.operations.whereInputTypeName;
// Add list where field filters (e.g. name_ALL, name_NONE, name_SINGLE, name_SOME)
if (field.typeHelper.isList()) {
addCypherRelationshipLegacyFilters({
field,
type,
result,
deprecatedDirectives,
});
addCypherRelationshipFilter({ field, type, result, deprecatedDirectives, composer });
}
else {
// Add base where field filter (e.g. name)
result[field.name] = {
type,
directives: deprecatedDirectives,
};
}
continue;
}
}
result[field.name] = {
type: (0, get_input_filter_from_attribute_type_1.getInputFilterFromAttributeType)(field, features),
directives: deprecatedDirectives,
};
if (!(0, utils_1.shouldAddDeprecatedFields)(features, "attributeFilters")) {
continue;
}
result[`${field.name}_EQ`] = {
type: field.getInputTypeNames().where.pretty,
directives: getAttributeDeprecationDirective(deprecatedDirectives, field, "EQ"),
};
// If the field is a boolean, skip it
// This is done here because the previous additions are still added for boolean fields
if (field.typeHelper.isBoolean()) {
continue;
}
// If the field is an array, add the includes and not includes fields
// if (field.isArray()) {
if (field.typeHelper.isList()) {
result[`${field.name}_INCLUDES`] = {
type: field.getInputTypeNames().where.type,
directives: getAttributeDeprecationDirective(deprecatedDirectives, field, "INCLUDES"),
};
continue;
}
// If the field is not an array, add the in and not in fields
result[`${field.name}_IN`] = {
type: field.getFilterableInputTypeName(),
directives: getAttributeDeprecationDirective(deprecatedDirectives, field, "IN"),
};
// If the field is a number or temporal, add the comparison operators
if (field.isNumericalOrTemporal()) {
["LT", "LTE", "GT", "GTE"].forEach((comparator) => {
result[`${field.name}_${comparator}`] = {
type: field.getInputTypeNames().where.type,
directives: getAttributeDeprecationDirective(deprecatedDirectives, field, comparator),
};
});
continue;
}
// If the field is spatial, add the point comparison operators
if (field.typeHelper.isSpatial()) {
["DISTANCE", "LT", "LTE", "GT", "GTE"].forEach((comparator) => {
result[`${field.name}_${comparator}`] = {
type: `${field.getTypeName()}Distance`,
directives: getAttributeDeprecationDirective(deprecatedDirectives, field, comparator),
};
});
continue;
}
// If the field is a string, add the string comparison operators
if (field.typeHelper.isString() || field.typeHelper.isID()) {
const stringWhereOperators = [
{ comparator: "CONTAINS", typeName: field.getInputTypeNames().where.type },
{ comparator: "STARTS_WITH", typeName: field.getInputTypeNames().where.type },
{ comparator: "ENDS_WITH", typeName: field.getInputTypeNames().where.type },
];
Object.entries(features?.filters?.[field.getInputTypeNames().where.type] || {}).forEach(([filter, enabled]) => {
if (enabled) {
if (filter === "MATCHES") {
stringWhereOperators.push({ comparator: filter, typeName: "String" });
}
else {
stringWhereOperators.push({
comparator: filter,
typeName: field.getInputTypeNames().where.type,
});
}
}
});
stringWhereOperators.forEach(({ comparator, typeName }) => {
const excludedComparators = ["CASE_INSENSITIVE"];
if (!excludedComparators.includes(comparator)) {
result[`${field.name}_${comparator}`] = {
type: typeName,
directives: getAttributeDeprecationDirective(deprecatedDirectives, field, comparator),
};
}
});
}
}
return result;
}
function getAttributeDeprecationDirective(deprecatedDirectives, field, comparator) {
if (deprecatedDirectives.length) {
return deprecatedDirectives;
}
switch (comparator) {
case "DISTANCE":
case "LT":
case "LTE":
case "GT":
case "GTE":
case "CONTAINS":
case "MATCHES":
case "IN":
case "INCLUDES":
case "EQ": {
return [
{
name: constants_1.DEPRECATED,
args: {
reason: `Please use the relevant generic filter ${field.name}: { ${comparator.toLowerCase()}: ... }`,
},
},
];
}
case "STARTS_WITH": {
return [
{
name: constants_1.DEPRECATED,
args: {
reason: `Please use the relevant generic filter ${field.name}: { startsWith: ... }`,
},
},
];
}
case "ENDS_WITH": {
return [
{
name: constants_1.DEPRECATED,
args: {
reason: `Please use the relevant generic filter ${field.name}: { endsWith: ... }`,
},
},
];
}
default: {
throw new Error(`Unknown comparator: ${comparator}`);
}
}
}
function addCypherRelationshipFilter({ field, type, result, deprecatedDirectives, composer, }) {
const targetName = field.annotations.cypher?.targetEntity?.name;
if (!targetName) {
throw new Error("Target entity is not defined for the cypher field");
}
// Relationship filters
const relationshipFiltersFields = (0, augment_where_input_1.fieldConfigsToFieldConfigMap)({
deprecatedDirectives: [],
fields: (0, augment_where_input_1.getRelationshipFilters)({
relationshipInfo: { targetName, inputTypeName: type },
}),
});
// this mimic the adapter RelationshipOperation field "relationshipFiltersTypeName"
const relationshipType = `${targetName}RelationshipFilters`;
composer.getOrCreateITC(relationshipType, (itc) => {
itc.addFields(relationshipFiltersFields);
});
result[field.name] = {
type: relationshipType,
directives: deprecatedDirectives,
};
}
function addCypherRelationshipLegacyFilters({ field, type, result, deprecatedDirectives, }) {
const quantifiers = ["ALL", "NONE", "SINGLE", "SOME"];
for (const quantifier of quantifiers) {
result[`${field.name}_${quantifier}`] = {
type,
directives: deprecatedDirectives,
};
}
}
//# sourceMappingURL=get-where-fields.js.map
;