Skip to content

Commit

Permalink
Merge branch 'dev' into move-interface-projection-to-cypher-builder
Browse files Browse the repository at this point in the history
  • Loading branch information
angrykoala authored Sep 5, 2022
2 parents f62b63a + f6715af commit 2607021
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export { PatternComprehension } from "./expressions/list/PatternComprehension";

// --Map
export { MapExpr as Map } from "./expressions/map/MapExpr";
export { MapProjection } from "./expressions/map/MapProjection";

// --Operations
export { or, and, not } from "./expressions/operations/boolean";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
*/

import { CypherASTNode } from "../../CypherASTNode";
import type { MapProjection } from "../../CypherBuilder";
import type { CypherEnvironment } from "../../Environment";
import type { MapExpr } from "../../expressions/map/MapExpr";
import type { PropertyRef } from "../../expressions/PropertyRef";
import type { Expr } from "../../types";
import { padBlock } from "../../utils/utils";

export type SetParam = [PropertyRef, Expr];
export type SetParam = [PropertyRef, Exclude<Expr, MapExpr | MapProjection>];

export class SetClause extends CypherASTNode {
protected params: SetParam[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import type { CypherEnvironment } from "../../Environment";
import type { CypherCompilable, Expr } from "../../types";
import { serializeMap } from "../../utils/serialize-map";

/** Represents a Map */
export class MapExpr implements CypherCompilable {
Expand All @@ -33,14 +34,6 @@ export class MapExpr implements CypherCompilable {
}

public getCypher(env: CypherEnvironment): string {
return this.serializeObject(env);
}

private serializeObject(env: CypherEnvironment): string {
const valuesList = Object.entries(this.value).map(([key, value]) => {
return `${key}: ${value.getCypher(env)}`;
});

return `{ ${valuesList.join(", ")} }`;
return serializeMap(env, this.value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* 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.
*/

import { TestClause } from "../../utils/TestClause";
import * as CypherBuilder from "../../CypherBuilder";

describe("Map Projection", () => {
test("Project empty map", () => {
const mapProjection = new CypherBuilder.MapProjection(new CypherBuilder.Variable(), []);

const queryResult = new TestClause(mapProjection).build();

expect(queryResult.cypher).toMatchInlineSnapshot(`"var0 { }"`);

expect(queryResult.params).toMatchInlineSnapshot(`Object {}`);
});

test("Project map with variables and nodes in projection", () => {
const var1 = new CypherBuilder.Variable();
const var2 = new CypherBuilder.NamedVariable("NamedVar");
const node = new CypherBuilder.Node({});

const mapProjection = new CypherBuilder.MapProjection(new CypherBuilder.Variable(), [var1, var2, node]);

const queryResult = new TestClause(mapProjection).build();

expect(queryResult.cypher).toMatchInlineSnapshot(`"var0 { .var1, .NamedVar, .this2 }"`);

expect(queryResult.params).toMatchInlineSnapshot(`Object {}`);
});

test("Project map with extra values only", () => {
const var1 = new CypherBuilder.Variable();
const var2 = new CypherBuilder.NamedVariable("NamedVar");

const mapProjection = new CypherBuilder.MapProjection(new CypherBuilder.Variable(), [], {
myValue: var1,
namedValue: var2,
});

const queryResult = new TestClause(mapProjection).build();

expect(queryResult.cypher).toMatchInlineSnapshot(`"var0 { myValue: var1, namedValue: NamedVar }"`);

expect(queryResult.params).toMatchInlineSnapshot(`Object {}`);
});

test("Project map with variables in projection and extra values", () => {
const var1 = new CypherBuilder.Variable();
const var2 = new CypherBuilder.NamedVariable("NamedVar");
const node = new CypherBuilder.Node({});

const mapProjection = new CypherBuilder.MapProjection(new CypherBuilder.Variable(), [var1, var2], {
namedValue: CypherBuilder.count(node),
});
const queryResult = new TestClause(mapProjection).build();

expect(queryResult.cypher).toMatchInlineSnapshot(`"var0 { .var2, .NamedVar, namedValue: count(this1) }"`);

expect(queryResult.params).toMatchInlineSnapshot(`Object {}`);
});

test("Map Projection in return", () => {
const mapVar = new CypherBuilder.Variable();
const var1 = new CypherBuilder.Variable();
const var2 = new CypherBuilder.NamedVariable("NamedVar");
const node = new CypherBuilder.Node({});

const mapProjection = new CypherBuilder.MapProjection(mapVar, [var1, var2], {
namedValue: CypherBuilder.count(node),
});

const queryResult = new CypherBuilder.Return([mapProjection, mapVar]).build();

expect(queryResult.cypher).toMatchInlineSnapshot(
`"RETURN var0 { .var2, .NamedVar, namedValue: count(this1) } AS var0"`
);

expect(queryResult.params).toMatchInlineSnapshot(`Object {}`);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* 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.
*/

import type { CypherEnvironment } from "../../Environment";
import type { CypherCompilable, Expr } from "../../types";
import { serializeMap } from "../../utils/serialize-map";
import { Variable } from "../../variables/Variable";

/** Represents a Map projection https://neo4j.com/docs/cypher-manual/current/syntax/maps/#cypher-map-projection */
export class MapProjection implements CypherCompilable {
private extraValues: Record<string, Expr>;
private variable: Variable;
private projection: Array<Variable>;

constructor(variable: Variable, projection: Array<Variable>, extraValues: Record<string, Expr> = {}) {
this.variable = variable;
this.projection = projection;
this.extraValues = extraValues;
}

public set(values: Record<string, Expr> | Variable): void {
if (values instanceof Variable) {
this.projection.push(values);
} else {
this.extraValues = { ...this.extraValues, ...values };
}
}

public getCypher(env: CypherEnvironment): string {
const variableStr = this.variable.getCypher(env);
const extraValuesStr = serializeMap(env, this.extraValues, true);

const projectionStr = this.projection.map((v) => `.${v.getCypher(env)}`).join(", ");

const commaStr = extraValuesStr && projectionStr ? ", " : "";

return `${variableStr} { ${projectionStr}${commaStr}${extraValuesStr} }`;
}
}
4 changes: 3 additions & 1 deletion packages/graphql/src/translate/cypher-builder/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import type { Case } from "./expressions/Case";
import type { MathOp } from "./expressions/operations/math";
import type { ListComprehension } from "./expressions/list/ListComprehension";
import type { PatternComprehension } from "./expressions/list/PatternComprehension";
import type { MapProjection } from "./CypherBuilder";

export type Operation = BooleanOp | ComparisonOp | MathOp;

Expand All @@ -45,7 +46,8 @@ export type Expr =
| Predicate
| ListComprehension
| PatternComprehension
| MapExpr
| MapExpr // NOTE this cannot be set as a property in a node
| MapProjection // NOTE this cannot be set as a property in a node
| RunFirstColumn;

/** Represents a predicate statement (i.e returns a boolean). Note that RawCypher is only added for compatibility */
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* 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.
*/

import type { Expr } from "../CypherBuilder";
import type { CypherEnvironment } from "../Environment";

export function serializeMap(env: CypherEnvironment, obj: Record<string, Expr>, omitCurlyBraces = false): string {
const valuesList = Object.entries(obj).map(([key, value]) => {
return `${key}: ${value.getCypher(env)}`;
});

const serializedContent = valuesList.join(", ");
if (omitCurlyBraces) return serializedContent;

return `{ ${serializedContent} }`;
}

0 comments on commit 2607021

Please sign in to comment.