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

Improve db procedures #11

Merged
merged 4 commits into from
Jun 9, 2023
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
5 changes: 5 additions & 0 deletions .changeset/hip-pillows-tap.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@neo4j/cypher-builder": patch
---

Adds db.index.fulltext.queryRelationships
1 change: 1 addition & 0 deletions src/Cypher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ export type { PredicateFunction } from "./expressions/functions/PredicateFunctio
export type { Order } from "./clauses/sub-clauses/OrderBy";
export type { CompositeClause } from "./clauses/utils/concat";
export type { CypherAggregationFunction as AggregationFunction } from "./expressions/functions/AggregationFunctions";
export { InputArgument } from "./utils/normalize-variable";

// utils
export * as utils from "./utils/utils";
109 changes: 100 additions & 9 deletions src/procedures/db.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ describe("db procedures", () => {
`"CALL db.index.fulltext.queryNodes(\\"my-text-index\\", $param0) YIELD node AS this0"`
);
expect(params).toMatchInlineSnapshot(`
{
"param0": "This is a lovely phrase",
}
`);
{
"param0": "This is a lovely phrase",
}
`);
});

test("Fulltext with where and return", () => {
Expand All @@ -56,11 +56,102 @@ describe("db procedures", () => {
RETURN this0"
`);
expect(params).toMatchInlineSnapshot(`
{
"param0": "This is a lovely phrase",
"param1": "The Matrix",
}
`);
{
"param0": "This is a lovely phrase",
"param1": "The Matrix",
}
`);
});

test("Fulltext with options", () => {
const fulltextProcedure = Cypher.db.index.fulltext.queryNodes(
"my-text-index",
new Param("This is a lovely phrase"),
{
skip: 5,
analyser: new Param("whitespace"),
}
);

const { cypher, params } = fulltextProcedure.build();

expect(cypher).toMatchInlineSnapshot(
`"CALL db.index.fulltext.queryNodes(\\"my-text-index\\", $param0, { skip: 5, analyser: $param1 })"`
);
expect(params).toMatchInlineSnapshot(`
{
"param0": "This is a lovely phrase",
"param1": "whitespace",
}
`);
});
});
describe("db.index.fulltext.queryRelationships", () => {
test("Simple fulltext", () => {
const targetNode = new Cypher.Node({ labels: ["Movie"] });
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess this is misleading as I expect that the target should be of type Relationship in this case

const fulltextProcedure = Cypher.db.index.fulltext
.queryRelationships("my-text-index", new Param("This is a lovely phrase"))
.yield(["relationship", targetNode]);

const { cypher, params } = fulltextProcedure.build();

expect(cypher).toMatchInlineSnapshot(
`"CALL db.index.fulltext.queryRelationships(\\"my-text-index\\", $param0) YIELD relationship AS this0"`
);
expect(params).toMatchInlineSnapshot(`
{
"param0": "This is a lovely phrase",
}
`);
});

test("Fulltext with options", () => {
const fulltextProcedure = Cypher.db.index.fulltext.queryRelationships(
"my-text-index",
new Param("This is a lovely phrase"),
{
skip: 5,
analyser: new Param("whitespace"),
}
);

const { cypher, params } = fulltextProcedure.build();

expect(cypher).toMatchInlineSnapshot(
`"CALL db.index.fulltext.queryRelationships(\\"my-text-index\\", $param0, { skip: 5, analyser: $param1 })"`
);
expect(params).toMatchInlineSnapshot(`
{
"param0": "This is a lovely phrase",
"param1": "whitespace",
}
`);
});
});

describe("db.labels", () => {
it("dbLabels", () => {
const dbLabels = Cypher.db.labels();
const { cypher, params } = dbLabels.build();

expect(cypher).toMatchInlineSnapshot(`"CALL db.labels()"`);
expect(params).toMatchInlineSnapshot(`{}`);
});

it("dbLabels with yield *", () => {
const dbLabels = Cypher.db.labels().yield("*");
const { cypher, params } = dbLabels.build();

expect(cypher).toMatchInlineSnapshot(`"CALL db.labels() YIELD *"`);
expect(params).toMatchInlineSnapshot(`{}`);
});

it("dbLabels with yield ", () => {
const dbLabels = Cypher.db.labels().yield("label");
const { cypher, params } = dbLabels.build();

expect(cypher).toMatchInlineSnapshot(`"CALL db.labels() YIELD label"`);
expect(params).toMatchInlineSnapshot(`{}`);
});
});
});
38 changes: 32 additions & 6 deletions src/procedures/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@
*/

import { CypherProcedure } from "./CypherProcedure";
import type { Literal } from "../references/Literal";
import { Literal } from "../references/Literal";
import type { Param } from "../references/Param";
import type { Variable } from "../references/Variable";
import { normalizeVariable } from "../utils/normalize-variable";
import { InputArgument, normalizeMap, normalizeVariable } from "../utils/normalize-variable";
import { Expr } from "../types";

type FulltextPhrase = string | Literal<string> | Param | Variable;

Expand All @@ -32,12 +33,37 @@ type FulltextPhrase = string | Literal<string> | Param | Variable;
*/
export const index = {
fulltext: {
queryNodes(indexName: string | Literal<string>, phrase: FulltextPhrase): CypherProcedure<"node" | "score"> {
// TODO: add options, skip limit, analyzer
const phraseVar = normalizeVariable(phrase);
queryNodes(
indexName: string | Literal<string>,
queryString: FulltextPhrase,
options?: { skip?: InputArgument<number>; limit?: InputArgument<number>; analyser?: InputArgument<string> }
): CypherProcedure<"node" | "score"> {
const phraseVar = normalizeVariable(queryString);
const indexNameVar = normalizeVariable(indexName);

return new CypherProcedure("db.index.fulltext.queryNodes", [indexNameVar, phraseVar]);
const procedureArgs: Expr[] = [indexNameVar, phraseVar];
if (options) {
const optionsMap = normalizeMap(options);
procedureArgs.push(optionsMap);
}

return new CypherProcedure("db.index.fulltext.queryNodes", procedureArgs);
},
queryRelationships(
indexName: string | Literal<string>,
queryString: FulltextPhrase,
options?: { skip?: InputArgument<number>; limit?: InputArgument<number>; analyser?: InputArgument<string> }
): CypherProcedure<"relationship" | "score"> {
const phraseVar = normalizeVariable(queryString);
const indexNameVar = normalizeVariable(indexName);

const procedureArgs: Expr[] = [indexNameVar, phraseVar];
if (options) {
const optionsMap = normalizeMap(options);
procedureArgs.push(optionsMap);
}

return new CypherProcedure("db.index.fulltext.queryRelationships", procedureArgs);
},
},
};
Expand Down
14 changes: 13 additions & 1 deletion src/utils/normalize-variable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,20 @@ import { Literal } from "../references/Literal";
import { isCypherCompilable } from "./is-cypher-compilable";
import type { Param } from "../references/Param";
import type { Variable } from "../references/Variable";
import { MapExpr } from "../expressions/map/MapExpr";

export function normalizeVariable(value: string | number | Variable | Literal | Param): Variable | Literal | Param {
type VariableInput = string | number | Variable | Literal | Param;

export type InputArgument<T extends string | number> = T | Variable | Literal<T> | Param<T>;

export function normalizeVariable(value: VariableInput): Variable | Literal | Param {
if (isCypherCompilable(value)) return value;
return new Literal(value);
}

export function normalizeMap(map: Record<string, VariableInput>): MapExpr {
return Object.entries(map).reduce((mapExpr, [key, value]) => {
mapExpr.set(key, normalizeVariable(value));
return mapExpr;
}, new MapExpr());
}
Loading