falkordb 6.2.0-beta.1 → 6.2.0

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.
package/README.md CHANGED
@@ -44,8 +44,15 @@ console.log('Connected to FalkorDB')
44
44
 
45
45
  const graph = db.selectGraph('myGraph')
46
46
 
47
- const result = await graph.query('MATCH (n) RETURN n')
48
- console.log(result)
47
+ await graph.query(`CREATE (:Rider {name:'Valentino Rossi'})-[:rides]->(:Team {name:'Yamaha'}),
48
+ (:Rider {name:'Dani Pedrosa'})-[:rides]->(:Team {name:'Honda'}),
49
+ (:Rider {name:'Andrea Dovizioso'})-[:rides]->(:Team {name:'Ducati'})`)
50
+
51
+ result = await graph.query(`MATCH (r:Rider)-[:rides]->(t:Team)
52
+ WHERE t.name = $name RETURN r.name`,
53
+ {params: {name: 'Yamaha'}})
54
+
55
+ console.log(result) // Valentino Rossi
49
56
 
50
57
  console.log(await db.list())
51
58
  console.log(await db.info())
@@ -53,6 +60,8 @@ console.log(await db.info())
53
60
  db.close()
54
61
  ```
55
62
 
63
+ To learn more about Cypher query language check: https://docs.falkordb.com/cypher/
64
+
56
65
  #### `.close()`
57
66
 
58
67
  Forcibly close a client's connection to FalkorDB immediately. Calling `close` will not send further pending commands to the Redis server, or wait for or parse outstanding responses.
@@ -0,0 +1,26 @@
1
+ import { RedisCommandArgument } from "@redis/client/dist/lib/commands";
2
+ import { QueryOptions } from "../commands";
3
+ import { ConstraintType, EntityType } from "../graph";
4
+ import FalkorDB from "../falkordb";
5
+ export interface Client {
6
+ init(falkordb: FalkorDB): Promise<void>;
7
+ list(): Promise<Array<string>>;
8
+ configGet(configKey: string): Promise<(string | number)[] | (string | number)[][]>;
9
+ configSet(configKey: string, value: number | string): Promise<void>;
10
+ info(section?: string): Promise<(string | string[])[]>;
11
+ query<T>(graph: string, query: RedisCommandArgument, options?: QueryOptions): Promise<any>;
12
+ profile<T>(graph: string, query: RedisCommandArgument): Promise<any>;
13
+ roQuery<T>(graph: string, query: RedisCommandArgument, options?: QueryOptions): Promise<any>;
14
+ copy<T>(srcGraph: string, destGraph: string): Promise<any>;
15
+ delete(graph: string): Promise<void>;
16
+ explain(graph: string, query: string): Promise<any>;
17
+ slowLog(graph: string): Promise<{
18
+ timestamp: Date;
19
+ command: string;
20
+ query: string;
21
+ took: number;
22
+ }[]>;
23
+ constraintCreate(graph: string, constraintType: ConstraintType, entityType: EntityType, label: string, ...properties: string[]): Promise<void>;
24
+ constraintDrop(graph: string, constraintType: ConstraintType, entityType: EntityType, label: string, ...properties: string[]): Promise<void>;
25
+ quit(): Promise<void>;
26
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,53 @@
1
+ import { Client } from "./client";
2
+ import { ConstraintType, EntityType } from "../graph";
3
+ import { RedisCommandArgument, RedisFunctions, RedisScripts } from "@redis/client/dist/lib/commands";
4
+ import commands, { QueryOptions } from "../commands";
5
+ import { RedisClusterType } from "@redis/client";
6
+ import FalkorDB from "../falkordb";
7
+ import { SingleGraphConnection } from "./single";
8
+ export type ClusterGraphConnection = RedisClusterType<{
9
+ falkordb: typeof commands;
10
+ }, RedisFunctions, RedisScripts>;
11
+ /**
12
+ * A client that connects to a Redis Cluster.
13
+ */
14
+ export declare class Cluster implements Client {
15
+ #private;
16
+ constructor(client: SingleGraphConnection);
17
+ init(falkordb: FalkorDB): Promise<void>;
18
+ query<T>(graph: string, query: RedisCommandArgument, options?: QueryOptions): Promise<{
19
+ headers: undefined;
20
+ data: undefined;
21
+ metadata: string[];
22
+ } | {
23
+ headers: string[];
24
+ data: (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[];
25
+ metadata: string[];
26
+ }>;
27
+ roQuery<T>(graph: string, query: RedisCommandArgument, options?: QueryOptions): Promise<{
28
+ headers: undefined;
29
+ data: undefined;
30
+ metadata: string[];
31
+ } | {
32
+ headers: string[];
33
+ data: (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[];
34
+ metadata: string[];
35
+ }>;
36
+ delete(graph: string): Promise<void>;
37
+ explain(graph: string, query: string): Promise<string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | any | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined>;
38
+ list(): Promise<Array<string>>;
39
+ configGet(configKey: string): Promise<(string | number)[] | (string | number)[][]>;
40
+ configSet(configKey: string, value: number | string): Promise<void>;
41
+ info(section?: string): Promise<(string | string[])[]>;
42
+ copy<T>(srcGraph: string, destGraph: string): Promise<"OK">;
43
+ slowLog(graph: string): Promise<{
44
+ timestamp: Date;
45
+ command: string;
46
+ query: string;
47
+ took: number;
48
+ }[]>;
49
+ constraintCreate(graph: string, constraintType: ConstraintType, entityType: EntityType, label: string, ...properties: string[]): Promise<void>;
50
+ constraintDrop(graph: string, constraintType: ConstraintType, entityType: EntityType, label: string, ...properties: string[]): Promise<void>;
51
+ profile<T>(graph: string, query: string): Promise<string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | any | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined>;
52
+ quit(): Promise<void>;
53
+ }
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Cluster = void 0;
4
+ const client_1 = require("@redis/client");
5
+ const lodash = require("lodash");
6
+ /**
7
+ * A client that connects to a Redis Cluster.
8
+ */
9
+ class Cluster {
10
+ #client;
11
+ constructor(client) {
12
+ // Convert the single client options to a cluster client options
13
+ const redisClusterOption = client.options;
14
+ redisClusterOption.rootNodes = [client.options];
15
+ // Remove the URL from the defaults so it won't override the dynamic cluster URLs
16
+ const defaults = lodash.cloneDeep(client.options);
17
+ defaults?.url && delete defaults.url;
18
+ redisClusterOption.defaults = defaults;
19
+ redisClusterOption.maxCommandRedirections = 100000;
20
+ client.disconnect();
21
+ this.#client = (0, client_1.createCluster)(redisClusterOption);
22
+ }
23
+ async init(falkordb) {
24
+ await this.#client
25
+ .on('error', err => falkordb.emit('error', err)) // Forward errors
26
+ .connect();
27
+ }
28
+ async query(graph, query, options) {
29
+ return this.#client.falkordb.query(graph, query, options, true);
30
+ }
31
+ async roQuery(graph, query, options) {
32
+ return this.#client.falkordb.roQuery(graph, query, options, true);
33
+ }
34
+ async delete(graph) {
35
+ const reply = this.#client.falkordb.delete(graph);
36
+ return reply.then(() => { });
37
+ }
38
+ async explain(graph, query) {
39
+ return this.#client.falkordb.explain(graph, query);
40
+ }
41
+ async list() {
42
+ return this.#client.falkordb.list();
43
+ }
44
+ async configGet(configKey) {
45
+ return this.#client.falkordb.configGet(configKey);
46
+ }
47
+ async configSet(configKey, value) {
48
+ const reply = this.#client.falkordb.configSet(configKey, value);
49
+ return reply.then(() => { });
50
+ }
51
+ async info(section) {
52
+ return this.#client.falkordb.info(section);
53
+ }
54
+ async copy(srcGraph, destGraph) {
55
+ return this.#client.falkordb.copy(srcGraph, destGraph);
56
+ }
57
+ slowLog(graph) {
58
+ return this.#client.falkordb.slowLog(graph);
59
+ }
60
+ async constraintCreate(graph, constraintType, entityType, label, ...properties) {
61
+ const reply = this.#client.falkordb.constraintCreate(graph, constraintType, entityType, label, ...properties);
62
+ return reply.then(() => { });
63
+ }
64
+ async constraintDrop(graph, constraintType, entityType, label, ...properties) {
65
+ const reply = this.#client.falkordb.constraintDrop(graph, constraintType, entityType, label, ...properties);
66
+ return reply.then(() => { });
67
+ }
68
+ async profile(graph, query) {
69
+ return this.#client.falkordb.profile(graph, query);
70
+ }
71
+ async quit() {
72
+ const reply = this.#client.quit();
73
+ return reply.then(() => { });
74
+ }
75
+ }
76
+ exports.Cluster = Cluster;
@@ -0,0 +1,38 @@
1
+ import { RedisCommandArgument } from "@redis/client/dist/lib/commands";
2
+ import { QueryOptions } from "../commands";
3
+ import FalkorDB from "../falkordb";
4
+ import { ConstraintType, EntityType } from "../graph";
5
+ import { Client } from "./client";
6
+ /**
7
+ * The `NullClient` class is a placeholder implementation of the `Client` interface.
8
+ *
9
+ * This class is designed to be used in scenarios where a client is required, but no actual
10
+ * implementation is available. All methods in this class throw a "Method not implemented."
11
+ * error, indicating that the functionality has not been provided.
12
+ *
13
+ * The `NullClient` can serve as a base class or a stub for future implementations, or as a
14
+ * fallback in cases where a functional client is not needed or cannot be instantiated.
15
+ *
16
+ */
17
+ export declare class NullClient implements Client {
18
+ init(falkordb: FalkorDB): Promise<void>;
19
+ list(): Promise<Array<string>>;
20
+ configGet(configKey: string): Promise<(string | number)[] | (string | number)[][]>;
21
+ configSet(configKey: string, value: number | string): Promise<void>;
22
+ info(section?: string): Promise<(string | string[])[]>;
23
+ query<T>(graph: string, query: RedisCommandArgument, options?: QueryOptions): Promise<any>;
24
+ profile<T>(graph: string, query: RedisCommandArgument): Promise<any>;
25
+ roQuery<T>(graph: string, query: RedisCommandArgument, options?: QueryOptions): Promise<any>;
26
+ copy<T>(srcGraph: string, destGraph: string): Promise<any>;
27
+ delete(graph: string): Promise<void>;
28
+ explain(graph: string, query: string): Promise<any>;
29
+ slowLog(graph: string): Promise<{
30
+ timestamp: Date;
31
+ command: string;
32
+ query: string;
33
+ took: number;
34
+ }[]>;
35
+ constraintCreate(graph: string, constraintType: ConstraintType, entityType: EntityType, label: string, ...properties: string[]): Promise<void>;
36
+ constraintDrop(graph: string, constraintType: ConstraintType, entityType: EntityType, label: string, ...properties: string[]): Promise<void>;
37
+ quit(): Promise<void>;
38
+ }
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NullClient = void 0;
4
+ /**
5
+ * The `NullClient` class is a placeholder implementation of the `Client` interface.
6
+ *
7
+ * This class is designed to be used in scenarios where a client is required, but no actual
8
+ * implementation is available. All methods in this class throw a "Method not implemented."
9
+ * error, indicating that the functionality has not been provided.
10
+ *
11
+ * The `NullClient` can serve as a base class or a stub for future implementations, or as a
12
+ * fallback in cases where a functional client is not needed or cannot be instantiated.
13
+ *
14
+ */
15
+ class NullClient {
16
+ init(falkordb) {
17
+ throw new Error("Method not implemented.");
18
+ }
19
+ list() {
20
+ throw new Error("Method not implemented.");
21
+ }
22
+ configGet(configKey) {
23
+ throw new Error("Method not implemented.");
24
+ }
25
+ configSet(configKey, value) {
26
+ throw new Error("Method not implemented.");
27
+ }
28
+ info(section) {
29
+ throw new Error("Method not implemented.");
30
+ }
31
+ query(graph, query, options) {
32
+ throw new Error("Method not implemented.");
33
+ }
34
+ profile(graph, query) {
35
+ throw new Error("Method not implemented.");
36
+ }
37
+ roQuery(graph, query, options) {
38
+ throw new Error("Method not implemented.");
39
+ }
40
+ copy(srcGraph, destGraph) {
41
+ throw new Error("Method not implemented.");
42
+ }
43
+ delete(graph) {
44
+ throw new Error("Method not implemented.");
45
+ }
46
+ explain(graph, query) {
47
+ throw new Error("Method not implemented.");
48
+ }
49
+ slowLog(graph) {
50
+ throw new Error("Method not implemented.");
51
+ }
52
+ constraintCreate(graph, constraintType, entityType, label, ...properties) {
53
+ throw new Error("Method not implemented.");
54
+ }
55
+ constraintDrop(graph, constraintType, entityType, label, ...properties) {
56
+ throw new Error("Method not implemented.");
57
+ }
58
+ quit() {
59
+ throw new Error("Method not implemented.");
60
+ }
61
+ }
62
+ exports.NullClient = NullClient;
@@ -0,0 +1,10 @@
1
+ import { Single } from "./single";
2
+ import FalkorDB from "../falkordb";
3
+ export declare class Sentinel extends Single {
4
+ init(falkordb: FalkorDB): Promise<void>;
5
+ /**
6
+ * Connect to the server using the details from sentinel server
7
+ * Register error event to reconnect on error from the sentinel server
8
+ */
9
+ private tryConnectSentinelServer;
10
+ }
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Sentinel = void 0;
4
+ const single_1 = require("./single");
5
+ const client_1 = require("@redis/client");
6
+ function extractDetails(masters) {
7
+ const allDetails = [];
8
+ for (const master of masters) {
9
+ const details = {};
10
+ for (let i = 0; i < master.length; i += 2) {
11
+ details[master[i]] = master[i + 1];
12
+ }
13
+ allDetails.push(details);
14
+ }
15
+ return allDetails;
16
+ }
17
+ class Sentinel extends single_1.Single {
18
+ init(falkordb) {
19
+ const redisOption = (this.client.options ?? {});
20
+ return this.tryConnectSentinelServer(this.client, redisOption, falkordb);
21
+ }
22
+ /**
23
+ * Connect to the server using the details from sentinel server
24
+ * Register error event to reconnect on error from the sentinel server
25
+ */
26
+ async tryConnectSentinelServer(client, redisOption, falkordb) {
27
+ // TODO support multi sentinels
28
+ const masters = await client.falkordb.sentinelMasters();
29
+ const details = extractDetails(masters);
30
+ if (details.length > 1) {
31
+ throw new Error('Multiple masters are not supported');
32
+ }
33
+ // Connect to the server with the details from sentinel
34
+ const socketOptions = {
35
+ ...redisOption.socket,
36
+ host: details[0]['ip'],
37
+ port: parseInt(details[0]['port'])
38
+ };
39
+ const serverOptions = {
40
+ ...redisOption,
41
+ socket: socketOptions
42
+ };
43
+ const realClient = (0, client_1.createClient)(serverOptions);
44
+ // Set original client as sentinel and server client as client
45
+ this.client = realClient;
46
+ await realClient
47
+ .on('error', async (err) => {
48
+ console.debug('Error on server connection', err);
49
+ // Disconnect the client to avoid further errors and retries
50
+ realClient.disconnect();
51
+ // If error occurs on previous server connection, no need to reconnect
52
+ if (this.client !== realClient) {
53
+ return;
54
+ }
55
+ try {
56
+ await this.tryConnectSentinelServer(client, redisOption, falkordb);
57
+ console.debug('Connected to server');
58
+ }
59
+ catch (e) {
60
+ console.debug('Error on server reconnect', e);
61
+ // Forward errors if reconnection fails
62
+ falkordb.emit('error', err);
63
+ }
64
+ })
65
+ .connect();
66
+ }
67
+ }
68
+ exports.Sentinel = Sentinel;
@@ -0,0 +1,50 @@
1
+ import { Client } from "./client";
2
+ import { ConstraintType, EntityType } from "../graph";
3
+ import { RedisCommandArgument, RedisFunctions, RedisScripts } from "@redis/client/dist/lib/commands";
4
+ import commands, { QueryOptions } from "../commands";
5
+ import { RedisClientType } from "@redis/client";
6
+ import FalkorDB from "../falkordb";
7
+ export type SingleGraphConnection = RedisClientType<{
8
+ falkordb: typeof commands;
9
+ }, RedisFunctions, RedisScripts>;
10
+ export declare class Single implements Client {
11
+ #private;
12
+ protected client: SingleGraphConnection;
13
+ constructor(client: SingleGraphConnection);
14
+ init(falkordb: FalkorDB): Promise<void>;
15
+ query<T>(graph: string, query: RedisCommandArgument, options?: QueryOptions): Promise<{
16
+ headers: undefined;
17
+ data: undefined;
18
+ metadata: string[];
19
+ } | {
20
+ headers: string[];
21
+ data: (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[];
22
+ metadata: string[];
23
+ }>;
24
+ roQuery<T>(graph: string, query: RedisCommandArgument, options?: QueryOptions): Promise<{
25
+ headers: undefined;
26
+ data: undefined;
27
+ metadata: string[];
28
+ } | {
29
+ headers: string[];
30
+ data: (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | any | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[] | null)[];
31
+ metadata: string[];
32
+ }>;
33
+ delete(graph: string): Promise<void>;
34
+ explain(graph: string, query: string): Promise<string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | any | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined>;
35
+ profile<T>(graph: string, query: string): Promise<string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | any | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined>;
36
+ list(): Promise<string[]>;
37
+ configGet(configKey: string): Promise<(string | number)[] | (string | number)[][]>;
38
+ configSet(configKey: string, value: number | string): Promise<void>;
39
+ info(section?: string): Promise<(string | string[])[]>;
40
+ slowLog(graph: string): Promise<{
41
+ timestamp: Date;
42
+ command: string;
43
+ query: string;
44
+ took: number;
45
+ }[]>;
46
+ constraintCreate(graph: string, constraintType: ConstraintType, entityType: EntityType, label: string, ...properties: string[]): Promise<void>;
47
+ constraintDrop(graph: string, constraintType: ConstraintType, entityType: EntityType, label: string, ...properties: string[]): Promise<void>;
48
+ copy<T>(srcGraph: string, destGraph: string): Promise<"OK">;
49
+ quit(): Promise<void>;
50
+ }
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Single = void 0;
4
+ class Single {
5
+ client;
6
+ #usePool;
7
+ constructor(client) {
8
+ this.client = client;
9
+ this.#usePool = !!(this.client.options?.isolationPoolOptions);
10
+ }
11
+ init(falkordb) {
12
+ return Promise.resolve();
13
+ }
14
+ async query(graph, query, options) {
15
+ const reply = this.#usePool ?
16
+ await this.client.executeIsolated(async (isolatedClient) => {
17
+ return isolatedClient.falkordb.query(graph, query, options, true);
18
+ })
19
+ :
20
+ await this.client.falkordb.query(graph, query, options, true);
21
+ return reply;
22
+ }
23
+ async roQuery(graph, query, options) {
24
+ const reply = this.#usePool ?
25
+ await this.client.executeIsolated(async (isolatedClient) => {
26
+ return isolatedClient.falkordb.roQuery(graph, query, options, true);
27
+ })
28
+ :
29
+ await this.client.falkordb.roQuery(graph, query, options, true);
30
+ return reply;
31
+ }
32
+ async delete(graph) {
33
+ if (this.#usePool) {
34
+ return this.client.executeIsolated(async (isolatedClient) => {
35
+ const reply = isolatedClient.falkordb.delete(graph);
36
+ return reply.then(() => { });
37
+ });
38
+ }
39
+ const reply = this.client.falkordb.delete(graph);
40
+ return reply.then(() => { });
41
+ }
42
+ async explain(graph, query) {
43
+ if (this.#usePool) {
44
+ return this.client.executeIsolated(async (isolatedClient) => {
45
+ return isolatedClient.falkordb.explain(graph, query);
46
+ });
47
+ }
48
+ return this.client.falkordb.explain(graph, query);
49
+ }
50
+ async profile(graph, query) {
51
+ if (this.#usePool) {
52
+ return this.client.executeIsolated(async (isolatedClient) => {
53
+ return isolatedClient.falkordb.profile(graph, query);
54
+ });
55
+ }
56
+ return this.client.falkordb.profile(graph, query);
57
+ }
58
+ async list() {
59
+ return this.client.falkordb.list();
60
+ }
61
+ async configGet(configKey) {
62
+ return this.client.falkordb.configGet(configKey);
63
+ }
64
+ async configSet(configKey, value) {
65
+ const reply = this.client.falkordb.configSet(configKey, value);
66
+ return reply.then(() => { });
67
+ }
68
+ async info(section) {
69
+ return this.client.falkordb.info(section);
70
+ }
71
+ async slowLog(graph) {
72
+ if (this.#usePool) {
73
+ return this.client.executeIsolated(async (isolatedClient) => {
74
+ return isolatedClient.falkordb.slowLog(graph);
75
+ });
76
+ }
77
+ return this.client.falkordb.slowLog(graph);
78
+ }
79
+ async constraintCreate(graph, constraintType, entityType, label, ...properties) {
80
+ const reply = this.client.falkordb.constraintCreate(graph, constraintType, entityType, label, ...properties);
81
+ return reply.then(() => { });
82
+ }
83
+ async constraintDrop(graph, constraintType, entityType, label, ...properties) {
84
+ const reply = this.client.falkordb.constraintDrop(graph, constraintType, entityType, label, ...properties);
85
+ return reply.then(() => { });
86
+ }
87
+ async copy(srcGraph, destGraph) {
88
+ return this.client.falkordb.copy(srcGraph, destGraph);
89
+ }
90
+ async quit() {
91
+ const reply = this.client.quit();
92
+ return reply.then(() => { });
93
+ }
94
+ }
95
+ exports.Single = Single;
@@ -1,2 +1,2 @@
1
- export declare function transformArguments(configKey: string, value: number): Array<string>;
1
+ export declare function transformArguments(configKey: string, value: number | string): Array<string>;
2
2
  export declare function transformReply(): 'OK';
@@ -6,6 +6,6 @@ function transformArguments(configKey, value) {
6
6
  'GRAPH.CONFIG',
7
7
  'SET',
8
8
  configKey,
9
- value.toString()
9
+ typeof value === "string" ? value : value.toString()
10
10
  ];
11
11
  }
@@ -1,11 +1,21 @@
1
1
  import * as tls from 'tls';
2
2
  import * as net from 'net';
3
3
  import { EventEmitter } from 'events';
4
- import Graph, { GraphConnection } from './graph';
4
+ import { RedisClientOptions, RedisFunctions, RedisScripts } from 'redis';
5
+ import Graph from './graph';
6
+ import commands from './commands';
7
+ import { RedisClusterOptions } from '@redis/client';
5
8
  import { Options as PoolOptions } from 'generic-pool';
9
+ import { Client } from './clients/client';
6
10
  type NetSocketOptions = Partial<net.SocketConnectOpts> & {
7
11
  tls?: false;
8
12
  };
13
+ export type TypedRedisClientOptions = RedisClientOptions<{
14
+ falkordb: typeof commands;
15
+ }, RedisFunctions, RedisScripts>;
16
+ export type TypedRedisClusterClientOptions = RedisClusterOptions<{
17
+ falkordb: typeof commands;
18
+ }, RedisFunctions, RedisScripts>;
9
19
  interface TlsSocketOptions extends tls.ConnectionOptions {
10
20
  tls: true;
11
21
  }
@@ -73,18 +83,16 @@ export interface FalkorDBOptions {
73
83
  }
74
84
  export default class FalkorDB extends EventEmitter {
75
85
  #private;
76
- private constructor();
77
- private connectServer;
78
86
  static connect(options?: FalkorDBOptions): Promise<FalkorDB>;
79
87
  selectGraph(graphId: string): Graph;
80
- get connection(): GraphConnection;
88
+ get connection(): Client;
81
89
  list(): Promise<string[]>;
82
90
  configGet(configKey: string): Promise<(string | number)[] | (string | number)[][]>;
83
- configSet(configKey: string, value: number): Promise<"OK">;
91
+ configSet(configKey: string, value: number | string): Promise<void>;
84
92
  info(section?: string): Promise<(string | string[])[]>;
85
93
  /**
86
94
  * Closes the client.
87
95
  */
88
- close(): Promise<string>;
96
+ close(): Promise<void>;
89
97
  }
90
98
  export {};
@@ -4,66 +4,22 @@ const events_1 = require("events");
4
4
  const redis_1 = require("redis");
5
5
  const graph_1 = require("./graph");
6
6
  const commands_1 = require("./commands");
7
- function extractDetails(masters) {
8
- const allDetails = [];
9
- for (const master of masters) {
10
- const details = {};
11
- for (let i = 0; i < master.length; i += 2) {
12
- details[master[i]] = master[i + 1];
13
- }
14
- allDetails.push(details);
7
+ const single_1 = require("./clients/single");
8
+ const sentinel_1 = require("./clients/sentinel");
9
+ const cluster_1 = require("./clients/cluster");
10
+ const nullClient_1 = require("./clients/nullClient");
11
+ async function clientFactory(client) {
12
+ const info = await client.info("server");
13
+ if (info.includes("redis_mode:sentinel")) {
14
+ return new sentinel_1.Sentinel(client);
15
+ }
16
+ else if (info.includes("redis_mode:cluster")) {
17
+ return new cluster_1.Cluster(client);
15
18
  }
16
- return allDetails;
19
+ return new single_1.Single(client);
17
20
  }
18
21
  class FalkorDB extends events_1.EventEmitter {
19
- #client;
20
- #sentinel;
21
- constructor(client) {
22
- super();
23
- this.#client = client;
24
- }
25
- async connectServer(client, redisOption) {
26
- // If not connected to sentinel, throws an error on missing command
27
- const masters = await client.falkordb.sentinelMasters();
28
- const details = extractDetails(masters);
29
- if (details.length > 1) {
30
- throw new Error('Multiple masters are not supported');
31
- }
32
- // Connect to the server with the details from sentinel
33
- const socketOptions = {
34
- ...redisOption.socket,
35
- host: details[0]['ip'],
36
- port: parseInt(details[0]['port'])
37
- };
38
- const serverOptions = {
39
- ...redisOption,
40
- socket: socketOptions
41
- };
42
- const realClient = (0, redis_1.createClient)(serverOptions);
43
- // Set original client as sentinel and server client as client
44
- this.#sentinel = client;
45
- this.#client = realClient;
46
- await realClient
47
- .on('error', async (err) => {
48
- console.debug('Error on server connection', err);
49
- // Disconnect the client to avoid further errors and retries
50
- realClient.disconnect();
51
- // If error occurs on previous server connection, no need to reconnect
52
- if (this.#client !== realClient) {
53
- return;
54
- }
55
- try {
56
- await this.connectServer(client, redisOption);
57
- console.debug('Connected to server');
58
- }
59
- catch (e) {
60
- console.debug('Error on server reconnect', e);
61
- // Forward errors if reconnection fails
62
- this.emit('error', err);
63
- }
64
- })
65
- .connect();
66
- }
22
+ #client = new nullClient_1.NullClient();
67
23
  static async connect(options) {
68
24
  const redisOption = (options ?? {});
69
25
  // If the URL is provided, and the protocol is `falkor` replaces it with `redis` for the underline redis client
@@ -78,17 +34,18 @@ class FalkorDB extends events_1.EventEmitter {
78
34
  redisOption.modules = {
79
35
  falkordb: commands_1.default
80
36
  };
81
- const client = (0, redis_1.createClient)(redisOption);
82
- const falkordb = new FalkorDB(client);
83
- await client
37
+ // Create an empty FalkorDB instance for the redisClient on error event to work
38
+ const falkordb = new FalkorDB();
39
+ // Create initial redis single client
40
+ const redisClient = (0, redis_1.createClient)(redisOption);
41
+ await redisClient
84
42
  .on('error', err => falkordb.emit('error', err)) // Forward errors
85
43
  .connect();
86
- try {
87
- await falkordb.connectServer(client, redisOption);
88
- }
89
- catch (e) {
90
- console.debug('Error in connecting to sentinel, connecting to server directly');
91
- }
44
+ // Create FalkorDB client wrapper
45
+ const client = await clientFactory(redisClient);
46
+ await client.init(falkordb);
47
+ // Set the client to the FalkorDB instance after it was initialized
48
+ falkordb.#client = client;
92
49
  return falkordb;
93
50
  }
94
51
  selectGraph(graphId) {
@@ -98,16 +55,16 @@ class FalkorDB extends events_1.EventEmitter {
98
55
  return this.#client;
99
56
  }
100
57
  async list() {
101
- return this.#client.falkordb.list();
58
+ return this.#client.list();
102
59
  }
103
60
  async configGet(configKey) {
104
- return this.#client.falkordb.configGet(configKey);
61
+ return this.#client.configGet(configKey);
105
62
  }
106
63
  async configSet(configKey, value) {
107
- return this.#client.falkordb.configSet(configKey, value);
64
+ return this.#client.configSet(configKey, value);
108
65
  }
109
66
  async info(section) {
110
- return this.#client.falkordb.info(section);
67
+ return this.#client.info(section);
111
68
  }
112
69
  /**
113
70
  * Closes the client.
@@ -1,31 +1,27 @@
1
1
  import { RedisCommandArgument } from "@redis/client/dist/lib/commands";
2
- import { RedisClientType, RedisFunctions, RedisScripts } from "redis";
3
2
  import { QueryOptions } from "./commands";
4
3
  import { QueryReply } from "./commands/QUERY";
5
- import Commands from "./commands";
6
4
  import { ConstraintType, EntityType } from "./commands/CONSTRAINT_CREATE";
5
+ import { Client } from "./clients/client";
7
6
  export { ConstraintType, EntityType };
8
- type GraphReply<T> = Omit<QueryReply, 'headers' | 'data'> & {
7
+ export type GraphReply<T> = Omit<QueryReply, 'headers' | 'data'> & {
9
8
  data?: Array<T>;
10
9
  };
11
- export type GraphConnection = RedisClientType<{
12
- falkordb: typeof Commands;
13
- }, RedisFunctions, RedisScripts>;
14
10
  export default class Graph {
15
11
  #private;
16
- constructor(client: GraphConnection, name: string);
12
+ constructor(client: Client, name: string);
17
13
  query<T>(query: RedisCommandArgument, options?: QueryOptions): Promise<GraphReply<T>>;
18
14
  roQuery<T>(query: RedisCommandArgument, options?: QueryOptions): Promise<GraphReply<T>>;
19
- delete(): Promise<string>;
20
- explain(query: string): Promise<string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | any | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined>;
21
- profile(query: string): Promise<string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | (string | number | any | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined)[] | null | undefined>;
15
+ delete(): Promise<void>;
16
+ explain(query: string): Promise<any>;
17
+ profile(query: string): Promise<any>;
22
18
  slowLog(): Promise<{
23
19
  timestamp: Date;
24
20
  command: string;
25
21
  query: string;
26
22
  took: number;
27
23
  }[]>;
28
- constraintCreate(constraintType: ConstraintType, entityType: EntityType, label: string, ...properties: string[]): Promise<"PENDING">;
29
- constraintDrop(constraintType: ConstraintType, entityType: EntityType, label: string, ...properties: string[]): Promise<"OK">;
30
- copy(destGraph: string): Promise<"OK">;
24
+ constraintCreate(constraintType: ConstraintType, entityType: EntityType, label: string, ...properties: string[]): Promise<void>;
25
+ constraintDrop(constraintType: ConstraintType, entityType: EntityType, label: string, ...properties: string[]): Promise<void>;
26
+ copy(destGraph: string): Promise<any>;
31
27
  }
package/dist/src/graph.js CHANGED
@@ -20,74 +20,43 @@ var GraphValueTypes;
20
20
  GraphValueTypes[GraphValueTypes["MAP"] = 10] = "MAP";
21
21
  GraphValueTypes[GraphValueTypes["POINT"] = 11] = "POINT";
22
22
  })(GraphValueTypes || (GraphValueTypes = {}));
23
+ // export type GraphConnection = SingleGraphConnection | ClusterGraphConnection;
23
24
  class Graph {
24
25
  #client;
25
26
  #name;
26
27
  #metadata;
27
- #usePool;
28
28
  constructor(client, name) {
29
29
  this.#client = client;
30
30
  this.#name = name;
31
- this.#usePool = !!(this.#client.options?.isolationPoolOptions);
32
31
  }
33
32
  async query(query, options) {
34
- const reply = this.#usePool ?
35
- await this.#client.executeIsolated(async (isolatedClient) => {
36
- return isolatedClient.falkordb.query(this.#name, query, options, true);
37
- })
38
- :
39
- await this.#client.falkordb.query(this.#name, query, options, true);
33
+ const reply = await this.#client.query(this.#name, query, options);
40
34
  return this.#parseReply(reply);
41
35
  }
42
36
  async roQuery(query, options) {
43
- const reply = this.#usePool ?
44
- await this.#client.executeIsolated(async (isolatedClient) => {
45
- return isolatedClient.falkordb.roQuery(this.#name, query, options, true);
46
- })
47
- :
48
- await this.#client.falkordb.roQuery(this.#name, query, options, true);
37
+ const reply = await this.#client.roQuery(this.#name, query, options);
49
38
  return this.#parseReply(reply);
50
39
  }
51
40
  async delete() {
52
- if (this.#usePool) {
53
- return this.#client.executeIsolated(async (isolatedClient) => {
54
- return isolatedClient.falkordb.delete(this.#name);
55
- });
56
- }
57
- return this.#client.falkordb.delete(this.#name);
41
+ return this.#client.delete(this.#name);
58
42
  }
59
43
  async explain(query) {
60
- if (this.#usePool) {
61
- return this.#client.executeIsolated(async (isolatedClient) => {
62
- return isolatedClient.falkordb.explain(this.#name, query);
63
- });
64
- }
65
- return this.#client.falkordb.explain(this.#name, query);
44
+ return this.#client.explain(this.#name, query);
66
45
  }
67
46
  async profile(query) {
68
- if (this.#usePool) {
69
- return this.#client.executeIsolated(async (isolatedClient) => {
70
- return isolatedClient.falkordb.profile(this.#name, query);
71
- });
72
- }
73
- return this.#client.falkordb.profile(this.#name, query);
47
+ return this.#client.profile(this.#name, query);
74
48
  }
75
49
  async slowLog() {
76
- if (this.#usePool) {
77
- return this.#client.executeIsolated(async (isolatedClient) => {
78
- return isolatedClient.falkordb.slowLog(this.#name);
79
- });
80
- }
81
- return this.#client.falkordb.slowLog(this.#name);
50
+ return this.#client.slowLog(this.#name);
82
51
  }
83
52
  async constraintCreate(constraintType, entityType, label, ...properties) {
84
- return this.#client.falkordb.constraintCreate(this.#name, constraintType, entityType, label, ...properties);
53
+ return this.#client.constraintCreate(this.#name, constraintType, entityType, label, ...properties);
85
54
  }
86
55
  async constraintDrop(constraintType, entityType, label, ...properties) {
87
- return this.#client.falkordb.constraintDrop(this.#name, constraintType, entityType, label, ...properties);
56
+ return this.#client.constraintDrop(this.#name, constraintType, entityType, label, ...properties);
88
57
  }
89
58
  async copy(destGraph) {
90
- return this.#client.falkordb.copy(this.#name, destGraph);
59
+ return this.#client.copy(this.#name, destGraph);
91
60
  }
92
61
  #setMetadataPromise;
93
62
  #updateMetadata() {
@@ -98,9 +67,9 @@ class Graph {
98
67
  // DO NOT use directly, use #updateMetadata instead
99
68
  async #setMetadata() {
100
69
  const [labels, relationshipTypes, propertyKeys] = await Promise.all([
101
- this.#client.falkordb.roQuery(this.#name, 'CALL db.labels()'),
102
- this.#client.falkordb.roQuery(this.#name, 'CALL db.relationshipTypes()'),
103
- this.#client.falkordb.roQuery(this.#name, 'CALL db.propertyKeys()')
70
+ this.#client.roQuery(this.#name, 'CALL db.labels()'),
71
+ this.#client.roQuery(this.#name, 'CALL db.relationshipTypes()'),
72
+ this.#client.roQuery(this.#name, 'CALL db.propertyKeys()')
104
73
  ]);
105
74
  this.#metadata = {
106
75
  labels: this.#cleanMetadataArray(labels.data),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "falkordb",
3
- "version": "6.2.0-beta.1",
3
+ "version": "6.2.0",
4
4
  "description": "A FalkorDB javascript library",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.js",
@@ -35,7 +35,8 @@
35
35
  "devDependencies": {
36
36
  "@tsconfig/node14": "^14.1.0",
37
37
  "@types/jest": "^29.5.12",
38
- "@types/node": "^20.11.28",
38
+ "@types/lodash": "^4.17.7",
39
+ "@types/node": "^22.0.0",
39
40
  "@typescript-eslint/eslint-plugin": "^7.16.0",
40
41
  "@typescript-eslint/parser": "^7.18.0",
41
42
  "eslint": "^8.57.0",
@@ -45,7 +46,9 @@
45
46
  },
46
47
  "dependencies": {
47
48
  "@redis/client": "^1.6.0",
49
+ "cluster-key-slot": "1.1.2",
48
50
  "generic-pool": "^3.9.0",
51
+ "lodash": "^4.17.21",
49
52
  "redis": "^4.7.0"
50
53
  }
51
54
  }