falkordb 6.9.2 → 6.9.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/README.md +56 -1
  2. package/dist/index.d.ts +6 -1
  3. package/dist/index.js +11 -1
  4. package/dist/src/clients/client.d.ts +23 -5
  5. package/dist/src/clients/client.js +7 -0
  6. package/dist/src/clients/cluster.d.ts +16 -6
  7. package/dist/src/clients/cluster.js +41 -16
  8. package/dist/src/clients/nullClient.d.ts +4 -3
  9. package/dist/src/clients/nullClient.js +5 -2
  10. package/dist/src/clients/single.d.ts +6 -5
  11. package/dist/src/clients/single.js +17 -8
  12. package/dist/src/commands/CONSTRAINT_CREATE.js +3 -1
  13. package/dist/src/commands/CONSTRAINT_DROP.js +3 -1
  14. package/dist/src/commands/COPY.js +3 -1
  15. package/dist/src/commands/EXPLAIN.d.ts +3 -2
  16. package/dist/src/commands/EXPLAIN.js +5 -4
  17. package/dist/src/commands/MEMORY_USAGE.js +7 -4
  18. package/dist/src/commands/PROFILE.d.ts +4 -3
  19. package/dist/src/commands/PROFILE.js +7 -5
  20. package/dist/src/commands/QUERY.js +7 -2
  21. package/dist/src/commands/RO_QUERY.js +5 -1
  22. package/dist/src/commands/SLOWLOG.d.ts +1 -1
  23. package/dist/src/commands/SLOWLOG.js +3 -1
  24. package/dist/src/commands/SLOWLOG_RESET.d.ts +5 -0
  25. package/dist/src/commands/SLOWLOG_RESET.js +19 -0
  26. package/dist/src/commands/index.d.ts +10 -1
  27. package/dist/src/commands/index.js +60 -12
  28. package/dist/src/entities.d.ts +19 -0
  29. package/dist/src/entities.js +121 -0
  30. package/dist/src/execution-plan.d.ts +45 -0
  31. package/dist/src/execution-plan.js +121 -0
  32. package/dist/src/falkordb.js +31 -4
  33. package/dist/src/graph.d.ts +95 -18
  34. package/dist/src/graph.js +321 -75
  35. package/dist/src/query-statistics.d.ts +6 -0
  36. package/dist/src/query-statistics.js +66 -0
  37. package/dist/src/types/falkordb-options.d.ts +8 -0
  38. package/dist/src/types/graph-value.d.ts +11 -5
  39. package/dist/src/types/index.d.ts +2 -0
  40. package/dist/src/types/index.js +2 -0
  41. package/dist/src/types/procedures.d.ts +47 -0
  42. package/dist/src/types/procedures.js +2 -0
  43. package/dist/src/types/query-options.d.ts +6 -1
  44. package/dist/src/types/query-statistics.d.ts +20 -0
  45. package/dist/src/types/query-statistics.js +2 -0
  46. package/package.json +1 -1
package/README.md CHANGED
@@ -10,6 +10,8 @@
10
10
 
