Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add normalize function #312

Merged
merged 1 commit into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .changeset/hip-clocks-allow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@neo4j/cypher-builder": minor
---

Add support for `normalize` function:

```
Cypher.normalize(new Cypher.Param("my string"), "NFC");
```
51 changes: 35 additions & 16 deletions src/expressions/functions/string.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,16 @@
* limitations under the License.
*/

import { TestClause } from "../../utils/TestClause";
import Cypher from "../..";
import { TestClause } from "../../utils/TestClause";

describe("String Functions", () => {
// Functions with 1 argument
test.each(["lTrim", "rTrim", "toLower", "toString", "toStringOrNull", "toUpper", "trim"] as const)(
test.each(["lTrim", "rTrim", "toLower", "toString", "toStringOrNull", "toUpper", "trim", "normalize"] as const)(
"%s",
(value) => {
const toLowerFunction = Cypher[value](new Cypher.Param("Hello"));
const { cypher, params } = new TestClause(toLowerFunction).build();
const testFunction = Cypher[value](new Cypher.Param("Hello"));
const { cypher, params } = new TestClause(testFunction).build();

expect(cypher).toBe(`${value}($param0)`);

Expand All @@ -38,8 +38,8 @@ describe("String Functions", () => {

// Functions with 2 arguments
test.each(["left", "right", "split"] as const)("%s", (value) => {
const toLowerFunction = Cypher[value](new Cypher.Param("Hello"), new Cypher.Literal("Hello"));
const { cypher, params } = new TestClause(toLowerFunction).build();
const testFunction = Cypher[value](new Cypher.Param("Hello"), new Cypher.Literal("Hello"));
const { cypher, params } = new TestClause(testFunction).build();

expect(cypher).toBe(`${value}($param0, "Hello")`);

Expand All @@ -49,12 +49,12 @@ describe("String Functions", () => {
});

test("replace", () => {
const toLowerFunction = Cypher.replace(
const replaceFunction = Cypher.replace(
new Cypher.Param("Helo"),
new Cypher.Literal("lo"),
new Cypher.Literal("llo")
);
const { cypher, params } = new TestClause(toLowerFunction).build();
const { cypher, params } = new TestClause(replaceFunction).build();

expect(cypher).toMatchInlineSnapshot(`"replace($param0, \\"lo\\", \\"llo\\")"`);

Expand All @@ -66,8 +66,8 @@ describe("String Functions", () => {
});

test("substring with 2 arguments", () => {
const toLowerFunction = Cypher.substring(new Cypher.Param("Hello"), new Cypher.Literal("lo"));
const { cypher, params } = new TestClause(toLowerFunction).build();
const substringFunction = Cypher.substring(new Cypher.Param("Hello"), new Cypher.Literal("lo"));
const { cypher, params } = new TestClause(substringFunction).build();

expect(cypher).toMatchInlineSnapshot(`"substring($param0, \\"lo\\")"`);

Expand All @@ -79,12 +79,8 @@ describe("String Functions", () => {
});

test("substring with 3 arguments", () => {
const toLowerFunction = Cypher.substring(
new Cypher.Param("Hello"),
new Cypher.Literal("lo"),
new Cypher.Literal(2)
);
const { cypher, params } = new TestClause(toLowerFunction).build();
const substring = Cypher.substring(new Cypher.Param("Hello"), new Cypher.Literal("lo"), new Cypher.Literal(2));
const { cypher, params } = new TestClause(substring).build();

expect(cypher).toMatchInlineSnapshot(`"substring($param0, \\"lo\\", 2)"`);

Expand All @@ -94,4 +90,27 @@ describe("String Functions", () => {
}
`);
});

test.each(["NFC", "NFD", "NFKC", "NFKD"] as const)("normalize with normalForm '%s' parameter", (normalForm) => {
const normalizeFunction = Cypher.normalize(new Cypher.Param("Hello"), normalForm);
const { cypher, params } = new TestClause(normalizeFunction).build();

expect(cypher).toBe(`normalize($param0, "${normalForm}")`);

expect(params).toEqual({
param0: "Hello",
});
});

test("normalize with normalForm parameter as an expression", () => {
const normalizeFunction = Cypher.normalize(new Cypher.Param("Hello"), new Cypher.Param("NFC"));
const { cypher, params } = new TestClause(normalizeFunction).build();

expect(cypher).toBe(`normalize($param0, $param1)`);

expect(params).toEqual({
param0: "Hello",
param1: "NFC",
});
});
});
13 changes: 13 additions & 0 deletions src/expressions/functions/string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import type { Expr } from "../../types";
import { filterTruthy } from "../../utils/filter-truthy";
import { normalizeExpr } from "../../utils/normalize-variable";

import { CypherFunction } from "./CypherFunctions";

Expand All @@ -40,6 +41,18 @@ export function lTrim(original: Expr): CypherFunction {
return new CypherFunction("lTrim", [original]);
}

/**
* @see [Cypher Documentation](https://neo4j.com/docs/cypher-manual/current/functions/string/)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reckon https://neo4j.com/docs/cypher-manual/current/functions/string/#functions-normalize would be the more accurate (as this one covers both), but then for consistency we should update all the links of each individual function here 😅

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reckon https://neo4j.com/docs/cypher-manual/current/functions/string/#functions-normalize would be the more accurate (as this one covers both), but then for consistency we should update all the links of each individual function here 😅

Oh I see, yeah maybe so in a different PR?

Copy link
Member Author

@angrykoala angrykoala Feb 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe, tbh we need to give a bit of thought to these links.

I added them because I think it is a great documentation tool to have these links in the editor, but we have no guarantee that these links will remain up (in fact, I'm sure some of these links are already broken)

* @group Cypher Functions
* @category String
* @param normalForm - A string with the normal form to use or a Cypher expression
* @example `Cypher.normalize(param, "NFC")`
*/
export function normalize(input: Expr, normalForm?: "NFC" | "NFD" | "NFKC" | "NFKD" | Expr): CypherFunction {
const normalFormExpr = normalForm ? normalizeExpr(normalForm) : undefined;
return new CypherFunction("normalize", filterTruthy([input, normalFormExpr]));
}

/**
* @see [Cypher Documentation](https://neo4j.com/docs/cypher-manual/current/functions/string/)
* @group Cypher Functions
Expand Down
Loading