cruddl 6.0.0-alpha.1 → 6.0.0-alpha.2
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/dist/cjs/arangodb/aql-generator.d.ts +5 -0
- package/dist/cjs/arangodb/aql-generator.js +29 -16
- package/dist/cjs/arangodb/arango-basics.d.ts +6 -2
- package/dist/cjs/arangodb/arango-basics.js +6 -4
- package/dist/cjs/arangodb/arangodb-adapter.js +5 -0
- package/dist/cjs/arangodb/config.d.ts +12 -0
- package/dist/cjs/arangodb/schema-migration/analyzer.js +23 -9
- package/dist/cjs/arangodb/schema-migration/arango-search-helpers.d.ts +6 -2
- package/dist/cjs/arangodb/schema-migration/arango-search-helpers.js +9 -7
- package/dist/cjs/arangodb/schema-migration/index-helpers.d.ts +3 -1
- package/dist/cjs/arangodb/schema-migration/index-helpers.js +4 -4
- package/dist/cjs/core/version.js +1 -1
- package/dist/esm/arangodb/aql-generator.d.ts +5 -0
- package/dist/esm/arangodb/aql-generator.js +29 -16
- package/dist/esm/arangodb/arango-basics.d.ts +6 -2
- package/dist/esm/arangodb/arango-basics.js +6 -4
- package/dist/esm/arangodb/arangodb-adapter.js +5 -0
- package/dist/esm/arangodb/config.d.ts +12 -0
- package/dist/esm/arangodb/schema-migration/analyzer.js +23 -9
- package/dist/esm/arangodb/schema-migration/arango-search-helpers.d.ts +6 -2
- package/dist/esm/arangodb/schema-migration/arango-search-helpers.js +9 -7
- package/dist/esm/arangodb/schema-migration/index-helpers.d.ts +3 -1
- package/dist/esm/arangodb/schema-migration/index-helpers.js +4 -4
- package/dist/esm/core/version.js +1 -1
- package/package.json +1 -1
|
@@ -19,6 +19,11 @@ export interface QueryGenerationOptions {
|
|
|
19
19
|
* See {@link ExecutionOptions.maxProjections} for details.
|
|
20
20
|
*/
|
|
21
21
|
readonly maxProjections?: number;
|
|
22
|
+
/**
|
|
23
|
+
* An optional prefix prepended to all collection and view names in generated AQL queries.
|
|
24
|
+
* See {@link ArangoDBConfig.collectionNamePrefix} for details.
|
|
25
|
+
*/
|
|
26
|
+
readonly collectionNamePrefix?: string;
|
|
22
27
|
}
|
|
23
28
|
export declare function getAQLQuery(node: QueryNode, options?: Partial<QueryGenerationOptions>): AQLCompoundQuery;
|
|
24
29
|
export declare function generateTokenizationQuery(tokensFiltered: ReadonlyArray<FlexSearchTokenizable>): AQLFragment;
|
|
@@ -381,7 +381,9 @@ register(flex_search_js_2.FlexSearchQueryNode, (node, context) => {
|
|
|
381
381
|
let itemContext = context
|
|
382
382
|
.bindVariable(node.itemVariable)
|
|
383
383
|
.withExtension(inFlexSearchFilterSymbol, true);
|
|
384
|
-
const viewName = (0, arango_search_helpers_js_1.getFlexSearchViewNameForRootEntity)(node.rootEntityType
|
|
384
|
+
const viewName = (0, arango_search_helpers_js_1.getFlexSearchViewNameForRootEntity)(node.rootEntityType, {
|
|
385
|
+
prefix: context.options.collectionNamePrefix,
|
|
386
|
+
});
|
|
385
387
|
context.addCollectionAccess(viewName, AccessType.EXPLICIT_READ);
|
|
386
388
|
return aqlExt.subquery((0, aql_js_1.aql) `FOR ${itemContext.getVariable(node.itemVariable)}`, (0, aql_js_1.aql) `IN ${aql_js_1.aql.collection(viewName)}`, (0, aql_js_1.aql) `SEARCH ${processNode(node.flexFilterNode, itemContext)}`, node.isOptimisationsDisabled ? (0, aql_js_1.aql) `OPTIONS { conditionOptimization: 'none' }` : (0, aql_js_1.aql) ``, (0, aql_js_1.aql) `RETURN ${itemContext.getVariable(node.itemVariable)}`);
|
|
387
389
|
});
|
|
@@ -1837,10 +1839,10 @@ function getRelationTraversalForStatements({ node, innermostItemVar, context, pr
|
|
|
1837
1839
|
let sourceFrag;
|
|
1838
1840
|
if (node.entitiesIdentifierKind === mutations_js_1.EntitiesIdentifierKind.ID) {
|
|
1839
1841
|
if (node.sourceIsList) {
|
|
1840
|
-
sourceFrag = getFullIDFromKeysFragment(plainSourceFrag, node.relationSegments[0].relationSide.sourceType);
|
|
1842
|
+
sourceFrag = getFullIDFromKeysFragment(plainSourceFrag, node.relationSegments[0].relationSide.sourceType, context);
|
|
1841
1843
|
}
|
|
1842
1844
|
else {
|
|
1843
|
-
sourceFrag = getFullIDFromKeyFragment(plainSourceFrag, node.relationSegments[0].relationSide.sourceType);
|
|
1845
|
+
sourceFrag = getFullIDFromKeyFragment(plainSourceFrag, node.relationSegments[0].relationSide.sourceType, context);
|
|
1844
1846
|
}
|
|
1845
1847
|
}
|
|
1846
1848
|
else {
|
|
@@ -1930,7 +1932,9 @@ function getRelationTraversalForStatements({ node, innermostItemVar, context, pr
|
|
|
1930
1932
|
forFragments.push(traversalFrag);
|
|
1931
1933
|
currentObjectFrag = nodeVar;
|
|
1932
1934
|
}
|
|
1933
|
-
context.addCollectionAccess((0, arango_basics_js_1.getCollectionNameForRootEntity)(segment.relationSide.targetType
|
|
1935
|
+
context.addCollectionAccess((0, arango_basics_js_1.getCollectionNameForRootEntity)(segment.relationSide.targetType, {
|
|
1936
|
+
prefix: context.options.collectionNamePrefix,
|
|
1937
|
+
}), AccessType.IMPLICIT_READ);
|
|
1934
1938
|
segmentIndex++;
|
|
1935
1939
|
}
|
|
1936
1940
|
// each FOR automatically removes NULLs of the previous step (just how FOR works)
|
|
@@ -2125,14 +2129,14 @@ function getFullIDFromKeyNode(node, rootEntityType, context) {
|
|
|
2125
2129
|
// special handling to avoid concat if possible - do not alter the behavior
|
|
2126
2130
|
if (node instanceof literals_js_1.LiteralQueryNode && typeof node.value == 'string') {
|
|
2127
2131
|
// just append the node to the literal key in JavaScript and bind it as a string
|
|
2128
|
-
return (0, aql_js_1.aql) `${(0, arango_basics_js_1.getCollectionNameForRootEntity)(rootEntityType) + '/' + node.value}`;
|
|
2132
|
+
return (0, aql_js_1.aql) `${(0, arango_basics_js_1.getCollectionNameForRootEntity)(rootEntityType, { prefix: context.options.collectionNamePrefix }) + '/' + node.value}`;
|
|
2129
2133
|
}
|
|
2130
2134
|
if (node instanceof queries_js_1.RootEntityIDQueryNode) {
|
|
2131
2135
|
// access the _id field. processNode(node) would access the _key field instead.
|
|
2132
2136
|
return (0, aql_js_1.aql) `${processNode(node.objectNode, context)}._id`;
|
|
2133
2137
|
}
|
|
2134
2138
|
// fall back to general case
|
|
2135
|
-
return getFullIDFromKeyFragment(processNode(node, context), rootEntityType);
|
|
2139
|
+
return getFullIDFromKeyFragment(processNode(node, context), rootEntityType, context);
|
|
2136
2140
|
}
|
|
2137
2141
|
function getFullIDsFromKeysNode(idsNode, rootEntityType, context) {
|
|
2138
2142
|
if (idsNode instanceof lists_js_1.ListQueryNode) {
|
|
@@ -2143,18 +2147,20 @@ function getFullIDsFromKeysNode(idsNode, rootEntityType, context) {
|
|
|
2143
2147
|
if (idsNode instanceof literals_js_1.LiteralQueryNode &&
|
|
2144
2148
|
(0, utils_js_1.isReadonlyArray)(idsNode.value) &&
|
|
2145
2149
|
idsNode.value.every((v) => typeof v === 'string')) {
|
|
2146
|
-
const collName = (0, arango_basics_js_1.getCollectionNameForRootEntity)(rootEntityType
|
|
2150
|
+
const collName = (0, arango_basics_js_1.getCollectionNameForRootEntity)(rootEntityType, {
|
|
2151
|
+
prefix: context.options.collectionNamePrefix,
|
|
2152
|
+
});
|
|
2147
2153
|
const ids = idsNode.value.map((val) => collName + '/' + val);
|
|
2148
2154
|
return aql_js_1.aql.value(ids);
|
|
2149
2155
|
}
|
|
2150
|
-
return getFullIDFromKeysFragment(processNode(idsNode, context), rootEntityType);
|
|
2156
|
+
return getFullIDFromKeysFragment(processNode(idsNode, context), rootEntityType, context);
|
|
2151
2157
|
}
|
|
2152
|
-
function getFullIDFromKeyFragment(keyFragment, rootEntityType) {
|
|
2153
|
-
return (0, aql_js_1.aql) `CONCAT(${(0, arango_basics_js_1.getCollectionNameForRootEntity)(rootEntityType) + '/'}, ${keyFragment})`;
|
|
2158
|
+
function getFullIDFromKeyFragment(keyFragment, rootEntityType, context) {
|
|
2159
|
+
return (0, aql_js_1.aql) `CONCAT(${(0, arango_basics_js_1.getCollectionNameForRootEntity)(rootEntityType, { prefix: context.options.collectionNamePrefix }) + '/'}, ${keyFragment})`;
|
|
2154
2160
|
}
|
|
2155
|
-
function getFullIDFromKeysFragment(keysFragment, rootEntityType) {
|
|
2161
|
+
function getFullIDFromKeysFragment(keysFragment, rootEntityType, context) {
|
|
2156
2162
|
const idVar = aql_js_1.aql.variable('id');
|
|
2157
|
-
return (0, aql_js_1.aql) `(FOR ${idVar} IN ${keysFragment} RETURN ${getFullIDFromKeyFragment(idVar, rootEntityType)})`;
|
|
2163
|
+
return (0, aql_js_1.aql) `(FOR ${idVar} IN ${keysFragment} RETURN ${getFullIDFromKeyFragment(idVar, rootEntityType, context)})`;
|
|
2158
2164
|
}
|
|
2159
2165
|
function formatEdge(relation, edge, context) {
|
|
2160
2166
|
const conditions = [];
|
|
@@ -2227,20 +2233,25 @@ function getAQLQuery(node, options = {}) {
|
|
|
2227
2233
|
clock: options.clock ?? new execution_options_js_1.DefaultClock(),
|
|
2228
2234
|
idGenerator: options.idGenerator ?? new execution_options_js_1.UUIDGenerator(),
|
|
2229
2235
|
maxProjections: options.maxProjections,
|
|
2236
|
+
collectionNamePrefix: options.collectionNamePrefix,
|
|
2230
2237
|
}));
|
|
2231
2238
|
}
|
|
2232
2239
|
function getCollectionForBilling(accessType, context) {
|
|
2233
|
-
const name = arango_basics_js_1.billingCollectionName;
|
|
2240
|
+
const name = (context.options.collectionNamePrefix ?? '') + arango_basics_js_1.billingCollectionName;
|
|
2234
2241
|
context.addCollectionAccess(name, accessType);
|
|
2235
2242
|
return aql_js_1.aql.collection(name);
|
|
2236
2243
|
}
|
|
2237
2244
|
function getCollectionForType(type, accessType, context) {
|
|
2238
|
-
const name = (0, arango_basics_js_1.getCollectionNameForRootEntity)(type
|
|
2245
|
+
const name = (0, arango_basics_js_1.getCollectionNameForRootEntity)(type, {
|
|
2246
|
+
prefix: context.options.collectionNamePrefix,
|
|
2247
|
+
});
|
|
2239
2248
|
context.addCollectionAccess(name, accessType);
|
|
2240
2249
|
return aql_js_1.aql.collection(name);
|
|
2241
2250
|
}
|
|
2242
2251
|
function getCollectionForRelation(relation, accessType, context) {
|
|
2243
|
-
const name = (0, arango_basics_js_1.getCollectionNameForRelation)(relation
|
|
2252
|
+
const name = (0, arango_basics_js_1.getCollectionNameForRelation)(relation, {
|
|
2253
|
+
prefix: context.options.collectionNamePrefix,
|
|
2254
|
+
});
|
|
2244
2255
|
context.addCollectionAccess(name, accessType);
|
|
2245
2256
|
return aql_js_1.aql.collection(name);
|
|
2246
2257
|
}
|
|
@@ -2250,7 +2261,9 @@ function getCollectionForRelation(relation, accessType, context) {
|
|
|
2250
2261
|
*/
|
|
2251
2262
|
function getSimpleFollowEdgeFragment(node, context) {
|
|
2252
2263
|
const dir = node.relationSide.isFromSide ? (0, aql_js_1.aql) `OUTBOUND` : (0, aql_js_1.aql) `INBOUND`;
|
|
2253
|
-
context.addCollectionAccess((0, arango_basics_js_1.getCollectionNameForRootEntity)(node.relationSide.targetType
|
|
2264
|
+
context.addCollectionAccess((0, arango_basics_js_1.getCollectionNameForRootEntity)(node.relationSide.targetType, {
|
|
2265
|
+
prefix: context.options.collectionNamePrefix,
|
|
2266
|
+
}), AccessType.IMPLICIT_READ);
|
|
2254
2267
|
return (0, aql_js_1.aql) `${dir} ${processNode(node.sourceEntityNode, context)} ${getCollectionForRelation(node.relationSide.relation, AccessType.EXPLICIT_READ, context)}`;
|
|
2255
2268
|
}
|
|
2256
2269
|
function generateTokenizationQuery(tokensFiltered) {
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { Relation } from '../core/model/implementation/relation.js';
|
|
2
2
|
import type { RootEntityType } from '../core/model/implementation/root-entity-type.js';
|
|
3
3
|
export declare const billingCollectionName = "billingEntities";
|
|
4
|
-
export declare function getCollectionNameForRootEntity(type: RootEntityType
|
|
5
|
-
|
|
4
|
+
export declare function getCollectionNameForRootEntity(type: RootEntityType, { prefix }: {
|
|
5
|
+
prefix: string | undefined;
|
|
6
|
+
}): string;
|
|
7
|
+
export declare function getCollectionNameForRelation(relation: Relation, { prefix }: {
|
|
8
|
+
prefix: string | undefined;
|
|
9
|
+
}): string;
|
|
@@ -5,9 +5,11 @@ exports.getCollectionNameForRootEntity = getCollectionNameForRootEntity;
|
|
|
5
5
|
exports.getCollectionNameForRelation = getCollectionNameForRelation;
|
|
6
6
|
const utils_js_1 = require("../core/utils/utils.js");
|
|
7
7
|
exports.billingCollectionName = 'billingEntities';
|
|
8
|
-
function getCollectionNameForRootEntity(type) {
|
|
9
|
-
return (0, utils_js_1.decapitalize)(type.pluralName);
|
|
8
|
+
function getCollectionNameForRootEntity(type, { prefix }) {
|
|
9
|
+
return (prefix ?? '') + (0, utils_js_1.decapitalize)(type.pluralName);
|
|
10
10
|
}
|
|
11
|
-
function getCollectionNameForRelation(relation) {
|
|
12
|
-
return getCollectionNameForRootEntity(relation.fromType
|
|
11
|
+
function getCollectionNameForRelation(relation, { prefix }) {
|
|
12
|
+
return (getCollectionNameForRootEntity(relation.fromType, { prefix }) +
|
|
13
|
+
'_' +
|
|
14
|
+
relation.fromField.name);
|
|
13
15
|
}
|
|
@@ -34,6 +34,10 @@ class ArangoDBAdapter {
|
|
|
34
34
|
this.config = config;
|
|
35
35
|
this.schemaContext = schemaContext;
|
|
36
36
|
this.logger = (0, config_js_1.getArangoDBLogger)(schemaContext);
|
|
37
|
+
if (config.collectionNamePrefix !== undefined &&
|
|
38
|
+
!/^[a-zA-Z0-9_]+$/.test(config.collectionNamePrefix)) {
|
|
39
|
+
throw new Error(`ArangoDBConfig.collectionNamePrefix must consist only of letters, digits, and underscores, but got: ${JSON.stringify(config.collectionNamePrefix)}`);
|
|
40
|
+
}
|
|
37
41
|
this.db = (0, config_js_1.initDatabase)(config);
|
|
38
42
|
this.analyzer = new analyzer_js_1.SchemaAnalyzer(config, schemaContext);
|
|
39
43
|
this.migrationPerformer = new performer_js_1.MigrationPerformer(config);
|
|
@@ -223,6 +227,7 @@ class ArangoDBAdapter {
|
|
|
223
227
|
clock: options.clock,
|
|
224
228
|
idGenerator: options.idGenerator,
|
|
225
229
|
maxProjections: options.maxProjections,
|
|
230
|
+
collectionNamePrefix: this.config.collectionNamePrefix,
|
|
226
231
|
});
|
|
227
232
|
executableQueries = aqlQuery.getExecutableQueries();
|
|
228
233
|
}
|
|
@@ -58,6 +58,18 @@ export interface ArangoDBConfig {
|
|
|
58
58
|
* Indices without names will never be ignored.
|
|
59
59
|
*/
|
|
60
60
|
readonly nonManagedIndexNamesPattern?: RegExp;
|
|
61
|
+
/**
|
|
62
|
+
* An optional prefix that is prepended to all collection names (document collections, edge
|
|
63
|
+
* collections) and FlexSearch view names managed by this adapter.
|
|
64
|
+
*
|
|
65
|
+
* This allows multiple cruddl instances to share a single ArangoDB database without collection
|
|
66
|
+
* name collisions. Must consist only of letters, digits, and underscores
|
|
67
|
+
* (characters matching /^[a-zA-Z0-9_]+$/).
|
|
68
|
+
*
|
|
69
|
+
* Example: if set to "myapp_", a type "Order" will be stored in "myapp_orders" instead of
|
|
70
|
+
* "orders".
|
|
71
|
+
*/
|
|
72
|
+
readonly collectionNamePrefix?: string;
|
|
61
73
|
}
|
|
62
74
|
export declare function initDatabase(config: ArangoDBConfig): Database;
|
|
63
75
|
export declare function getArangoDBLogger(schemaContext: ProjectOptions | undefined): Logger;
|
|
@@ -31,15 +31,18 @@ class SchemaAnalyzer {
|
|
|
31
31
|
const existingCollectionNames = new Set(existingCollections.map((coll) => coll.name));
|
|
32
32
|
const migrations = [];
|
|
33
33
|
for (const rootEntity of model.rootEntityTypes) {
|
|
34
|
-
const collectionName = (0, arango_basics_js_1.getCollectionNameForRootEntity)(rootEntity
|
|
34
|
+
const collectionName = (0, arango_basics_js_1.getCollectionNameForRootEntity)(rootEntity, {
|
|
35
|
+
prefix: this.config.collectionNamePrefix,
|
|
36
|
+
});
|
|
35
37
|
if (existingCollectionNames.has(collectionName)) {
|
|
36
38
|
continue;
|
|
37
39
|
}
|
|
38
40
|
migrations.push(new migrations_js_1.CreateDocumentCollectionMigration(collectionName));
|
|
39
41
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
migrations.
|
|
42
|
+
const prefixedBillingCollectionName = (this.config.collectionNamePrefix ?? '') + arango_basics_js_1.billingCollectionName;
|
|
43
|
+
if (!existingCollectionNames.has(prefixedBillingCollectionName) &&
|
|
44
|
+
!migrations.some((value) => value.collectionName === prefixedBillingCollectionName)) {
|
|
45
|
+
migrations.push(new migrations_js_1.CreateDocumentCollectionMigration(prefixedBillingCollectionName));
|
|
43
46
|
}
|
|
44
47
|
return migrations;
|
|
45
48
|
}
|
|
@@ -49,7 +52,9 @@ class SchemaAnalyzer {
|
|
|
49
52
|
const existingCollectionNames = new Set(existingCollections.map((coll) => coll.name));
|
|
50
53
|
const migrations = [];
|
|
51
54
|
for (const relation of model.relations) {
|
|
52
|
-
const collectionName = (0, arango_basics_js_1.getCollectionNameForRelation)(relation
|
|
55
|
+
const collectionName = (0, arango_basics_js_1.getCollectionNameForRelation)(relation, {
|
|
56
|
+
prefix: this.config.collectionNamePrefix,
|
|
57
|
+
});
|
|
53
58
|
if (existingCollectionNames.has(collectionName)) {
|
|
54
59
|
continue;
|
|
55
60
|
}
|
|
@@ -59,7 +64,9 @@ class SchemaAnalyzer {
|
|
|
59
64
|
}
|
|
60
65
|
async getIndexMigrations(model) {
|
|
61
66
|
// update indices
|
|
62
|
-
const requiredIndices = (0, index_helpers_js_1.getRequiredIndicesFromModel)(model
|
|
67
|
+
const requiredIndices = (0, index_helpers_js_1.getRequiredIndicesFromModel)(model, {
|
|
68
|
+
prefix: this.config.collectionNamePrefix,
|
|
69
|
+
});
|
|
63
70
|
const existingIndicesPromises = model.rootEntityTypes.map((rootEntityType) => this.getPersistentCollectionIndices(rootEntityType));
|
|
64
71
|
let existingIndices = [];
|
|
65
72
|
await Promise.all(existingIndicesPromises).then((promiseResults) => promiseResults.forEach((indices) => indices.forEach((index) => existingIndices.push(index))));
|
|
@@ -92,7 +99,9 @@ class SchemaAnalyzer {
|
|
|
92
99
|
];
|
|
93
100
|
}
|
|
94
101
|
async getPersistentCollectionIndices(rootEntityType) {
|
|
95
|
-
const collectionName = (0, arango_basics_js_1.getCollectionNameForRootEntity)(rootEntityType
|
|
102
|
+
const collectionName = (0, arango_basics_js_1.getCollectionNameForRootEntity)(rootEntityType, {
|
|
103
|
+
prefix: this.config.collectionNamePrefix,
|
|
104
|
+
});
|
|
96
105
|
const coll = this.db.collection(collectionName);
|
|
97
106
|
if (!(await coll.exists())) {
|
|
98
107
|
return [];
|
|
@@ -129,11 +138,16 @@ class SchemaAnalyzer {
|
|
|
129
138
|
}
|
|
130
139
|
}
|
|
131
140
|
// the views that match the model
|
|
132
|
-
const requiredViews = (0, arango_search_helpers_js_1.getRequiredViewsFromModel)(model
|
|
141
|
+
const requiredViews = (0, arango_search_helpers_js_1.getRequiredViewsFromModel)(model, {
|
|
142
|
+
prefix: this.config.collectionNamePrefix,
|
|
143
|
+
});
|
|
133
144
|
// the currently existing views
|
|
134
145
|
const views = (await this.db.listViews())
|
|
135
146
|
.map((value) => this.db.view(value.name))
|
|
136
|
-
.filter((view) => model.rootEntityTypes.some((rootEntityType) => view.name ===
|
|
147
|
+
.filter((view) => model.rootEntityTypes.some((rootEntityType) => view.name ===
|
|
148
|
+
(0, arango_search_helpers_js_1.getFlexSearchViewNameForRootEntity)(rootEntityType, {
|
|
149
|
+
prefix: this.config.collectionNamePrefix,
|
|
150
|
+
})));
|
|
137
151
|
const configuration = this.config.arangoSearchConfiguration;
|
|
138
152
|
const viewsToCreate = await (0, arango_search_helpers_js_1.calculateRequiredArangoSearchViewCreateOperations)(views, requiredViews, this.db, configuration);
|
|
139
153
|
const viewsToDrop = (0, arango_search_helpers_js_1.calculateRequiredArangoSearchViewDropOperations)(views, requiredViews);
|
|
@@ -46,8 +46,12 @@ export interface ArangoSearchConfiguration {
|
|
|
46
46
|
*/
|
|
47
47
|
readonly useRenameStrategyToRecreate?: boolean;
|
|
48
48
|
}
|
|
49
|
-
export declare function getRequiredViewsFromModel(model: Model
|
|
50
|
-
|
|
49
|
+
export declare function getRequiredViewsFromModel(model: Model, { prefix }: {
|
|
50
|
+
prefix: string | undefined;
|
|
51
|
+
}): ReadonlyArray<ArangoSearchDefinition>;
|
|
52
|
+
export declare function getFlexSearchViewNameForRootEntity(rootEntity: RootEntityType, { prefix }: {
|
|
53
|
+
prefix: string | undefined;
|
|
54
|
+
}): string;
|
|
51
55
|
export declare function calculateRequiredArangoSearchViewCreateOperations(existingViews: ReadonlyArray<View>, requiredViews: ReadonlyArray<ArangoSearchDefinition>, db: Database, configuration?: ArangoSearchConfiguration): Promise<ReadonlyArray<SchemaMigration>>;
|
|
52
56
|
export declare function calculateRequiredArangoSearchViewDropOperations(views: ReadonlyArray<View>, definitions: ReadonlyArray<ArangoSearchDefinition>): ReadonlyArray<SchemaMigration>;
|
|
53
57
|
/**
|
|
@@ -19,19 +19,21 @@ const string_map_js_1 = require("../../core/schema/scalars/string-map.js");
|
|
|
19
19
|
const arango_basics_js_1 = require("../arango-basics.js");
|
|
20
20
|
const migrations_js_1 = require("./migrations.js");
|
|
21
21
|
exports.FLEX_SEARCH_VIEW_PREFIX = 'flex_view_';
|
|
22
|
-
function getRequiredViewsFromModel(model) {
|
|
22
|
+
function getRequiredViewsFromModel(model, { prefix }) {
|
|
23
23
|
return model.rootEntityTypes
|
|
24
24
|
.filter((value) => value.isFlexSearchIndexed)
|
|
25
|
-
.map((rootEntity) => getViewForRootEntity(rootEntity));
|
|
25
|
+
.map((rootEntity) => getViewForRootEntity(rootEntity, prefix));
|
|
26
26
|
}
|
|
27
|
-
function getFlexSearchViewNameForRootEntity(rootEntity) {
|
|
28
|
-
return
|
|
27
|
+
function getFlexSearchViewNameForRootEntity(rootEntity, { prefix }) {
|
|
28
|
+
return ((prefix ?? '') +
|
|
29
|
+
exports.FLEX_SEARCH_VIEW_PREFIX +
|
|
30
|
+
(0, arango_basics_js_1.getCollectionNameForRootEntity)(rootEntity, { prefix: undefined }));
|
|
29
31
|
}
|
|
30
|
-
function getViewForRootEntity(rootEntityType) {
|
|
32
|
+
function getViewForRootEntity(rootEntityType, prefix) {
|
|
31
33
|
return {
|
|
32
34
|
rootEntityType,
|
|
33
|
-
viewName: getFlexSearchViewNameForRootEntity(rootEntityType),
|
|
34
|
-
collectionName: (0, arango_basics_js_1.getCollectionNameForRootEntity)(rootEntityType),
|
|
35
|
+
viewName: getFlexSearchViewNameForRootEntity(rootEntityType, { prefix }),
|
|
36
|
+
collectionName: (0, arango_basics_js_1.getCollectionNameForRootEntity)(rootEntityType, { prefix }),
|
|
35
37
|
primarySort: rootEntityType.flexSearchPrimarySort.map((clause) => ({
|
|
36
38
|
field: getPrimarySortFieldPath(clause.field),
|
|
37
39
|
asc: clause.direction === order_js_1.OrderDirection.ASCENDING,
|
|
@@ -13,7 +13,9 @@ export interface IndexDefinition {
|
|
|
13
13
|
}
|
|
14
14
|
export declare function describeIndex(index: IndexDefinition): string;
|
|
15
15
|
export declare function getIndexDescriptor(index: IndexDefinition): string;
|
|
16
|
-
export declare function getRequiredIndicesFromModel(model: Model
|
|
16
|
+
export declare function getRequiredIndicesFromModel(model: Model, { prefix }: {
|
|
17
|
+
prefix: string | undefined;
|
|
18
|
+
}): ReadonlyArray<IndexDefinition>;
|
|
17
19
|
export declare function calculateRequiredIndexOperations(existingIndices: ReadonlyArray<IndexDefinition>, requiredIndices: ReadonlyArray<IndexDefinition>, config: ArangoDBConfig): {
|
|
18
20
|
indicesToDelete: ReadonlyArray<IndexDefinition>;
|
|
19
21
|
indicesToCreate: ReadonlyArray<IndexDefinition>;
|
|
@@ -33,13 +33,13 @@ function indexDefinitionsEqual(a, b) {
|
|
|
33
33
|
a.sparse === b.sparse &&
|
|
34
34
|
a.type === b.type);
|
|
35
35
|
}
|
|
36
|
-
function getRequiredIndicesFromModel(model) {
|
|
37
|
-
return model.rootEntityTypes.flatMap((rootEntity) => getIndicesForRootEntity(rootEntity));
|
|
36
|
+
function getRequiredIndicesFromModel(model, { prefix }) {
|
|
37
|
+
return model.rootEntityTypes.flatMap((rootEntity) => getIndicesForRootEntity(rootEntity, prefix));
|
|
38
38
|
}
|
|
39
|
-
function getIndicesForRootEntity(rootEntity) {
|
|
39
|
+
function getIndicesForRootEntity(rootEntity, prefix) {
|
|
40
40
|
return rootEntity.indices.map((index) => ({
|
|
41
41
|
rootEntity,
|
|
42
|
-
collectionName: (0, arango_basics_js_1.getCollectionNameForRootEntity)(rootEntity),
|
|
42
|
+
collectionName: (0, arango_basics_js_1.getCollectionNameForRootEntity)(rootEntity, { prefix }),
|
|
43
43
|
name: index.name,
|
|
44
44
|
fields: index.fields.map(getArangoFieldPath),
|
|
45
45
|
unique: index.unique,
|
package/dist/cjs/core/version.js
CHANGED
|
@@ -3,4 +3,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.CRUDDL_VERSION = void 0;
|
|
4
4
|
// do not modify - this is parsed and changed by the build script
|
|
5
5
|
// explicitly annotating with "string" so typescript won't infer a string literal type
|
|
6
|
-
exports.CRUDDL_VERSION = '6.0.0-alpha.
|
|
6
|
+
exports.CRUDDL_VERSION = '6.0.0-alpha.2';
|
|
@@ -19,6 +19,11 @@ export interface QueryGenerationOptions {
|
|
|
19
19
|
* See {@link ExecutionOptions.maxProjections} for details.
|
|
20
20
|
*/
|
|
21
21
|
readonly maxProjections?: number;
|
|
22
|
+
/**
|
|
23
|
+
* An optional prefix prepended to all collection and view names in generated AQL queries.
|
|
24
|
+
* See {@link ArangoDBConfig.collectionNamePrefix} for details.
|
|
25
|
+
*/
|
|
26
|
+
readonly collectionNamePrefix?: string;
|
|
22
27
|
}
|
|
23
28
|
export declare function getAQLQuery(node: QueryNode, options?: Partial<QueryGenerationOptions>): AQLCompoundQuery;
|
|
24
29
|
export declare function generateTokenizationQuery(tokensFiltered: ReadonlyArray<FlexSearchTokenizable>): AQLFragment;
|
|
@@ -377,7 +377,9 @@ register(FlexSearchQueryNode, (node, context) => {
|
|
|
377
377
|
let itemContext = context
|
|
378
378
|
.bindVariable(node.itemVariable)
|
|
379
379
|
.withExtension(inFlexSearchFilterSymbol, true);
|
|
380
|
-
const viewName = getFlexSearchViewNameForRootEntity(node.rootEntityType
|
|
380
|
+
const viewName = getFlexSearchViewNameForRootEntity(node.rootEntityType, {
|
|
381
|
+
prefix: context.options.collectionNamePrefix,
|
|
382
|
+
});
|
|
381
383
|
context.addCollectionAccess(viewName, AccessType.EXPLICIT_READ);
|
|
382
384
|
return aqlExt.subquery(aql `FOR ${itemContext.getVariable(node.itemVariable)}`, aql `IN ${aql.collection(viewName)}`, aql `SEARCH ${processNode(node.flexFilterNode, itemContext)}`, node.isOptimisationsDisabled ? aql `OPTIONS { conditionOptimization: 'none' }` : aql ``, aql `RETURN ${itemContext.getVariable(node.itemVariable)}`);
|
|
383
385
|
});
|
|
@@ -1833,10 +1835,10 @@ function getRelationTraversalForStatements({ node, innermostItemVar, context, pr
|
|
|
1833
1835
|
let sourceFrag;
|
|
1834
1836
|
if (node.entitiesIdentifierKind === EntitiesIdentifierKind.ID) {
|
|
1835
1837
|
if (node.sourceIsList) {
|
|
1836
|
-
sourceFrag = getFullIDFromKeysFragment(plainSourceFrag, node.relationSegments[0].relationSide.sourceType);
|
|
1838
|
+
sourceFrag = getFullIDFromKeysFragment(plainSourceFrag, node.relationSegments[0].relationSide.sourceType, context);
|
|
1837
1839
|
}
|
|
1838
1840
|
else {
|
|
1839
|
-
sourceFrag = getFullIDFromKeyFragment(plainSourceFrag, node.relationSegments[0].relationSide.sourceType);
|
|
1841
|
+
sourceFrag = getFullIDFromKeyFragment(plainSourceFrag, node.relationSegments[0].relationSide.sourceType, context);
|
|
1840
1842
|
}
|
|
1841
1843
|
}
|
|
1842
1844
|
else {
|
|
@@ -1926,7 +1928,9 @@ function getRelationTraversalForStatements({ node, innermostItemVar, context, pr
|
|
|
1926
1928
|
forFragments.push(traversalFrag);
|
|
1927
1929
|
currentObjectFrag = nodeVar;
|
|
1928
1930
|
}
|
|
1929
|
-
context.addCollectionAccess(getCollectionNameForRootEntity(segment.relationSide.targetType
|
|
1931
|
+
context.addCollectionAccess(getCollectionNameForRootEntity(segment.relationSide.targetType, {
|
|
1932
|
+
prefix: context.options.collectionNamePrefix,
|
|
1933
|
+
}), AccessType.IMPLICIT_READ);
|
|
1930
1934
|
segmentIndex++;
|
|
1931
1935
|
}
|
|
1932
1936
|
// each FOR automatically removes NULLs of the previous step (just how FOR works)
|
|
@@ -2121,14 +2125,14 @@ function getFullIDFromKeyNode(node, rootEntityType, context) {
|
|
|
2121
2125
|
// special handling to avoid concat if possible - do not alter the behavior
|
|
2122
2126
|
if (node instanceof LiteralQueryNode && typeof node.value == 'string') {
|
|
2123
2127
|
// just append the node to the literal key in JavaScript and bind it as a string
|
|
2124
|
-
return aql `${getCollectionNameForRootEntity(rootEntityType) + '/' + node.value}`;
|
|
2128
|
+
return aql `${getCollectionNameForRootEntity(rootEntityType, { prefix: context.options.collectionNamePrefix }) + '/' + node.value}`;
|
|
2125
2129
|
}
|
|
2126
2130
|
if (node instanceof RootEntityIDQueryNode) {
|
|
2127
2131
|
// access the _id field. processNode(node) would access the _key field instead.
|
|
2128
2132
|
return aql `${processNode(node.objectNode, context)}._id`;
|
|
2129
2133
|
}
|
|
2130
2134
|
// fall back to general case
|
|
2131
|
-
return getFullIDFromKeyFragment(processNode(node, context), rootEntityType);
|
|
2135
|
+
return getFullIDFromKeyFragment(processNode(node, context), rootEntityType, context);
|
|
2132
2136
|
}
|
|
2133
2137
|
function getFullIDsFromKeysNode(idsNode, rootEntityType, context) {
|
|
2134
2138
|
if (idsNode instanceof ListQueryNode) {
|
|
@@ -2139,18 +2143,20 @@ function getFullIDsFromKeysNode(idsNode, rootEntityType, context) {
|
|
|
2139
2143
|
if (idsNode instanceof LiteralQueryNode &&
|
|
2140
2144
|
isReadonlyArray(idsNode.value) &&
|
|
2141
2145
|
idsNode.value.every((v) => typeof v === 'string')) {
|
|
2142
|
-
const collName = getCollectionNameForRootEntity(rootEntityType
|
|
2146
|
+
const collName = getCollectionNameForRootEntity(rootEntityType, {
|
|
2147
|
+
prefix: context.options.collectionNamePrefix,
|
|
2148
|
+
});
|
|
2143
2149
|
const ids = idsNode.value.map((val) => collName + '/' + val);
|
|
2144
2150
|
return aql.value(ids);
|
|
2145
2151
|
}
|
|
2146
|
-
return getFullIDFromKeysFragment(processNode(idsNode, context), rootEntityType);
|
|
2152
|
+
return getFullIDFromKeysFragment(processNode(idsNode, context), rootEntityType, context);
|
|
2147
2153
|
}
|
|
2148
|
-
function getFullIDFromKeyFragment(keyFragment, rootEntityType) {
|
|
2149
|
-
return aql `CONCAT(${getCollectionNameForRootEntity(rootEntityType) + '/'}, ${keyFragment})`;
|
|
2154
|
+
function getFullIDFromKeyFragment(keyFragment, rootEntityType, context) {
|
|
2155
|
+
return aql `CONCAT(${getCollectionNameForRootEntity(rootEntityType, { prefix: context.options.collectionNamePrefix }) + '/'}, ${keyFragment})`;
|
|
2150
2156
|
}
|
|
2151
|
-
function getFullIDFromKeysFragment(keysFragment, rootEntityType) {
|
|
2157
|
+
function getFullIDFromKeysFragment(keysFragment, rootEntityType, context) {
|
|
2152
2158
|
const idVar = aql.variable('id');
|
|
2153
|
-
return aql `(FOR ${idVar} IN ${keysFragment} RETURN ${getFullIDFromKeyFragment(idVar, rootEntityType)})`;
|
|
2159
|
+
return aql `(FOR ${idVar} IN ${keysFragment} RETURN ${getFullIDFromKeyFragment(idVar, rootEntityType, context)})`;
|
|
2154
2160
|
}
|
|
2155
2161
|
function formatEdge(relation, edge, context) {
|
|
2156
2162
|
const conditions = [];
|
|
@@ -2223,20 +2229,25 @@ export function getAQLQuery(node, options = {}) {
|
|
|
2223
2229
|
clock: options.clock ?? new DefaultClock(),
|
|
2224
2230
|
idGenerator: options.idGenerator ?? new UUIDGenerator(),
|
|
2225
2231
|
maxProjections: options.maxProjections,
|
|
2232
|
+
collectionNamePrefix: options.collectionNamePrefix,
|
|
2226
2233
|
}));
|
|
2227
2234
|
}
|
|
2228
2235
|
function getCollectionForBilling(accessType, context) {
|
|
2229
|
-
const name = billingCollectionName;
|
|
2236
|
+
const name = (context.options.collectionNamePrefix ?? '') + billingCollectionName;
|
|
2230
2237
|
context.addCollectionAccess(name, accessType);
|
|
2231
2238
|
return aql.collection(name);
|
|
2232
2239
|
}
|
|
2233
2240
|
function getCollectionForType(type, accessType, context) {
|
|
2234
|
-
const name = getCollectionNameForRootEntity(type
|
|
2241
|
+
const name = getCollectionNameForRootEntity(type, {
|
|
2242
|
+
prefix: context.options.collectionNamePrefix,
|
|
2243
|
+
});
|
|
2235
2244
|
context.addCollectionAccess(name, accessType);
|
|
2236
2245
|
return aql.collection(name);
|
|
2237
2246
|
}
|
|
2238
2247
|
function getCollectionForRelation(relation, accessType, context) {
|
|
2239
|
-
const name = getCollectionNameForRelation(relation
|
|
2248
|
+
const name = getCollectionNameForRelation(relation, {
|
|
2249
|
+
prefix: context.options.collectionNamePrefix,
|
|
2250
|
+
});
|
|
2240
2251
|
context.addCollectionAccess(name, accessType);
|
|
2241
2252
|
return aql.collection(name);
|
|
2242
2253
|
}
|
|
@@ -2246,7 +2257,9 @@ function getCollectionForRelation(relation, accessType, context) {
|
|
|
2246
2257
|
*/
|
|
2247
2258
|
function getSimpleFollowEdgeFragment(node, context) {
|
|
2248
2259
|
const dir = node.relationSide.isFromSide ? aql `OUTBOUND` : aql `INBOUND`;
|
|
2249
|
-
context.addCollectionAccess(getCollectionNameForRootEntity(node.relationSide.targetType
|
|
2260
|
+
context.addCollectionAccess(getCollectionNameForRootEntity(node.relationSide.targetType, {
|
|
2261
|
+
prefix: context.options.collectionNamePrefix,
|
|
2262
|
+
}), AccessType.IMPLICIT_READ);
|
|
2250
2263
|
return aql `${dir} ${processNode(node.sourceEntityNode, context)} ${getCollectionForRelation(node.relationSide.relation, AccessType.EXPLICIT_READ, context)}`;
|
|
2251
2264
|
}
|
|
2252
2265
|
export function generateTokenizationQuery(tokensFiltered) {
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import type { Relation } from '../core/model/implementation/relation.js';
|
|
2
2
|
import type { RootEntityType } from '../core/model/implementation/root-entity-type.js';
|
|
3
3
|
export declare const billingCollectionName = "billingEntities";
|
|
4
|
-
export declare function getCollectionNameForRootEntity(type: RootEntityType
|
|
5
|
-
|
|
4
|
+
export declare function getCollectionNameForRootEntity(type: RootEntityType, { prefix }: {
|
|
5
|
+
prefix: string | undefined;
|
|
6
|
+
}): string;
|
|
7
|
+
export declare function getCollectionNameForRelation(relation: Relation, { prefix }: {
|
|
8
|
+
prefix: string | undefined;
|
|
9
|
+
}): string;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { decapitalize } from '../core/utils/utils.js';
|
|
2
2
|
export const billingCollectionName = 'billingEntities';
|
|
3
|
-
export function getCollectionNameForRootEntity(type) {
|
|
4
|
-
return decapitalize(type.pluralName);
|
|
3
|
+
export function getCollectionNameForRootEntity(type, { prefix }) {
|
|
4
|
+
return (prefix ?? '') + decapitalize(type.pluralName);
|
|
5
5
|
}
|
|
6
|
-
export function getCollectionNameForRelation(relation) {
|
|
7
|
-
return getCollectionNameForRootEntity(relation.fromType
|
|
6
|
+
export function getCollectionNameForRelation(relation, { prefix }) {
|
|
7
|
+
return (getCollectionNameForRootEntity(relation.fromType, { prefix }) +
|
|
8
|
+
'_' +
|
|
9
|
+
relation.fromField.name);
|
|
8
10
|
}
|
|
@@ -32,6 +32,10 @@ export class ArangoDBAdapter {
|
|
|
32
32
|
this.config = config;
|
|
33
33
|
this.schemaContext = schemaContext;
|
|
34
34
|
this.logger = getArangoDBLogger(schemaContext);
|
|
35
|
+
if (config.collectionNamePrefix !== undefined &&
|
|
36
|
+
!/^[a-zA-Z0-9_]+$/.test(config.collectionNamePrefix)) {
|
|
37
|
+
throw new Error(`ArangoDBConfig.collectionNamePrefix must consist only of letters, digits, and underscores, but got: ${JSON.stringify(config.collectionNamePrefix)}`);
|
|
38
|
+
}
|
|
35
39
|
this.db = initDatabase(config);
|
|
36
40
|
this.analyzer = new SchemaAnalyzer(config, schemaContext);
|
|
37
41
|
this.migrationPerformer = new MigrationPerformer(config);
|
|
@@ -221,6 +225,7 @@ export class ArangoDBAdapter {
|
|
|
221
225
|
clock: options.clock,
|
|
222
226
|
idGenerator: options.idGenerator,
|
|
223
227
|
maxProjections: options.maxProjections,
|
|
228
|
+
collectionNamePrefix: this.config.collectionNamePrefix,
|
|
224
229
|
});
|
|
225
230
|
executableQueries = aqlQuery.getExecutableQueries();
|
|
226
231
|
}
|
|
@@ -58,6 +58,18 @@ export interface ArangoDBConfig {
|
|
|
58
58
|
* Indices without names will never be ignored.
|
|
59
59
|
*/
|
|
60
60
|
readonly nonManagedIndexNamesPattern?: RegExp;
|
|
61
|
+
/**
|
|
62
|
+
* An optional prefix that is prepended to all collection names (document collections, edge
|
|
63
|
+
* collections) and FlexSearch view names managed by this adapter.
|
|
64
|
+
*
|
|
65
|
+
* This allows multiple cruddl instances to share a single ArangoDB database without collection
|
|
66
|
+
* name collisions. Must consist only of letters, digits, and underscores
|
|
67
|
+
* (characters matching /^[a-zA-Z0-9_]+$/).
|
|
68
|
+
*
|
|
69
|
+
* Example: if set to "myapp_", a type "Order" will be stored in "myapp_orders" instead of
|
|
70
|
+
* "orders".
|
|
71
|
+
*/
|
|
72
|
+
readonly collectionNamePrefix?: string;
|
|
61
73
|
}
|
|
62
74
|
export declare function initDatabase(config: ArangoDBConfig): Database;
|
|
63
75
|
export declare function getArangoDBLogger(schemaContext: ProjectOptions | undefined): Logger;
|
|
@@ -28,15 +28,18 @@ export class SchemaAnalyzer {
|
|
|
28
28
|
const existingCollectionNames = new Set(existingCollections.map((coll) => coll.name));
|
|
29
29
|
const migrations = [];
|
|
30
30
|
for (const rootEntity of model.rootEntityTypes) {
|
|
31
|
-
const collectionName = getCollectionNameForRootEntity(rootEntity
|
|
31
|
+
const collectionName = getCollectionNameForRootEntity(rootEntity, {
|
|
32
|
+
prefix: this.config.collectionNamePrefix,
|
|
33
|
+
});
|
|
32
34
|
if (existingCollectionNames.has(collectionName)) {
|
|
33
35
|
continue;
|
|
34
36
|
}
|
|
35
37
|
migrations.push(new CreateDocumentCollectionMigration(collectionName));
|
|
36
38
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
migrations.
|
|
39
|
+
const prefixedBillingCollectionName = (this.config.collectionNamePrefix ?? '') + billingCollectionName;
|
|
40
|
+
if (!existingCollectionNames.has(prefixedBillingCollectionName) &&
|
|
41
|
+
!migrations.some((value) => value.collectionName === prefixedBillingCollectionName)) {
|
|
42
|
+
migrations.push(new CreateDocumentCollectionMigration(prefixedBillingCollectionName));
|
|
40
43
|
}
|
|
41
44
|
return migrations;
|
|
42
45
|
}
|
|
@@ -46,7 +49,9 @@ export class SchemaAnalyzer {
|
|
|
46
49
|
const existingCollectionNames = new Set(existingCollections.map((coll) => coll.name));
|
|
47
50
|
const migrations = [];
|
|
48
51
|
for (const relation of model.relations) {
|
|
49
|
-
const collectionName = getCollectionNameForRelation(relation
|
|
52
|
+
const collectionName = getCollectionNameForRelation(relation, {
|
|
53
|
+
prefix: this.config.collectionNamePrefix,
|
|
54
|
+
});
|
|
50
55
|
if (existingCollectionNames.has(collectionName)) {
|
|
51
56
|
continue;
|
|
52
57
|
}
|
|
@@ -56,7 +61,9 @@ export class SchemaAnalyzer {
|
|
|
56
61
|
}
|
|
57
62
|
async getIndexMigrations(model) {
|
|
58
63
|
// update indices
|
|
59
|
-
const requiredIndices = getRequiredIndicesFromModel(model
|
|
64
|
+
const requiredIndices = getRequiredIndicesFromModel(model, {
|
|
65
|
+
prefix: this.config.collectionNamePrefix,
|
|
66
|
+
});
|
|
60
67
|
const existingIndicesPromises = model.rootEntityTypes.map((rootEntityType) => this.getPersistentCollectionIndices(rootEntityType));
|
|
61
68
|
let existingIndices = [];
|
|
62
69
|
await Promise.all(existingIndicesPromises).then((promiseResults) => promiseResults.forEach((indices) => indices.forEach((index) => existingIndices.push(index))));
|
|
@@ -89,7 +96,9 @@ export class SchemaAnalyzer {
|
|
|
89
96
|
];
|
|
90
97
|
}
|
|
91
98
|
async getPersistentCollectionIndices(rootEntityType) {
|
|
92
|
-
const collectionName = getCollectionNameForRootEntity(rootEntityType
|
|
99
|
+
const collectionName = getCollectionNameForRootEntity(rootEntityType, {
|
|
100
|
+
prefix: this.config.collectionNamePrefix,
|
|
101
|
+
});
|
|
93
102
|
const coll = this.db.collection(collectionName);
|
|
94
103
|
if (!(await coll.exists())) {
|
|
95
104
|
return [];
|
|
@@ -126,11 +135,16 @@ export class SchemaAnalyzer {
|
|
|
126
135
|
}
|
|
127
136
|
}
|
|
128
137
|
// the views that match the model
|
|
129
|
-
const requiredViews = getRequiredViewsFromModel(model
|
|
138
|
+
const requiredViews = getRequiredViewsFromModel(model, {
|
|
139
|
+
prefix: this.config.collectionNamePrefix,
|
|
140
|
+
});
|
|
130
141
|
// the currently existing views
|
|
131
142
|
const views = (await this.db.listViews())
|
|
132
143
|
.map((value) => this.db.view(value.name))
|
|
133
|
-
.filter((view) => model.rootEntityTypes.some((rootEntityType) => view.name ===
|
|
144
|
+
.filter((view) => model.rootEntityTypes.some((rootEntityType) => view.name ===
|
|
145
|
+
getFlexSearchViewNameForRootEntity(rootEntityType, {
|
|
146
|
+
prefix: this.config.collectionNamePrefix,
|
|
147
|
+
})));
|
|
134
148
|
const configuration = this.config.arangoSearchConfiguration;
|
|
135
149
|
const viewsToCreate = await calculateRequiredArangoSearchViewCreateOperations(views, requiredViews, this.db, configuration);
|
|
136
150
|
const viewsToDrop = calculateRequiredArangoSearchViewDropOperations(views, requiredViews);
|
|
@@ -46,8 +46,12 @@ export interface ArangoSearchConfiguration {
|
|
|
46
46
|
*/
|
|
47
47
|
readonly useRenameStrategyToRecreate?: boolean;
|
|
48
48
|
}
|
|
49
|
-
export declare function getRequiredViewsFromModel(model: Model
|
|
50
|
-
|
|
49
|
+
export declare function getRequiredViewsFromModel(model: Model, { prefix }: {
|
|
50
|
+
prefix: string | undefined;
|
|
51
|
+
}): ReadonlyArray<ArangoSearchDefinition>;
|
|
52
|
+
export declare function getFlexSearchViewNameForRootEntity(rootEntity: RootEntityType, { prefix }: {
|
|
53
|
+
prefix: string | undefined;
|
|
54
|
+
}): string;
|
|
51
55
|
export declare function calculateRequiredArangoSearchViewCreateOperations(existingViews: ReadonlyArray<View>, requiredViews: ReadonlyArray<ArangoSearchDefinition>, db: Database, configuration?: ArangoSearchConfiguration): Promise<ReadonlyArray<SchemaMigration>>;
|
|
52
56
|
export declare function calculateRequiredArangoSearchViewDropOperations(views: ReadonlyArray<View>, definitions: ReadonlyArray<ArangoSearchDefinition>): ReadonlyArray<SchemaMigration>;
|
|
53
57
|
/**
|
|
@@ -8,19 +8,21 @@ import { GraphQLI18nString } from '../../core/schema/scalars/string-map.js';
|
|
|
8
8
|
import { getCollectionNameForRootEntity } from '../arango-basics.js';
|
|
9
9
|
import { CreateArangoSearchViewMigration, DropArangoSearchViewMigration, RecreateArangoSearchViewMigration, UpdateArangoSearchViewMigration, } from './migrations.js';
|
|
10
10
|
export const FLEX_SEARCH_VIEW_PREFIX = 'flex_view_';
|
|
11
|
-
export function getRequiredViewsFromModel(model) {
|
|
11
|
+
export function getRequiredViewsFromModel(model, { prefix }) {
|
|
12
12
|
return model.rootEntityTypes
|
|
13
13
|
.filter((value) => value.isFlexSearchIndexed)
|
|
14
|
-
.map((rootEntity) => getViewForRootEntity(rootEntity));
|
|
14
|
+
.map((rootEntity) => getViewForRootEntity(rootEntity, prefix));
|
|
15
15
|
}
|
|
16
|
-
export function getFlexSearchViewNameForRootEntity(rootEntity) {
|
|
17
|
-
return
|
|
16
|
+
export function getFlexSearchViewNameForRootEntity(rootEntity, { prefix }) {
|
|
17
|
+
return ((prefix ?? '') +
|
|
18
|
+
FLEX_SEARCH_VIEW_PREFIX +
|
|
19
|
+
getCollectionNameForRootEntity(rootEntity, { prefix: undefined }));
|
|
18
20
|
}
|
|
19
|
-
function getViewForRootEntity(rootEntityType) {
|
|
21
|
+
function getViewForRootEntity(rootEntityType, prefix) {
|
|
20
22
|
return {
|
|
21
23
|
rootEntityType,
|
|
22
|
-
viewName: getFlexSearchViewNameForRootEntity(rootEntityType),
|
|
23
|
-
collectionName: getCollectionNameForRootEntity(rootEntityType),
|
|
24
|
+
viewName: getFlexSearchViewNameForRootEntity(rootEntityType, { prefix }),
|
|
25
|
+
collectionName: getCollectionNameForRootEntity(rootEntityType, { prefix }),
|
|
24
26
|
primarySort: rootEntityType.flexSearchPrimarySort.map((clause) => ({
|
|
25
27
|
field: getPrimarySortFieldPath(clause.field),
|
|
26
28
|
asc: clause.direction === OrderDirection.ASCENDING,
|
|
@@ -13,7 +13,9 @@ export interface IndexDefinition {
|
|
|
13
13
|
}
|
|
14
14
|
export declare function describeIndex(index: IndexDefinition): string;
|
|
15
15
|
export declare function getIndexDescriptor(index: IndexDefinition): string;
|
|
16
|
-
export declare function getRequiredIndicesFromModel(model: Model
|
|
16
|
+
export declare function getRequiredIndicesFromModel(model: Model, { prefix }: {
|
|
17
|
+
prefix: string | undefined;
|
|
18
|
+
}): ReadonlyArray<IndexDefinition>;
|
|
17
19
|
export declare function calculateRequiredIndexOperations(existingIndices: ReadonlyArray<IndexDefinition>, requiredIndices: ReadonlyArray<IndexDefinition>, config: ArangoDBConfig): {
|
|
18
20
|
indicesToDelete: ReadonlyArray<IndexDefinition>;
|
|
19
21
|
indicesToCreate: ReadonlyArray<IndexDefinition>;
|
|
@@ -27,13 +27,13 @@ function indexDefinitionsEqual(a, b) {
|
|
|
27
27
|
a.sparse === b.sparse &&
|
|
28
28
|
a.type === b.type);
|
|
29
29
|
}
|
|
30
|
-
export function getRequiredIndicesFromModel(model) {
|
|
31
|
-
return model.rootEntityTypes.flatMap((rootEntity) => getIndicesForRootEntity(rootEntity));
|
|
30
|
+
export function getRequiredIndicesFromModel(model, { prefix }) {
|
|
31
|
+
return model.rootEntityTypes.flatMap((rootEntity) => getIndicesForRootEntity(rootEntity, prefix));
|
|
32
32
|
}
|
|
33
|
-
function getIndicesForRootEntity(rootEntity) {
|
|
33
|
+
function getIndicesForRootEntity(rootEntity, prefix) {
|
|
34
34
|
return rootEntity.indices.map((index) => ({
|
|
35
35
|
rootEntity,
|
|
36
|
-
collectionName: getCollectionNameForRootEntity(rootEntity),
|
|
36
|
+
collectionName: getCollectionNameForRootEntity(rootEntity, { prefix }),
|
|
37
37
|
name: index.name,
|
|
38
38
|
fields: index.fields.map(getArangoFieldPath),
|
|
39
39
|
unique: index.unique,
|
package/dist/esm/core/version.js
CHANGED