@neo4j-cypher/query-tools 2.0.0-next.28 → 2.0.0-next.30

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 (94) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/cjs/package.json +22 -22
  3. package/dist/cjs/src/cypher-execution/extract-unique-nodes-and-relationships.test.js +3 -1
  4. package/dist/cjs/src/cypher-execution/extract-unique-nodes-and-relationships.test.js.map +1 -1
  5. package/dist/cjs/src/data-transforms/record-to-string.js +2 -3
  6. package/dist/cjs/src/data-transforms/record-to-string.js.map +1 -1
  7. package/dist/cjs/src/metadataPoller.js +8 -1
  8. package/dist/cjs/src/metadataPoller.js.map +1 -1
  9. package/dist/cjs/src/neo4jConnection.js +2 -2
  10. package/dist/cjs/src/neo4jConnection.js.map +1 -1
  11. package/dist/cjs/src/queries/dataSummary.js +1 -1
  12. package/dist/cjs/src/queries/dataSummary.js.map +1 -1
  13. package/dist/cjs/src/queries/databases.d.ts +12 -1
  14. package/dist/cjs/src/queries/databases.js +85 -2
  15. package/dist/cjs/src/queries/databases.js.map +1 -1
  16. package/dist/cjs/src/queries/functions.js +1 -1
  17. package/dist/cjs/src/queries/functions.js.map +1 -1
  18. package/dist/cjs/src/queries/graphSchema.js +1 -1
  19. package/dist/cjs/src/queries/graphSchema.js.map +1 -1
  20. package/dist/cjs/src/queries/procedures.js +1 -1
  21. package/dist/cjs/src/queries/procedures.js.map +1 -1
  22. package/dist/cjs/src/queries/roles.js +1 -1
  23. package/dist/cjs/src/queries/roles.js.map +1 -1
  24. package/dist/cjs/src/queries/users.js +1 -1
  25. package/dist/cjs/src/queries/users.js.map +1 -1
  26. package/dist/cjs/src/queries/version.js +1 -1
  27. package/dist/cjs/src/queries/version.js.map +1 -1
  28. package/dist/cjs/src/result-transformers/graph-result-transformer.js +1 -1
  29. package/dist/cjs/src/result-transformers/graph-result-transformer.js.map +1 -1
  30. package/dist/cjs/src/schemaPoller.js +5 -2
  31. package/dist/cjs/src/schemaPoller.js.map +1 -1
  32. package/dist/cjs/src/types/sdkTypes.d.ts +7 -1
  33. package/dist/cjs/src/types/sdkTypes.js +8 -0
  34. package/dist/cjs/src/types/sdkTypes.js.map +1 -1
  35. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  36. package/dist/cjs/vitest.config.mjs +2 -1
  37. package/dist/cjs/vitest.config.mjs.map +1 -1
  38. package/dist/esm/package.json +22 -22
  39. package/dist/esm/src/cypher-execution/extract-unique-nodes-and-relationships.test.js +3 -1
  40. package/dist/esm/src/cypher-execution/extract-unique-nodes-and-relationships.test.js.map +1 -1
  41. package/dist/esm/src/data-transforms/record-to-string.js +2 -3
  42. package/dist/esm/src/data-transforms/record-to-string.js.map +1 -1
  43. package/dist/esm/src/metadataPoller.js +8 -1
  44. package/dist/esm/src/metadataPoller.js.map +1 -1
  45. package/dist/esm/src/neo4jConnection.js +2 -2
  46. package/dist/esm/src/neo4jConnection.js.map +1 -1
  47. package/dist/esm/src/queries/dataSummary.js +1 -1
  48. package/dist/esm/src/queries/dataSummary.js.map +1 -1
  49. package/dist/esm/src/queries/databases.d.ts +12 -1
  50. package/dist/esm/src/queries/databases.js +83 -2
  51. package/dist/esm/src/queries/databases.js.map +1 -1
  52. package/dist/esm/src/queries/functions.js +1 -1
  53. package/dist/esm/src/queries/functions.js.map +1 -1
  54. package/dist/esm/src/queries/graphSchema.js +1 -1
  55. package/dist/esm/src/queries/graphSchema.js.map +1 -1
  56. package/dist/esm/src/queries/procedures.js +1 -1
  57. package/dist/esm/src/queries/procedures.js.map +1 -1
  58. package/dist/esm/src/queries/roles.js +1 -1
  59. package/dist/esm/src/queries/roles.js.map +1 -1
  60. package/dist/esm/src/queries/users.js +1 -1
  61. package/dist/esm/src/queries/users.js.map +1 -1
  62. package/dist/esm/src/queries/version.js +1 -1
  63. package/dist/esm/src/queries/version.js.map +1 -1
  64. package/dist/esm/src/result-transformers/graph-result-transformer.js +1 -1
  65. package/dist/esm/src/result-transformers/graph-result-transformer.js.map +1 -1
  66. package/dist/esm/src/schemaPoller.js +5 -2
  67. package/dist/esm/src/schemaPoller.js.map +1 -1
  68. package/dist/esm/src/types/sdkTypes.d.ts +7 -1
  69. package/dist/esm/src/types/sdkTypes.js +7 -1
  70. package/dist/esm/src/types/sdkTypes.js.map +1 -1
  71. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  72. package/dist/esm/vitest.config.mjs +2 -1
  73. package/dist/esm/vitest.config.mjs.map +1 -1
  74. package/dist/types/src/queries/databases.d.ts +12 -1
  75. package/dist/types/src/types/sdkTypes.d.ts +7 -1
  76. package/dist/types/tsconfig.tsbuildinfo +1 -1
  77. package/package.json +14 -14
  78. package/src/cypher-execution/extract-unique-nodes-and-relationships.test.ts +3 -1
  79. package/src/data-transforms/record-to-string.ts +2 -3
  80. package/src/metadataPoller.ts +9 -1
  81. package/src/neo4jConnection.ts +4 -2
  82. package/src/queries/dataSummary.ts +1 -1
  83. package/src/queries/databases.ts +107 -4
  84. package/src/queries/functions.ts +1 -1
  85. package/src/queries/graphSchema.ts +1 -1
  86. package/src/queries/procedures.ts +1 -1
  87. package/src/queries/roles.ts +1 -1
  88. package/src/queries/users.ts +1 -1
  89. package/src/queries/version.ts +1 -1
  90. package/src/result-transformers/graph-result-transformer.ts +10 -11
  91. package/src/schemaPoller.ts +6 -5
  92. package/src/types/cypher-data-types.ts +1 -1
  93. package/src/types/sdkTypes.ts +7 -1
  94. package/vitest.config.mts +2 -1
