@neo4j/graphql
Version:
A GraphQL to Cypher query execution layer for Neo4j and JavaScript GraphQL implementations
143 lines • 6.88 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.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CompositeConnectionReadOperation = void 0;
const cypher_builder_1 = __importDefault(require("@neo4j/cypher-builder"));
const operations_1 = require("../operations");
const utils_1 = require("../../../../../utils/utils");
const context_has_target_1 = require("../../../utils/context-has-target");
const QueryASTContext_1 = require("../../QueryASTContext");
class CompositeConnectionReadOperation extends operations_1.Operation {
constructor(children) {
super();
this.sortFields = [];
this.children = children;
}
transpile(context) {
const edgeVar = new cypher_builder_1.default.NamedVariable("edge");
const edgesVar = new cypher_builder_1.default.NamedVariable("edges");
const totalCount = new cypher_builder_1.default.NamedVariable("totalCount");
const nestedSubqueries = this.children.flatMap((c) => {
const subQueryContext = new QueryASTContext_1.QueryASTContext({ ...context, returnVariable: edgeVar });
const result = c.transpile(subQueryContext);
if ((0, context_has_target_1.hasTarget)(context)) {
const parentNode = context.target;
return result.clauses.map((sq) => cypher_builder_1.default.utils.concat(new cypher_builder_1.default.With(parentNode), sq));
}
else {
return result.clauses;
}
});
const union = new cypher_builder_1.default.Union(...nestedSubqueries);
const contextTarget = context.target ? [context.target] : [];
const nestedSubquery = new cypher_builder_1.default.Call(new cypher_builder_1.default.Call(union, contextTarget).return([cypher_builder_1.default.collect(edgeVar), edgesVar]), contextTarget);
let orderSubquery;
let returnEdgesVar = edgesVar;
if (this.pagination || this.sortFields.length > 0) {
const paginationField = this.pagination && this.pagination.getPagination();
const nestedContext = new QueryASTContext_1.QueryASTContext({
// NOOP context
target: new cypher_builder_1.default.Node(),
env: context.env,
neo4jGraphQLContext: context.neo4jGraphQLContext,
});
const sortFields = this.getSortFields(nestedContext, edgeVar.property("node"), edgeVar.property("properties"));
const extraWithOrder = new cypher_builder_1.default.Unwind([edgesVar, edgeVar]).with(edgeVar).orderBy(...sortFields);
if (paginationField && paginationField.skip) {
extraWithOrder.skip(paginationField.skip);
}
// Missing skip
if (paginationField && paginationField.limit) {
extraWithOrder.limit(paginationField.limit);
}
const edgesVar2 = new cypher_builder_1.default.Variable();
extraWithOrder.return([cypher_builder_1.default.collect(edgeVar), edgesVar2]);
returnEdgesVar = edgesVar2;
orderSubquery = new cypher_builder_1.default.Call(extraWithOrder, [edgesVar]);
}
const { fields: aggregateFields, subqueries: aggregateSubqueries, projectionMap: aggregateReturnMap, } = this.transpileAggregation(context);
const aggregateVariables = aggregateFields.map((c) => c[1]);
const subqueryWith = new cypher_builder_1.default.With(edgesVar, ...aggregateFields).with(edgesVar, [cypher_builder_1.default.size(edgesVar), totalCount], ...aggregateVariables);
const returnClause = new cypher_builder_1.default.Return([
new cypher_builder_1.default.Map({
edges: returnEdgesVar,
totalCount: totalCount,
...aggregateReturnMap,
}),
context.returnVariable,
]);
return {
clauses: [
cypher_builder_1.default.utils.concat(nestedSubquery, ...aggregateSubqueries.map((clause) => new cypher_builder_1.default.Call(clause, (0, utils_1.filterTruthy)([context.target]))), subqueryWith, orderSubquery, returnClause),
],
projectionExpr: context.returnVariable,
};
}
addSort(sortElement) {
this.sortFields.push(sortElement);
}
addPagination(pagination) {
this.pagination = pagination;
}
setAggregationField(aggregationField) {
this.aggregationField = aggregationField;
}
getChildren() {
const sortFields = this.sortFields.flatMap((s) => {
return [...s.edge, ...s.node];
});
return (0, utils_1.filterTruthy)([...this.children, this.aggregationField, ...sortFields, this.pagination]);
}
getSortFields(context, nodeVar, edgeVar) {
return this.sortFields.flatMap(({ node, edge }) => {
const nodeFields = node.flatMap((s) => s.getSortFields(context, nodeVar, false));
const edgeFields = edge.flatMap((s) => s.getSortFields(context, edgeVar, false));
return [...nodeFields, ...edgeFields];
});
}
// NOTE: duplicate from ConnectionReadOperation
transpileAggregation(context) {
if (!this.aggregationField) {
return {
fields: [],
subqueries: [],
projectionMap: {},
};
}
const projectionMap = {};
const subqueries = this.aggregationField.getSubqueries(context);
const aggregationProjectionField = this.aggregationField.getProjectionField();
const fields = Object.entries(aggregationProjectionField).map(([key, value]) => {
const variable = new cypher_builder_1.default.Variable();
projectionMap[key] = variable;
return [value, variable];
});
return {
fields,
subqueries,
projectionMap,
};
}
}
exports.CompositeConnectionReadOperation = CompositeConnectionReadOperation;
//# sourceMappingURL=CompositeConnectionReadOperation.js.map