11
11
  `falkordb` is a [FalkorDB](https://www.falkordb.com) client for Node.js.
12
12
 
13
+ > **Upgrading from v6?** Version 7 has breaking changes; see the [migration guide](MIGRATION.md).
14
+
13
15
  ## Installation
14
16
 
15
17
  Start a falkordb via docker:
@@ -50,7 +52,9 @@ const result = await graph.query(`MATCH (r:Rider)-[:rides]->(t:Team)
50
52
  WHERE t.name = $name RETURN r.name`,
51
53
  {params: {name: 'Yamaha'}})
52
54
 
53
- console.log(result) // Valentino Rossi
55
+ console.log(result.data) // [{ 'r.name': 'Valentino Rossi' }]
56
+ console.log(result.headers) // ['r.name']
57
+ console.log(result.stats) // { nodesCreated: 0, ..., cachedExecution: false, queryInternalExecutionTime: 0.31 }
54
58
 
55
59
  console.log(await db.list())
56
60
  console.log(await db.info())
@@ -60,6 +64,57 @@ db.close()
60
64
 
61
65
  To learn more about Cypher query language check: https://docs.falkordb.com/cypher/
62
66
 
67
+ ### Query Parameters
68
+
69
+ Parameter values can be a `string`, a finite `number`, a `bigint`, a `boolean`, `null`, an array, a typed array
70
+ (e.g. a `Float32Array` vector, sent as a list) or a plain object (sent as a map). Names that are not plain
71
+ identifiers, such as `my-key` or `@type`, work too: quote them in the query, e.g. `` $`my-key` ``.
72
+ An empty name, or one containing the `` ` `` character, throws a `TypeError`, since FalkorDB cannot
73
+ represent it.
74
+
75
+ ```typescript
76
+ await graph.query('RETURN $`my-key`, $vec', {params: {'my-key': 1, vec: new Float32Array([0.1, 0.2])}})
77
+ ```
78
+
79
+ A plain object is an object literal or one made with `Object.create(null)`. Any other object, such as a
80
+ class instance, `Date`, `Map` or `Set`, throws a `TypeError`, as do `undefined` and `NaN`; convert such
81
+ values first:
82
+
83
+ ```typescript
84
+ await graph.query('CREATE (:User $props)', {params: {props: {...user, createdAt: user.createdAt.toISOString()}}})
85
+ ```
86
+
87
+ A `bigint` is sent exactly. Integers in query results are returned as a `number` by default, so values beyond
88
+ `Number.MAX_SAFE_INTEGER` come back rounded; see [Large Integers](#large-integers) to read them exactly.
89
+
90
+ ### Large Integers
91
+
92
+ To read every integer in query results exactly, connect with `integers: 'bigint'`. Integer values then come
93
+ back as a `bigint`, including small ones; node and edge ids stay a `number`.
94
+
95
+ ```typescript
96
+ const db = await FalkorDB.connect({ socket: { host: 'localhost', port: 6379 }, integers: 'bigint' })
97
+ const { data } = await db.selectGraph('g').query('RETURN 9007199254740993 AS n')
98
+ console.log(data) // [{ n: 9007199254740993n }]
99
+ ```
100
+
101
+ `JSON.stringify` throws on a `bigint`, so convert such values before serializing them.
102
+
103
+ ### Query Replies
104
+
105
+ `graph.query`, `graph.roQuery` and the index helpers (such as `graph.createNodeRangeIndex`) resolve to `{ headers?, data?, stats }`:
106
+ `headers` and `data` are present when the query returns columns, and `stats` always holds the server's statistics as numbers (a counter the server did not report is `0`).
107
+
108
+ ```typescript
109
+ const { stats } = await graph.query(`CREATE (:Rider {name:'Jorge Lorenzo'})`)
110
+
111
+ console.log(stats.nodesCreated) // 1
112
+ console.log(stats.queryInternalExecutionTime) // milliseconds, e.g. 0.42
113
+ console.log(stats.cachedExecution) // false
114
+ ```
115
+
116
+ Version 7 removed the raw `metadata` lines (such as `"Nodes created: 1"`) from these replies; read the matching `stats` field instead.
117
+
63
118
  ### Connection Options
64
119
 
65
120
  `host` and `port` are the shorthand for a plain TCP connection and default to `localhost:6379`.
package/dist/index.d.ts CHANGED
@@ -2,4 +2,9 @@ export { Graph } from './src/graph';
2
2
  export { ConstraintType, EntityType } from './src/enums';
3
3
  export { FalkorDB } from './src/falkordb';
4
4
  export type { FalkorDBOptions } from './src/types';
5
- export type { QueryParam, QueryParams, QueryOptions, QueryOptionsBackwardCompatible } from './src/types';
5
+ export type { GraphReply, QueryStatistics } from './src/types';
6
+ export { ExecutionPlan, Operation } from './src/execution-plan';
7
+ export { nodeToString, edgeToString, pathToString, nodesEqual, edgesEqual, pathsEqual } from './src/entities';
8
+ export type { GraphNode, GraphEdge, GraphPath, GraphValue } from './src/types';
9
+ export type { QueryParam, QueryParams, QueryOptions, QueryOptionsBackwardCompatible, PlanOptions } from './src/types';
10
+ export type { CallProcedureOptions, ConstraintInfo, IndexInfo, VectorIndexOptions } from './src/types';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FalkorDB = exports.EntityType = exports.ConstraintType = exports.Graph = void 0;
3
+ exports.pathsEqual = exports.edgesEqual = exports.nodesEqual = exports.pathToString = exports.edgeToString = exports.nodeToString = exports.Operation = exports.ExecutionPlan = exports.FalkorDB = exports.EntityType = exports.ConstraintType = exports.Graph = void 0;
4
4
  // Named re-exports, not `default as`: with esModuleInterop (always on since
5
5
  // TypeScript 6) a default re-export compiles to `__importDefault(m).default`,
6
6
  // which Node's CommonJS export detection cannot see, so ESM callers could not
@@ -12,3 +12,13 @@ Object.defineProperty(exports, "ConstraintType", { enumerable: true, get: functi
12
12
  Object.defineProperty(exports, "EntityType", { enumerable: true, get: function () { return enums_1.EntityType; } });
13
13
  var falkordb_1 = require("./src/falkordb");
14
14
  Object.defineProperty(exports, "FalkorDB", { enumerable: true, get: function () { return falkordb_1.FalkorDB; } });
15
+ var execution_plan_1 = require("./src/execution-plan");
16
+ Object.defineProperty(exports, "ExecutionPlan", { enumerable: true, get: function () { return execution_plan_1.ExecutionPlan; } });
17
+ Object.defineProperty(exports, "Operation", { enumerable: true, get: function () { return execution_plan_1.Operation; } });
18
+ var entities_1 = require("./src/entities");
19
+ Object.defineProperty(exports, "nodeToString", { enumerable: true, get: function () { return entities_1.nodeToString; } });
20
+ Object.defineProperty(exports, "edgeToString", { enumerable: true, get: function () { return entities_1.edgeToString; } });
21
+ Object.defineProperty(exports, "pathToString", { enumerable: true, get: function () { return entities_1.pathToString; } });
22
+ Object.defineProperty(exports, "nodesEqual", { enumerable: true, get: function () { return entities_1.nodesEqual; } });
23
+ Object.defineProperty(exports, "edgesEqual", { enumerable: true, get: function () { return entities_1.edgesEqual; } });
24
+ Object.defineProperty(exports, "pathsEqual", { enumerable: true, get: function () { return entities_1.pathsEqual; } });
@@ -1,7 +1,14 @@
1
1
  import { RedisArgument } from "@redis/client";
2
2
  import { ConstraintType, EntityType } from "../enums";
3
3
  import FalkorDB from "../falkordb";
4
- import type { MemoryUsageOptions, MemoryUsageReply, QueryOptions, SingleGraphConnection, SlowLogReply, UdfListReply } from "../types";
4
+ import type { MemoryUsageOptions, MemoryUsageReply, PlanOptions, QueryOptions, SingleGraphConnection, SlowLogReply, UdfListReply } from "../types";
5
+ /**
6
+ * Returns integer replies as their exact digits, for graphs whose integers
7
+ * are read as `bigint`: a `number` would round any beyond 2^53.
8
+ */
9
+ export declare const EXACT_INTEGERS: {
10
+ readonly 58: StringConstructor;
11
+ };
5
12
  export interface Client {
6
13
  init(falkordb: FalkorDB): Promise<void>;
7
14
  list(): Promise<Array<string>>;
@@ -16,14 +23,25 @@ export interface Client {
16
23
  configGet(configKey: string): Promise<(string | number)[] | (string | number)[][]>;
17
24
  configSet(configKey: string, value: number | string): Promise<void>;
18
25
  info(section?: string): Promise<(string | string[])[]>;
19
- query<T>(graph: string, query: RedisArgument, options?: QueryOptions, compact?: boolean, version?: number): Promise<any>;
20
- profile<T>(graph: string, query: RedisArgument): Promise<any>;
21
- roQuery<T>(graph: string, query: RedisArgument, options?: QueryOptions, compact?: boolean, version?: number): Promise<any>;
26
+ /**
27
+ * @param exactIntegers Return integer replies as strings of their exact
28
+ * digits (see `EXACT_INTEGERS`)
29
+ */
30
+ query<T>(graph: string, query: RedisArgument, options?: QueryOptions, compact?: boolean, version?: number, exactIntegers?: boolean): Promise<any>;
31
+ profile<T>(graph: string, query: RedisArgument, options?: PlanOptions): Promise<any>;
32
+ /**
33
+ * @param primary Run on the graph's master even where reads may be served
34
+ * by replicas, for a reply that must be at least as new as the master's
35
+ * @param exactIntegers Return integer replies as strings of their exact
36
+ * digits (see `EXACT_INTEGERS`)
37
+ */
38
+ roQuery<T>(graph: string, query: RedisArgument, options?: QueryOptions, compact?: boolean, version?: number, primary?: boolean, exactIntegers?: boolean): Promise<any>;
22
39
  copy<T>(srcGraph: string, destGraph: string): Promise<any>;
23
40
  delete(graph: string): Promise<void>;
24
- explain(graph: string, query: string): Promise<any>;
41
+ explain(graph: string, query: string, options?: PlanOptions): Promise<any>;
25
42
  memoryUsage(graph: string, options?: MemoryUsageOptions): Promise<MemoryUsageReply>;
26
43
  slowLog(graph: string): Promise<SlowLogReply>;
44
+ slowLogReset(graph: string): Promise<void>;
27
45
  constraintCreate(graph: string, constraintType: ConstraintType, entityType: EntityType, label: string, ...properties: string[]): Promise<void>;
28
46
  constraintDrop(graph: string, constraintType: ConstraintType, entityType: EntityType, label: string, ...properties: string[]): Promise<void>;
29
47
  udfLoad(name: string, script: string | Function, replace?: boolean): Promise<string>;
@@ -1,2 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EXACT_INTEGERS = void 0;
4
+ const client_1 = require("@redis/client");
5
+ /**
6
+ * Returns integer replies as their exact digits, for graphs whose integers
7
+ * are read as `bigint`: a `number` would round any beyond 2^53.
8
+ */
9
+ exports.EXACT_INTEGERS = { [client_1.RESP_TYPES.NUMBER]: String };
@@ -3,19 +3,28 @@ import { ConstraintType, EntityType } from "../enums";
3
3
  import { RedisArgument, RedisFunctions, RedisScripts } from "@redis/client";
4
4
  import commands from "../commands";
5
5
  import FalkorDB from "../falkordb";
6
- import type { ClusterGraphConnection, MemoryUsageOptions, MemoryUsageReply, QueryOptions, SingleGraphConnection, UdfListReply } from "../types";
6
+ import type { ClusterGraphConnection, MemoryUsageOptions, MemoryUsageReply, PlanOptions, QueryOptions, SingleGraphConnection, UdfListReply } from "../types";
7
7
  export type { ClusterGraphConnection };
8
+ export interface ClusterOptions {
9
+ /**
10
+ * Serve read-only commands (e.g. `GRAPH.RO_QUERY`) from replicas as well as
11
+ * masters. Replica connections are sent
12
+ * [`READONLY`](https://redis.io/docs/latest/commands/readonly/) so they
13
+ * answer reads instead of redirecting them; writes still go to the masters.
14
+ */
15
+ useReplicas?: boolean;
16
+ }
8
17
  /**
9
18
  * A client that connects to a Redis Cluster.
10
19
  */
11
20
  export declare class Cluster implements Client {
12
21
  #private;
13
- constructor(client: SingleGraphConnection);
22
+ constructor(client: SingleGraphConnection, { useReplicas }?: ClusterOptions);
14
23
  getConnection(): Promise<import("@redis/client").RedisClientType<{
15
24
  falkordb: typeof commands;
16
25
  }, RedisFunctions, RedisScripts, 2, {}>>;
17
26
  init(falkordb: FalkorDB): Promise<void>;
18
- query<T>(graph: string, query: RedisArgument, options?: QueryOptions, compact?: boolean, version?: number): Promise<{
27
+ query<T>(graph: string, query: RedisArgument, options?: QueryOptions, compact?: boolean, version?: number, exactIntegers?: boolean): Promise<{
19
28
  headers: undefined;
20
29
  data: undefined;
21
30
  metadata: string[];
@@ -24,7 +33,7 @@ export declare class Cluster implements Client {
24
33
  data: (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[];
25
34
  metadata: string[];
26
35
  }>;
27
- roQuery<T>(graph: string, query: RedisArgument, options?: QueryOptions, compact?: boolean, version?: number): Promise<{
36
+ roQuery<T>(graph: string, query: RedisArgument, options?: QueryOptions, compact?: boolean, version?: number, primary?: boolean, exactIntegers?: boolean): Promise<{
28
37
  headers: undefined;
29
38
  data: undefined;
30
39
  metadata: string[];
@@ -34,7 +43,7 @@ export declare class Cluster implements Client {
34
43
  metadata: string[];
35
44
  }>;
36
45
  delete(graph: string): Promise<void>;
37
- explain(graph: string, query: string): Promise<string[]>;
46
+ explain(graph: string, query: string, options?: PlanOptions): Promise<string[]>;
38
47
  list(): Promise<Array<string>>;
39
48
  stubs(): Promise<Array<string>>;
40
49
  configGet(configKey: string): Promise<(string | number)[][] | (string | number)[]>;
@@ -47,6 +56,7 @@ export declare class Cluster implements Client {
47
56
  query: string;
48
57
  took: number;
49
58
  }[]>;
59
+ slowLogReset(graph: string): Promise<void>;
50
60
  memoryUsage(graph: string, options?: MemoryUsageOptions): Promise<MemoryUsageReply>;
51
61
  constraintCreate(graph: string, constraintType: ConstraintType, entityType: EntityType, label: string, ...properties: string[]): Promise<void>;
52
62
  constraintDrop(graph: string, constraintType: ConstraintType, entityType: EntityType, label: string, ...properties: string[]): Promise<void>;
@@ -54,7 +64,7 @@ export declare class Cluster implements Client {
54
64
  udfList(lib?: string, withCode?: boolean): Promise<UdfListReply>;
55
65
  udfFlush(): Promise<string>;
56
66
  udfDelete(lib: string): Promise<string>;
57
- profile<_T>(graph: string, query: string): Promise<string[]>;
67
+ profile<_T>(graph: string, query: string, options?: PlanOptions): Promise<string[]>;
58
68
  quit(): Promise<void>;
59
69
  disconnect(): Promise<void>;
60
70
  }
@@ -34,14 +34,16 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.Cluster = void 0;
37
- const client_1 = require("@redis/client");
37
+ const client_1 = require("./client");
38
+ const RO_QUERY = __importStar(require("../commands/RO_QUERY"));
39
+ const client_2 = require("@redis/client");
38
40
  const lodash = __importStar(require("lodash"));
39
41
  /**
40
42
  * A client that connects to a Redis Cluster.
41
43
  */
42
44
  class Cluster {
43
45
  #client;
44
- constructor(client) {
46
+ constructor(client, { useReplicas = false } = {}) {
45
47
  // Convert the single client options to a cluster client options
46
48
  const redisClusterOption = client.options;
47
49
  redisClusterOption.rootNodes = [
@@ -52,11 +54,19 @@ class Cluster {
52
54
  defaults?.url && delete defaults.url;
53
55
  redisClusterOption.defaults = defaults;
54
56
  redisClusterOption.maxCommandRedirections = 100000;
57
+ redisClusterOption.useReplicas = useReplicas;
55
58
  client.disconnect();
56
- this.#client = (0, client_1.createCluster)(redisClusterOption);
59
+ this.#client = (0, client_2.createCluster)(redisClusterOption);
57
60
  }
58
61
  async getConnection() {
59
- const connection = this.#client.nodeClient(this.#client.getRandomNode());
62
+ return this.#masterClient();
63
+ }
64
+ // Keyless commands are routed to a random node, which can be a replica when
65
+ // `useReplicas` is on, so writes without a key are sent to a master instead.
66
+ async #masterClient() {
67
+ const { masters } = this.#client;
68
+ const master = masters[Math.floor(Math.random() * masters.length)];
69
+ const connection = this.#client.nodeClient(master);
60
70
  return connection instanceof Promise ? await connection : connection;
61
71
  }
62
72
  async init(falkordb) {
@@ -65,19 +75,30 @@ class Cluster {
65
75
  .connect();
66
76
  }
67
77
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
68
- async query(graph, query, options, compact = true, version) {
69
- return this.#client.falkordb.query(graph, query, options, compact, version);
78
+ async query(graph, query, options, compact = true, version, exactIntegers = false) {
79
+ return this.#withIntegers(exactIntegers).falkordb.query(graph, query, options, compact, version);
80
+ }
81
+ #withIntegers(exactIntegers) {
82
+ return exactIntegers ? this.#client.withTypeMapping(client_1.EXACT_INTEGERS) : this.#client;
70
83
  }
71
84
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
72
- async roQuery(graph, query, options, compact = true, version) {
73
- return this.#client.falkordb.roQuery(graph, query, options, compact, version);
85
+ async roQuery(graph, query, options, compact = true, version, primary = false, exactIntegers = false) {
86
+ if (primary) {
87
+ // Still GRAPH.RO_QUERY, so read-only users can run it, but routed to the
88
+ // slot's master as a write would be
89
+ const args = RO_QUERY.transformArguments(graph, query, options, compact, version);
90
+ return RO_QUERY.transformReply(exactIntegers
91
+ ? await this.#client.sendCommand(graph, false, args, { typeMapping: client_1.EXACT_INTEGERS })
92
+ : await this.#client.sendCommand(graph, false, args));
93
+ }
94
+ return this.#withIntegers(exactIntegers).falkordb.roQuery(graph, query, options, compact, version);
74
95
  }
75
96
  async delete(graph) {
76
97
  const reply = this.#client.falkordb.delete(graph);
77
98
  return reply.then(() => { });
78
99
  }
79
- async explain(graph, query) {
80
- return this.#client.falkordb.explain(graph, query);
100
+ async explain(graph, query, options) {
101
+ return this.#client.falkordb.explain(graph, query, options);
81
102
  }
82
103
  async list() {
83
104
  const reply = await Promise.all(this.#client.masters.map(async (master) => {
@@ -120,7 +141,7 @@ class Cluster {
120
141
  return this.#client.falkordb.configGet(configKey);
121
142
  }
122
143
  async configSet(configKey, value) {
123
- const reply = this.#client.falkordb.configSet(configKey, value);
144
+ const reply = (await this.#masterClient()).falkordb.configSet(configKey, value);
124
145
  return reply.then(() => { });
125
146
  }
126
147
  async info(section) {
@@ -132,6 +153,10 @@ class Cluster {
132
153
  slowLog(graph) {
133
154
  return this.#client.falkordb.slowLog(graph);
134
155
  }
156
+ async slowLogReset(graph) {
157
+ const reply = this.#client.falkordb.slowLogReset(graph);
158
+ return reply.then(() => { });
159
+ }
135
160
  async memoryUsage(graph, options) {
136
161
  return this.#client.falkordb.memoryUsage(graph, options);
137
162
  }
@@ -144,19 +169,19 @@ class Cluster {
144
169
  return reply.then(() => { });
145
170
  }
146
171
  async udfLoad(name, script, replace = false) {
147
- return this.#client.falkordb.udfLoad(name, script, replace);
172
+ return (await this.#masterClient()).falkordb.udfLoad(name, script, replace);
148
173
  }
149
174
  async udfList(lib, withCode = false) {
150
175
  return this.#client.falkordb.udfList(lib, withCode);
151
176
  }
152
177
  async udfFlush() {
153
- return this.#client.falkordb.udfFlush();
178
+ return (await this.#masterClient()).falkordb.udfFlush();
154
179
  }
155
180
  async udfDelete(lib) {
156
- return this.#client.falkordb.udfDelete(lib);
181
+ return (await this.#masterClient()).falkordb.udfDelete(lib);
157
182
  }
158
- async profile(graph, query) {
159
- return this.#client.falkordb.profile(graph, query);
183
+ async profile(graph, query, options) {
184
+ return this.#client.falkordb.profile(graph, query, options);
160
185
  }
161
186
  async quit() {
162
187
  return this.disconnect();
@@ -2,7 +2,7 @@ import { RedisArgument } from "@redis/client";
2
2
  import FalkorDB from "../falkordb";
3
3
  import { ConstraintType, EntityType } from "../enums";
4
4
  import { Client } from "./client";
5
- import type { MemoryUsageOptions, MemoryUsageReply, QueryOptions, SingleGraphConnection, SlowLogReply, UdfListReply } from "../types";
5
+ import type { MemoryUsageOptions, MemoryUsageReply, PlanOptions, QueryOptions, SingleGraphConnection, SlowLogReply, UdfListReply } from "../types";
6
6
  /**
7
7
  * The `NullClient` class is a placeholder implementation of the `Client` interface.
8
8
  *
@@ -23,12 +23,13 @@ export declare class NullClient implements Client {
23
23
  configSet(_configKey: string, _value: number | string): Promise<void>;
24
24
  info(_section?: string): Promise<(string | string[])[]>;
25
25
  query<_T>(_graph: string, _query: RedisArgument, _options?: QueryOptions): Promise<any>;
26
- profile<_T>(_graph: string, _query: RedisArgument): Promise<any>;
26
+ profile<_T>(_graph: string, _query: RedisArgument, _options?: PlanOptions): Promise<any>;
27
27
  roQuery<_T>(_graph: string, _query: RedisArgument, _options?: QueryOptions): Promise<any>;
28
28
  copy<_T>(_srcGraph: string, _destGraph: string): Promise<any>;
29
29
  delete(_graph: string): Promise<void>;
30
- explain(_graph: string, _query: string): Promise<any>;
30
+ explain(_graph: string, _query: string, _options?: PlanOptions): Promise<any>;
31
31
  slowLog(_graph: string): Promise<SlowLogReply>;
32
+ slowLogReset(_graph: string): Promise<void>;
32
33
  memoryUsage(_graph: string, _options?: MemoryUsageOptions): Promise<MemoryUsageReply>;
33
34
  constraintCreate(_graph: string, _constraintType: ConstraintType, _entityType: EntityType, _label: string, ..._properties: string[]): Promise<void>;
34
35
  constraintDrop(_graph: string, _constraintType: ConstraintType, _entityType: EntityType, _label: string, ..._properties: string[]): Promise<void>;
@@ -37,7 +37,7 @@ class NullClient {
37
37
  query(_graph, _query, _options) {
38
38
  throw new Error("Method not implemented.");
39
39
  }
40
- profile(_graph, _query) {
40
+ profile(_graph, _query, _options) {
41
41
  throw new Error("Method not implemented.");
42
42
  }
43
43
  roQuery(_graph, _query, _options) {
@@ -49,12 +49,15 @@ class NullClient {
49
49
  delete(_graph) {
50
50
  throw new Error("Method not implemented.");
51
51
  }
52
- explain(_graph, _query) {
52
+ explain(_graph, _query, _options) {
53
53
  throw new Error("Method not implemented.");
54
54
  }
55
55
  slowLog(_graph) {
56
56
  throw new Error("Method not implemented.");
57
57
  }
58
+ slowLogReset(_graph) {
59
+ throw new Error("Method not implemented.");
60
+ }
58
61
  memoryUsage(_graph, _options) {
59
62
  throw new Error("Method not implemented.");
60
63
  }
@@ -2,7 +2,7 @@ import { Client } from "./client";
2
2
  import { ConstraintType, EntityType } from "../enums";
3
3
  import { RedisArgument } from "@redis/client";
4
4
  import FalkorDB from "../falkordb";
5
- import type { MemoryUsageOptions, MemoryUsageReply, QueryOptions, SingleGraphConnection, UdfListReply } from "../types";
5
+ import type { MemoryUsageOptions, MemoryUsageReply, PlanOptions, QueryOptions, SingleGraphConnection, UdfListReply } from "../types";
6
6
  export type { SingleGraphConnection };
7
7
  export interface RunOptions {
8
8
  /** The command does not change data, so running it twice is harmless */
@@ -17,7 +17,7 @@ export declare class Single implements Client {
17
17
  * Subclasses override it to add recovery, e.g. retrying on a new master.
18
18
  */
19
19
  protected run<T>(fn: (client: SingleGraphConnection) => Promise<T>, _options?: RunOptions): Promise<T>;
20
- query<_T>(graph: string, query: RedisArgument, options?: QueryOptions, compact?: boolean, version?: number): Promise<{
20
+ query<_T>(graph: string, query: RedisArgument, options?: QueryOptions, compact?: boolean, version?: number, exactIntegers?: boolean): Promise<{
21
21
  headers: undefined;
22
22
  data: undefined;
23
23
  metadata: string[];
@@ -26,7 +26,7 @@ export declare class Single implements Client {
26
26
  data: (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | /*elided*/ any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[];
27
27
  metadata: string[];
28
28
  }>;
29
- roQuery<_T>(graph: string, query: RedisArgument, options?: QueryOptions, compact?: boolean, version?: number): Promise<{
29
+ roQuery<_T>(graph: string, query: RedisArgument, options?: QueryOptions, compact?: boolean, version?: number, _primary?: boolean, exactIntegers?: boolean): Promise<{
30
30
  headers: undefined;
31
31
  data: undefined;
32
32
  metadata: string[];
@@ -36,8 +36,8 @@ export declare class Single implements Client {
36
36
  metadata: string[];
37
37
  }>;
38
38
  delete(graph: string): Promise<void>;
39
- explain(graph: string, query: string): Promise<string[]>;
40
- profile<_T>(graph: string, query: string): Promise<string[]>;
39
+ explain(graph: string, query: string, options?: PlanOptions): Promise<string[]>;
40
+ profile<_T>(graph: string, query: string, options?: PlanOptions): Promise<string[]>;
41
41
  list(): Promise<string[]>;
42
42
  stubs(): Promise<string[]>;
43
43
  configGet(configKey: string): Promise<(string | number)[][] | (string | number)[]>;
@@ -49,6 +49,7 @@ export declare class Single implements Client {
49
49
  query: string;
50
50
  took: number;
51
51
  }[]>;
52
+ slowLogReset(graph: string): Promise<void>;
52
53
  memoryUsage(graph: string, options?: MemoryUsageOptions): Promise<MemoryUsageReply>;
53
54
  constraintCreate(graph: string, constraintType: ConstraintType, entityType: EntityType, label: string, ...properties: string[]): Promise<void>;
54
55
  constraintDrop(graph: string, constraintType: ConstraintType, entityType: EntityType, label: string, ...properties: string[]): Promise<void>;
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Single = void 0;
4
+ const client_1 = require("./client");
4
5
  const READ_ONLY = { readOnly: true };
6
+ function withIntegers(client, exactIntegers) {
7
+ return exactIntegers ? client.withTypeMapping(client_1.EXACT_INTEGERS) : client;
8
+ }
5
9
  class Single {
6
10
  client;
7
11
  constructor(client) {
@@ -17,21 +21,22 @@ class Single {
17
21
  run(fn, _options = {}) {
18
22
  return fn(this.client);
19
23
  }
20
- async query(graph, query, options, compact = true, version) {
21
- return await this.run((client) => client.falkordb.query(graph, query, options, compact, version));
24
+ async query(graph, query, options, compact = true, version, exactIntegers = false) {
25
+ return await this.run((client) => withIntegers(client, exactIntegers).falkordb.query(graph, query, options, compact, version));
22
26
  }
23
- async roQuery(graph, query, options, compact = true, version) {
24
- return await this.run((client) => client.falkordb.roQuery(graph, query, options, compact, version), READ_ONLY);
27
+ // A single node serves every read, so `primary` needs no handling
28
+ async roQuery(graph, query, options, compact = true, version, _primary = false, exactIntegers = false) {
29
+ return await this.run((client) => withIntegers(client, exactIntegers).falkordb.roQuery(graph, query, options, compact, version), READ_ONLY);
25
30
  }
26
31
  async delete(graph) {
27
32
  const reply = this.run((client) => client.falkordb.delete(graph));
28
33
  return reply.then(() => { });
29
34
  }
30
- async explain(graph, query) {
31
- return this.run((client) => client.falkordb.explain(graph, query), READ_ONLY);
35
+ async explain(graph, query, options) {
36
+ return this.run((client) => client.falkordb.explain(graph, query, options), READ_ONLY);
32
37
  }
33
- async profile(graph, query) {
34
- return this.run((client) => client.falkordb.profile(graph, query));
38
+ async profile(graph, query, options) {
39
+ return this.run((client) => client.falkordb.profile(graph, query, options));
35
40
  }
36
41
  async list() {
37
42
  return this.run((client) => client.falkordb.list(), READ_ONLY);
@@ -52,6 +57,10 @@ class Single {
52
57
  async slowLog(graph) {
53
58
  return this.run((client) => client.falkordb.slowLog(graph), READ_ONLY);
54
59
  }
60
+ async slowLogReset(graph) {
61
+ const reply = this.run((client) => client.falkordb.slowLogReset(graph));
62
+ return reply.then(() => { });
63
+ }
55
64
  async memoryUsage(graph, options) {
56
65
  return this.run((client) => client.falkordb.memoryUsage(graph, options), READ_ONLY);
57
66
  }
@@ -9,7 +9,9 @@ Object.defineProperty(exports, "ConstraintType", { enumerable: true, get: functi
9
9
  Object.defineProperty(exports, "EntityType", { enumerable: true, get: function () { return enums_1.EntityType; } });
10
10
  // GRAPH.CONSTRAINT CREATE key constraintType {NODE label | RELATIONSHIP reltype} PROPERTIES propCount prop [prop...]
11
11
  function parseCommand(parser, key, constraintType, entityType, label, ...properties) {
12
- parser.push('GRAPH.CONSTRAINT', 'CREATE', key, constraintType, entityType, label, 'PROPERTIES', properties.length.toString(), ...properties);
12
+ parser.push('GRAPH.CONSTRAINT', 'CREATE');
13
+ parser.pushKey(key);
14
+ parser.push(constraintType, entityType, label, 'PROPERTIES', properties.length.toString(), ...properties);
13
15
  }
14
16
  function transformArguments(key, constraintType, entityType, label, ...properties) {
15
17
  return [
@@ -5,7 +5,9 @@ exports.transformArguments = transformArguments;
5
5
  exports.transformReply = transformReply;
6
6
  // GRAPH.CONSTRAINT DROP key constraintType {NODE label | RELATIONSHIP reltype} PROPERTIES propCount prop [prop...]
7
7
  function parseCommand(parser, key, constraintType, entityType, label, ...properties) {
8
- parser.push('GRAPH.CONSTRAINT', 'DROP', key, constraintType, entityType, label, 'PROPERTIES', properties.length.toString(), ...properties);
8
+ parser.push('GRAPH.CONSTRAINT', 'DROP');
9
+ parser.pushKey(key);
10
+ parser.push(constraintType, entityType, label, 'PROPERTIES', properties.length.toString(), ...properties);
9
11
  }
10
12
  function transformArguments(key, constraintType, entityType, label, ...properties) {
11
13
  return [
@@ -4,7 +4,9 @@ exports.parseCommand = parseCommand;
4
4
  exports.transformArguments = transformArguments;
5
5
  exports.transformReply = transformReply;
6
6
  function parseCommand(parser, srcGraph, destGraph) {
7
- parser.push('GRAPH.COPY', srcGraph, destGraph);
7
+ parser.push('GRAPH.COPY');
8
+ parser.pushKey(srcGraph);
9
+ parser.pushKey(destGraph);
8
10
  }
9
11
  function transformArguments(srcGraph, destGraph) {
10
12
  return [
@@ -1,6 +1,7 @@
1
1
  import { CommandParser } from '@redis/client';
2
+ import type { PlanOptions } from '../types';
2
3
  export declare const FIRST_KEY_INDEX = 1;
3
4
  export declare const IS_READ_ONLY = true;
4
- export declare function parseCommand(parser: CommandParser, key: string, query: string): void;
5
- export declare function transformArguments(key: string, query: string): Array<string>;
5
+ export declare function parseCommand(parser: CommandParser, key: string, query: string, options?: PlanOptions): void;
6
+ export declare function transformArguments(key: string, query: string, options?: PlanOptions): Array<string>;
6
7
  export declare function transformReply(reply: unknown): Array<string>;
@@ -4,15 +4,16 @@ exports.IS_READ_ONLY = exports.FIRST_KEY_INDEX = void 0;
4
4
  exports.parseCommand = parseCommand;
5
5
  exports.transformArguments = transformArguments;
6
6
  exports.transformReply = transformReply;
7
+ const _1 = require(".");
7
8
  exports.FIRST_KEY_INDEX = 1;
8
9
  exports.IS_READ_ONLY = true;
9
- function parseCommand(parser, key, query) {
10
+ function parseCommand(parser, key, query, options) {
10
11
  parser.push('GRAPH.EXPLAIN');
11
12
  parser.pushKey(key);
12
- parser.push(query);
13
+ parser.push((0, _1.withParamsHeader)(query, options?.params));
13
14
  }
14
- function transformArguments(key, query) {
15
- return ['GRAPH.EXPLAIN', key, query];
15
+ function transformArguments(key, query, options) {
16
+ return ['GRAPH.EXPLAIN', key, (0, _1.withParamsHeader)(query, options?.params)];
16
17
  }
17
18
  function transformReply(reply) {
18
19
  return reply;
@@ -4,14 +4,17 @@ exports.parseCommand = parseCommand;
4
4
  exports.transformArguments = transformArguments;
5
5
  exports.transformReply = transformReply;
6
6
  function parseCommand(parser, key, options) {
7
- parser.push("GRAPH.MEMORY", "USAGE", key);
8
- if (options?.SAMPLES) {
9
- parser.push(String(options.SAMPLES));
7
+ parser.push("GRAPH.MEMORY", "USAGE");
8
+ parser.pushKey(key);
9
+ if (options?.SAMPLES !== undefined) {
10
+ parser.push("SAMPLES", String(options.SAMPLES));
10
11
  }
11
12
  }
12
13
  function transformArguments(key, options) {
13
14
  const args = ["GRAPH.MEMORY", "USAGE", key];
14
- return options?.SAMPLES ? [...args, String(options.SAMPLES)] : [...args];
15
+ return options?.SAMPLES !== undefined
16
+ ? [...args, "SAMPLES", String(options.SAMPLES)]
17
+ : args;
15
18
  }
16
19
  function transformReply(reply) {
17
20
  return reply;
@@ -1,6 +1,7 @@
1
1
  import { CommandParser } from '@redis/client';
2
+ import type { PlanOptions } from '../types';
2
3
  export declare const FIRST_KEY_INDEX = 1;
3
- export declare const IS_READ_ONLY = true;
4
- export declare function parseCommand(parser: CommandParser, key: string, query: string): void;
5
- export declare function transformArguments(key: string, query: string): Array<string>;
4
+ export declare const IS_READ_ONLY = false;
5
+ export declare function parseCommand(parser: CommandParser, key: string, query: string, options?: PlanOptions): void;
6
+ export declare function transformArguments(key: string, query: string, options?: PlanOptions): Array<string>;
6
7
  export declare function transformReply(reply: unknown): Array<string>;
@@ -4,15 +4,17 @@ exports.IS_READ_ONLY = exports.FIRST_KEY_INDEX = void 0;
4
4
  exports.parseCommand = parseCommand;
5
5
  exports.transformArguments = transformArguments;
6
6
  exports.transformReply = transformReply;
7
+ const _1 = require(".");
7
8
  exports.FIRST_KEY_INDEX = 1;
8
- exports.IS_READ_ONLY = true;
9
- function parseCommand(parser, key, query) {
9
+ // PROFILE executes the query, which may write
10
+ exports.IS_READ_ONLY = false;
11
+ function parseCommand(parser, key, query, options) {
10
12
  parser.push('GRAPH.PROFILE');
11
13
  parser.pushKey(key);
12
- parser.push(query);
14
+ parser.push((0, _1.withParamsHeader)(query, options?.params));
13
15
  }
14
- function transformArguments(key, query) {
15
- return ['GRAPH.PROFILE', key, query];
16
+ function transformArguments(key, query, options) {
17
+ return ['GRAPH.PROFILE', key, (0, _1.withParamsHeader)(query, options?.params)];
16
18
  }
17
19
  function transformReply(reply) {
18
20
  return reply;