package/package.json CHANGED
@@ -1,9 +1,16 @@
1
1
  {
2
2
  "name": "@neo4j-cypher/query-tools",
3
+ "version": "2.0.0-next.30",
3
4
  "description": "",
4
- "author": "Neo4j Inc.",
5
+ "bugs": {
6
+ "url": "https://github.com/neo4j/cypher-language-support/issues"
7
+ },
5
8
  "license": "Apache-2.0",
6
- "version": "2.0.0-next.28",
9
+ "author": "Neo4j Inc.",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git://github.com/neo4j/cypher-language-support.git"
13
+ },
7
14
  "main": "./dist/cjs/src/index.js",
8
15
  "module": "./dist/esm/src/index.js",
9
16
  "types": "./dist/types/src/index.d.ts",
@@ -15,25 +22,18 @@
15
22
  "default": "./dist/cjs/src/index.js"
16
23
  }
17
24
  },
18
- "repository": {
19
- "type": "git",
20
- "url": "git://github.com/neo4j/cypher-language-support.git"
21
- },
22
- "bugs": {
23
- "url": "https://github.com/neo4j/cypher-language-support/issues"
24
- },
25
- "engineStrict": true,
26
- "engines": {
27
- "node": ">=24.11.1"
28
- },
29
25
  "dependencies": {
30
26
  "ajv": "^8.12.0",
31
27
  "neo4j-driver": "6.0.1",
32
- "@neo4j-cypher/language-support": "2.0.0-next.28"
28
+ "@neo4j-cypher/language-support": "2.0.0-next.30"
33
29
  },
