Skip to content

Commit 2acb53d

Browse files
authored
Add aliases in connections pane (#631)
1 parent ff40387 commit 2acb53d

14 files changed

Lines changed: 190 additions & 35 deletions

File tree

packages/query-tools/src/neo4jConnection.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ function resolveInitialDatabase(
2121
database?: string,
2222
): string | undefined {
2323
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;
2527
}
2628

2729
const home = databases.find((d) => d.home);

packages/query-tools/src/queries/dataSummary.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ UNION ALL
2121
CALL db.propertyKeys() YIELD propertyKey
2222
RETURN COLLECT(propertyKey)[..${ITEM_LIMIT}] AS result`;
2323

24-
const resultTransformer = resultTransformers.mappedResultTransformer({
24+
const resultTransformer = resultTransformers.mapped({
2525
map(record) {
2626
return record.toObject().result as string[];
2727
},

packages/query-tools/src/queries/databases.ts

Lines changed: 107 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { CypherVersion } from '@neo4j-cypher/language-support';
22
import { resultTransformers } from 'neo4j-driver';
3-
import { ExecuteQueryArgs } from '../types/sdkTypes';
3+
import { ExecuteQueryArgs, DbType } from '../types/sdkTypes';
44

55
export type DatabaseStatus =
66
| 'online'
@@ -26,14 +26,38 @@ export type Database = {
2626
default: boolean;
2727
home: boolean;
2828
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
3030
// "new keys", not sure which version introduced
3131
writer?: boolean;
3232
access?: string;
3333
constituents?: string[];
3434
defaultLanguage?: CypherVersion;
3535
};
3636

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+
3761
/**
3862
* List available databases in your dbms
3963
* https://neo4j.com/docs/cypher-manual/current/administration/databases/#administration-databases-show-databases
@@ -43,7 +67,7 @@ export function listDatabases(): ExecuteQueryArgs<{
4367
}> {
4468
const query = 'SHOW DATABASES YIELD *';
4569

46-
const resultTransformer = resultTransformers.mappedResultTransformer({
70+
const resultTransformer = resultTransformers.mapped({
4771
map(record) {
4872
const obj = record.toObject();
4973
if (obj.defaultLanguage) {
@@ -52,8 +76,21 @@ export function listDatabases(): ExecuteQueryArgs<{
5276
return obj as Database;
5377
},
5478
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+
5592
return {
56-
databases: databases.filter((x) => x?.writer === true),
93+
databases: sortDatabases(uiDatabases),
5794
summary,
5895
};
5996
},
@@ -64,3 +101,69 @@ export function listDatabases(): ExecuteQueryArgs<{
64101
queryConfig: { resultTransformer, routing: 'READ', database: 'system' },
65102
};
66103
}
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+
}

packages/query-tools/src/queries/functions.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export function listFunctions(
7777
${executableByMe ? 'EXECUTABLE BY CURRENT USER' : ''}
7878
YIELD *`;
7979

80-
const resultTransformer = resultTransformers.mappedResultTransformer({
80+
const resultTransformer = resultTransformers.mapped({
8181
map(record) {
8282
const objResult = record.toObject();
8383
validateFunction(objResult);

packages/query-tools/src/queries/graphSchema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export function listGraphSchema(
6565
graphSchema: { from: string; to: string; relType: string }[];
6666
}> {
6767
const query = `CALL db.schema.visualization() YIELD *`;
68-
const resultTransformer = resultTransformers.mappedResultTransformer({
68+
const resultTransformer = resultTransformers.mapped({
6969
map(record) {
7070
const objResult = record.toObject();
7171
validateGraphSchema(objResult);

packages/query-tools/src/queries/procedures.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ export function listProcedures(
9191
${executableByMe ? 'EXECUTABLE BY CURRENT USER' : ''}
9292
YIELD *`;
9393

94-
const resultTransformer = resultTransformers.mappedResultTransformer({
94+
const resultTransformer = resultTransformers.mapped({
9595
map(record) {
9696
// Assign default values (e.g. isDeprecated) in case they are not present
9797
const objResult = record.toObject();

packages/query-tools/src/queries/roles.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export function listRoles(): ExecuteQueryArgs<{
1414
}> {
1515
const query = `SHOW ROLES`;
1616

17-
const resultTransformer = resultTransformers.mappedResultTransformer({
17+
const resultTransformer = resultTransformers.mapped({
1818
map(record) {
1919
return record.toObject() as Neo4jRole;
2020
},

packages/query-tools/src/queries/users.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export function listUsers(): ExecuteQueryArgs<{
1919
}> {
2020
const query = `SHOW USERS`;
2121

22-
const resultTransformer = resultTransformers.mappedResultTransformer({
22+
const resultTransformer = resultTransformers.mapped({
2323
map(record) {
2424
return record.toObject() as Neo4jUser;
2525
},

packages/query-tools/src/queries/version.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export function getVersion(): ExecuteQueryArgs<{
99
}> {
1010
const query = 'CALL dbms.components() YIELD name, versions';
1111

12-
const resultTransformer = resultTransformers.mappedResultTransformer({
12+
const resultTransformer = resultTransformers.mapped({
1313
map(record) {
1414
const obj = record.toObject();
1515
const name = obj.name as string;

packages/query-tools/src/result-transformers/graph-result-transformer.ts

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,13 @@ export type GraphResult = DeduplicatedNodesAndRels & {
4444
*
4545
* @returns ResultTransformer producing {@link GraphResult}
4646
*/
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+
});

0 commit comments

Comments
 (0)