34
30
  "devDependencies": {
35
31
  "@testcontainers/neo4j": "^11.5.1"
36
32
  },
33
+ "engines": {
34
+ "node": ">=24.11.1"
35
+ },
36
+ "engineStrict": true,
37
37
  "scripts": {
38
38
  "build": "concurrently 'npm:build-esm' 'npm:build-commonjs' 'npm:build-types'",
39
39
  "build-esm": "tsc --module esnext --outDir dist/esm",
@@ -156,7 +156,9 @@ describe('extractNodesAndRels', () => {
156
156
 
157
157
  const { nodes, relationships, limitHit } = extractUniqueNodesAndRels(
158
158
  [boltRecord],
159
- { nodeLimit: 1 },
159
+ {
160
+ nodeLimit: 1,
161
+ },
160
162
  );
161
163
  expect(limitHit).toBe(true);
162
164
  expect(nodes.length).toBe(1);
@@ -34,9 +34,7 @@ export function propertyToString(
34
34
  localeFormattedDates = false,
35
35
  ): string {
36
36
  if (Array.isArray(property)) {
37
- return `[${property
38
- .map((p) => propertyToString(p, quoteStrings))
39
- .join(', ')}]`;
37
+ return `[${property.map((p) => propertyToString(p, quoteStrings)).join(', ')}]`;
40
38
  }
41
39
 
42
40
  if (property === null) {
@@ -72,6 +70,7 @@ export function propertyToString(
72
70
 
73
71
  if (typeof property === 'string') {
74
72
  if (quoteStrings) {
73
+ // oxlint-disable-next-line typescript-eslint/restrict-template-expressions
75
74
  return `"${property}"`;
76
75
  }
77
76
  return property;
@@ -250,10 +250,18 @@ export class ConnectedMetadataPoller extends MetadataPoller {
250
250
  await Promise.allSettled([
251
251
  this.databases.refetch(),
252
252
  this.dataSummary.refetch(),
253
- this.graphSchema.refetch(),
254
253
  ...Object.values(this.procedures).map((version) => version?.refetch()),
255
254
  ...Object.values(this.functions).map((version) => version?.refetch()),
256
255
  ]);
256
+ if (
257
+ this.dbSchema.labels &&
258
+ this.dbSchema.relationshipTypes &&
259
+ this.dbSchema.labels.length + this.dbSchema.relationshipTypes.length < 200
260
+ ) {
261
+ await this.graphSchema.refetch();
262
+ } else {
263
+ this.dbSchema.graphSchema = undefined;
264
+ }
257
265
  this.events.emit('schemaFetched');
258
266
  }
259
267
 
@@ -21,7 +21,9 @@ function resolveInitialDatabase(
21
21
  database?: string,
22
22
  ): string | undefined {
23
23
  if (database) {
24
- return databases.find((d) => d.name === database)?.name;
24
+ return databases.find(
25
+ (d) => d.name === database || d?.aliases?.includes(database),
26
+ )?.name;
25
27
  }
26
28
 
27
29
  const home = databases.find((d) => d.home);
@@ -165,7 +167,7 @@ export class Neo4jConnection {
165
167
  const result = tx.run(query, {});
166
168
  return await queryConfig.resultTransformer(result);
167
169
  },
168
- { metadata: { ...METADATA_BASE, type: queryType } },
170
+ { timeout: 500, metadata: { ...METADATA_BASE, type: queryType } },
169
171
  );
170
172
  } finally {
171
173
  await session.close();
@@ -21,7 +21,7 @@ UNION ALL
21
21
  CALL db.propertyKeys() YIELD propertyKey
22
22
  RETURN COLLECT(propertyKey)[..${ITEM_LIMIT}] AS result`;
23
23
 
24
- const resultTransformer = resultTransformers.mappedResultTransformer({
24
+ const resultTransformer = resultTransformers.mapped({
25
25
  map(record) {
26
26
  return record.toObject().result as string[];
27
27
  },
@@ -1,6 +1,6 @@
1
1
  import type { CypherVersion } from '@neo4j-cypher/language-support';
2
2
  import { resultTransformers } from 'neo4j-driver';
3
- import { ExecuteQueryArgs } from '../types/sdkTypes';
3
+ import { ExecuteQueryArgs, DbType } from '../types/sdkTypes';
4
4
 
5
5
  export type DatabaseStatus =
6
6
  | 'online'
@@ -26,7 +26,7 @@ export type Database = {
26
26
  default: boolean;
27
27
  home: boolean;
28
28
  aliases?: string[]; // introduced in neo4j 4.4
29
- type?: 'system' | 'composite' | 'standard'; // introduced in neo4j 5
29
+ type?: 'system' | 'composite' | 'standard' | 'graph shard' | 'property shard'; // introduced in neo4j 5
30
30
  // "new keys", not sure which version introduced
31
31
  writer?: boolean;
32
32
  access?: string;
@@ -34,6 +34,30 @@ export type Database = {
34
34
  defaultLanguage?: CypherVersion;
35
35
  };
36
36
 
37
+ const findComposite = (databases: Database[], alias: string) => {
38
+ const compositeDatabases = databases.filter((db) => db.type === 'composite');
39
+ return compositeDatabases.find((db) => db.constituents?.includes(alias));
40
+ };
41
+
42
+ const isCompositeAlias = (databases: Database[], alias: string) =>
43
+ findComposite(databases, alias) !== undefined;
44
+
45
+ const removeCompositeAliases = (databases: Database[]): Database[] => {
46
+ return databases.map(({ aliases, ...db }) => ({
47
+ ...db,
48
+ aliases: aliases?.filter((alias) => !isCompositeAlias(databases, alias)),
49
+ }));
50
+ };
51
+
52
+ const removeSPDShards = (databases: Database[]): Database[] => {
53
+ return databases.filter((db) => {
54
+ return !(
55
+ db.type !== undefined &&
56
+ (db.type === DbType.GRAPH_SHARD || db.type === DbType.PROPERTY_SHARD)
57
+ );
58
+ });
59
+ };
60
+
37
61
  /**
38
62
  * List available databases in your dbms
39
63
  * https://neo4j.com/docs/cypher-manual/current/administration/databases/#administration-databases-show-databases
@@ -43,7 +67,7 @@ export function listDatabases(): ExecuteQueryArgs<{
43
67
  }> {
44
68
  const query = 'SHOW DATABASES YIELD *';
45
69
 
46
- const resultTransformer = resultTransformers.mappedResultTransformer({
70
+ const resultTransformer = resultTransformers.mapped({
47
71
  map(record) {
48
72
  const obj = record.toObject();
49
73
  if (obj.defaultLanguage) {
@@ -52,8 +76,21 @@ export function listDatabases(): ExecuteQueryArgs<{
52
76
  return obj as Database;
53
77
  },
54
78
  collect(databases, summary) {
79
+ const instancesRecord: Record<string, Database> = databases.reduce<
80
+ Record<string, Database>
81
+ >((acc, db) => {
82
+ const instanceId: string = `${db.name}@${db.address}`;
83
+ acc[instanceId] = db;
84
+ return acc;
85
+ }, {});
86
+ const logicalDatabases: Record<string, Database> =
87
+ getLogicalDatabases(instancesRecord);
88
+ const uiDatabases = removeSPDShards(
89
+ removeCompositeAliases(Object.values(logicalDatabases)),
90
+ );
91
+
55
92
  return {
56
- databases: databases.filter((x) => x?.writer === true),
93
+ databases: sortDatabases(uiDatabases),
57
94
  summary,
58
95
  };
59
96
  },
@@ -64,3 +101,69 @@ export function listDatabases(): ExecuteQueryArgs<{
64
101
  queryConfig: { resultTransformer, routing: 'READ', database: 'system' },
65
102
  };
66
103
  }
104
+
105
+ /**
106
+ * In clustered environments a single logical database can be backed by multiple physical database instances
107
+ * running on a different servers.
108
+ * This function returns a record of logical databases.
109
+ * A database instance is uniquely identified by its name and address.
110
+ * A logical database is uniquely identified by its name.
111
+ * @input instances - a record of database instances
112
+ * @output a record of logical databases
113
+ */
114
+ export function getLogicalDatabases(
115
+ instances: Record<string, Database>,
116
+ ): Record<string, Database> {
117
+ // Two databases with the same name but different properties will be merged into one
118
+ // merging rules:
119
+ // - if writer (leader) is present, use it, otherwise use the first one
120
+
121
+ // Merge db2 into db1
122
+ const mergeDatabases = (db1: Database, db2: Database): Database => {
123
+ let mergedDatabase = { ...db1 };
124
+ if (db2.role === 'leader' || db2.writer === true) {
125
+ mergedDatabase = { ...db2 };
126
+ }
127
+
128
+ return mergedDatabase;
129
+ };
130
+
131
+ return Object.values(instances).reduce<Record<string, Database>>(
132
+ (databases, instance) => {
133
+ const key = instance.name;
134
+ const existingDatabase = databases[key];
135
+ const mergedDatabase = existingDatabase
136
+ ? mergeDatabases(existingDatabase, instance)
137
+ : instance;
138
+
139
+ return {
140
+ ...databases,
141
+ [key]: mergedDatabase,
142
+ };
143
+ },
144
+ {},
145
+ );
146
+ }
147
+
148
+ export function sortDatabases(databases: Database[]) {
149
+ function databaseComparator(a: Database, b: Database) {
150
+ // disable eslint to make code more readable
151
+ /* eslint-disable curly */
152
+ // home is greater than default
153
+ if (a.default && b.home) return 1;
154
+
155
+ // otherwiser default is greater than anything else
156
+ if (a.default) return -1;
157
+ if (b.default) return 1;
158
+
159
+ // system is less than anything else
160
+ if (a.name === 'system') return 1;
161
+ if (b.name === 'system') return -1;
162
+ /* eslint-enable curly */
163
+
164
+ // else sort alphabetically
165
+ return a.name.localeCompare(b.name);
166
+ }
167
+
168
+ return databases.sort(databaseComparator);
169
+ }
@@ -77,7 +77,7 @@ export function listFunctions(
77
77
  ${executableByMe ? 'EXECUTABLE BY CURRENT USER' : ''}
78
78
  YIELD *`;
79
79
 
80
- const resultTransformer = resultTransformers.mappedResultTransformer({
80
+ const resultTransformer = resultTransformers.mapped({
81
81
  map(record) {
82
82
  const objResult = record.toObject();
83
83
  validateFunction(objResult);
@@ -65,7 +65,7 @@ export function listGraphSchema(
65
65
  graphSchema: { from: string; to: string; relType: string }[];
66
66
  }> {
67
67
  const query = `CALL db.schema.visualization() YIELD *`;
68
- const resultTransformer = resultTransformers.mappedResultTransformer({
68
+ const resultTransformer = resultTransformers.mapped({
69
69
  map(record) {
70
70
  const objResult = record.toObject();
71
71
  validateGraphSchema(objResult);
@@ -91,7 +91,7 @@ export function listProcedures(
91
91
  ${executableByMe ? 'EXECUTABLE BY CURRENT USER' : ''}
92
92
  YIELD *`;
93
93
 
94
- const resultTransformer = resultTransformers.mappedResultTransformer({
94
+ const resultTransformer = resultTransformers.mapped({
95
95
  map(record) {
96
96
  // Assign default values (e.g. isDeprecated) in case they are not present
97
97
  const objResult = record.toObject();
@@ -14,7 +14,7 @@ export function listRoles(): ExecuteQueryArgs<{
14
14
  }> {
15
15
  const query = `SHOW ROLES`;
16
16
 
17
- const resultTransformer = resultTransformers.mappedResultTransformer({
17
+ const resultTransformer = resultTransformers.mapped({
18
18
  map(record) {
19
19
  return record.toObject() as Neo4jRole;
20
20
  },
@@ -19,7 +19,7 @@ export function listUsers(): ExecuteQueryArgs<{
19
19
  }> {
20
20
  const query = `SHOW USERS`;
21
21
 
22
- const resultTransformer = resultTransformers.mappedResultTransformer({
22
+ const resultTransformer = resultTransformers.mapped({
23
23
  map(record) {
24
24
  return record.toObject() as Neo4jUser;
25
25
  },
@@ -9,7 +9,7 @@ export function getVersion(): ExecuteQueryArgs<{
9
9
  }> {
10
10
  const query = 'CALL dbms.components() YIELD name, versions';
11
11
 
12
- const resultTransformer = resultTransformers.mappedResultTransformer({
12
+ const resultTransformer = resultTransformers.mapped({
13
13
  map(record) {
14
14
  const obj = record.toObject();
15
15
  const name = obj.name as string;
@@ -44,14 +44,13 @@ export type GraphResult = DeduplicatedNodesAndRels & {
44
44
  *
45
45
  * @returns ResultTransformer producing {@link GraphResult}
46
46
  */
47
- export const graphResultTransformer =
48
- resultTransformers.mappedResultTransformer({
49
- map(record) {
50
- return record;
51
- },
52
- collect(records, summary): GraphResult {
53
- const { nodes, relationships, limitHit } =
54
- extractUniqueNodesAndRels(records);
55
- return { nodes, relationships, limitHit, records, summary };
56
- },
57
- });
47
+ export const graphResultTransformer = resultTransformers.mapped({
48
+ map(record) {
49
+ return record;
50
+ },
51
+ collect(records, summary): GraphResult {
52
+ const { nodes, relationships, limitHit } =
53
+ extractUniqueNodesAndRels(records);
54
+ return { nodes, relationships, limitHit, records, summary };
55
+ },
56
+ });
@@ -168,7 +168,7 @@ export class Neo4jSchemaPoller {
168
168
  );
169
169
  return ['5', '25'];
170
170
  }
171
- } catch (e) {
171
+ } catch {
172
172
  return ['5'];
173
173
  }
174
174
 
@@ -247,7 +247,10 @@ export class Neo4jSchemaPoller {
247
247
  const driver = neo4j.driver(
248
248
  url,
249
249
  neo4j.auth.basic(credentials.username, credentials.password),
250
- { userAgent: config.appName, ...config.driverConfig },
250
+ {
251
+ userAgent: config.appName,
252
+ ...config.driverConfig,
253
+ },
251
254
  );
252
255
 
253
256
  await driver.verifyConnectivity({ database: database });
@@ -323,9 +326,7 @@ export class Neo4jSchemaPoller {
323
326
  retriable: boolean,
324
327
  ): ConnectionError {
325
328
  const friendlyMessage = retriable
326
- ? `${connectionError.friendlyMessage}. Retrying in ${
327
- RETRY_INTERVAL_MS / 1000
328
- } seconds`
329
+ ? `${connectionError.friendlyMessage}. Retrying in ${RETRY_INTERVAL_MS / 1000} seconds`
329
330
  : connectionError.friendlyMessage;
330
331
 
331
332
  return {
@@ -25,7 +25,7 @@ import {
25
25
  } from 'neo4j-driver';
26
26
  type NumberOrInteger = number | Integer | bigint;
27
27
 
28
- /**
28
+ /**
29
29
  The neo4j driver type mapping - https://neo4j.com/docs/javascript-manual/current/cypher-workflow/#js-driver-type-mapping
30
30
 
31
31
  Star denotes custom driver class.
@@ -1,6 +1,12 @@
1
1
  import type { QueryConfig, ResultSummary } from 'neo4j-driver';
2
2
 
3
- export type DbType = 'system' | 'standard' | 'composite';
3
+ export const DbType = {
4
+ SYSTEM: 'system',
5
+ STANDARD: 'standard',
6
+ COMPOSITE: 'composite',
7
+ GRAPH_SHARD: 'graph shard',
8
+ PROPERTY_SHARD: 'property shard',
9
+ };
4
10
 
5
11
  export type QueryType =
6
12
  // Query automatically run by the app.
package/vitest.config.mts CHANGED
@@ -3,6 +3,7 @@ import { defineConfig } from 'vitest/config';
3
3
  export default defineConfig({
4
4
  test: {
5
5
  globals: true,
6
- hookTimeout: 90000
6
+ hookTimeout: 90000,
7
+ dir: './src',
7
8
  },
8
9
  });