@push.rocks/smartmongo 2.2.0 → 4.0.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/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/index.d.ts +1 -1
- package/dist_ts/index.js +3 -3
- package/dist_ts/tsmdb/engine/AggregationEngine.js +189 -0
- package/dist_ts/{congodb → tsmdb}/engine/IndexEngine.d.ts +23 -3
- package/dist_ts/tsmdb/engine/IndexEngine.js +678 -0
- package/dist_ts/tsmdb/engine/QueryEngine.js +271 -0
- package/dist_ts/tsmdb/engine/QueryPlanner.d.ts +64 -0
- package/dist_ts/tsmdb/engine/QueryPlanner.js +308 -0
- package/dist_ts/tsmdb/engine/SessionEngine.d.ts +117 -0
- package/dist_ts/tsmdb/engine/SessionEngine.js +232 -0
- package/dist_ts/{congodb → tsmdb}/engine/TransactionEngine.d.ts +1 -1
- package/dist_ts/tsmdb/engine/TransactionEngine.js +287 -0
- package/dist_ts/tsmdb/engine/UpdateEngine.js +461 -0
- package/dist_ts/{congodb/errors/CongoErrors.d.ts → tsmdb/errors/TsmdbErrors.d.ts} +16 -16
- package/dist_ts/tsmdb/errors/TsmdbErrors.js +155 -0
- package/dist_ts/{congodb → tsmdb}/index.d.ts +11 -4
- package/dist_ts/tsmdb/index.js +31 -0
- package/dist_ts/tsmdb/server/CommandRouter.d.ts +87 -0
- package/dist_ts/tsmdb/server/CommandRouter.js +222 -0
- package/dist_ts/{congodb/server/CongoServer.d.ts → tsmdb/server/TsmdbServer.d.ts} +6 -6
- package/dist_ts/tsmdb/server/TsmdbServer.js +229 -0
- package/dist_ts/{congodb → tsmdb}/server/WireProtocol.d.ts +1 -1
- package/dist_ts/tsmdb/server/WireProtocol.js +298 -0
- package/dist_ts/{congodb → tsmdb}/server/handlers/AdminHandler.d.ts +1 -1
- package/dist_ts/tsmdb/server/handlers/AdminHandler.js +668 -0
- package/dist_ts/{congodb → tsmdb}/server/handlers/AggregateHandler.d.ts +1 -1
- package/dist_ts/tsmdb/server/handlers/AggregateHandler.js +277 -0
- package/dist_ts/{congodb → tsmdb}/server/handlers/DeleteHandler.d.ts +1 -1
- package/dist_ts/tsmdb/server/handlers/DeleteHandler.js +95 -0
- package/dist_ts/{congodb → tsmdb}/server/handlers/FindHandler.d.ts +1 -1
- package/dist_ts/tsmdb/server/handlers/FindHandler.js +291 -0
- package/dist_ts/{congodb → tsmdb}/server/handlers/HelloHandler.d.ts +1 -1
- package/dist_ts/{congodb → tsmdb}/server/handlers/HelloHandler.js +2 -2
- package/dist_ts/{congodb → tsmdb}/server/handlers/IndexHandler.d.ts +1 -1
- package/dist_ts/tsmdb/server/handlers/IndexHandler.js +183 -0
- package/dist_ts/{congodb → tsmdb}/server/handlers/InsertHandler.d.ts +1 -1
- package/dist_ts/tsmdb/server/handlers/InsertHandler.js +79 -0
- package/dist_ts/{congodb → tsmdb}/server/handlers/UpdateHandler.d.ts +1 -1
- package/dist_ts/tsmdb/server/handlers/UpdateHandler.js +296 -0
- package/dist_ts/tsmdb/server/handlers/index.js +10 -0
- package/dist_ts/{congodb → tsmdb}/server/index.d.ts +2 -2
- package/dist_ts/tsmdb/server/index.js +7 -0
- package/dist_ts/{congodb → tsmdb}/storage/FileStorageAdapter.d.ts +27 -3
- package/dist_ts/tsmdb/storage/FileStorageAdapter.js +465 -0
- package/dist_ts/{congodb → tsmdb}/storage/IStorageAdapter.d.ts +7 -2
- package/dist_ts/{congodb → tsmdb}/storage/IStorageAdapter.js +1 -1
- package/dist_ts/{congodb → tsmdb}/storage/MemoryStorageAdapter.d.ts +3 -2
- package/dist_ts/tsmdb/storage/MemoryStorageAdapter.js +378 -0
- package/dist_ts/{congodb → tsmdb}/storage/OpLog.d.ts +1 -1
- package/dist_ts/tsmdb/storage/OpLog.js +221 -0
- package/dist_ts/tsmdb/storage/WAL.d.ts +117 -0
- package/dist_ts/tsmdb/storage/WAL.js +286 -0
- package/dist_ts/tsmdb/tsmdb.plugins.js +14 -0
- package/dist_ts/{congodb → tsmdb}/types/interfaces.d.ts +3 -3
- package/dist_ts/{congodb → tsmdb}/types/interfaces.js +1 -1
- package/dist_ts/tsmdb/utils/checksum.d.ts +30 -0
- package/dist_ts/tsmdb/utils/checksum.js +77 -0
- package/dist_ts/tsmdb/utils/index.d.ts +1 -0
- package/dist_ts/tsmdb/utils/index.js +2 -0
- package/package.json +1 -1
- package/readme.hints.md +7 -12
- package/readme.md +25 -25
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/index.ts +2 -2
- package/ts/{congodb → tsmdb}/engine/AggregationEngine.ts +1 -1
- package/ts/tsmdb/engine/IndexEngine.ts +798 -0
- package/ts/{congodb → tsmdb}/engine/QueryEngine.ts +1 -1
- package/ts/tsmdb/engine/QueryPlanner.ts +393 -0
- package/ts/tsmdb/engine/SessionEngine.ts +292 -0
- package/ts/{congodb → tsmdb}/engine/TransactionEngine.ts +12 -12
- package/ts/{congodb → tsmdb}/engine/UpdateEngine.ts +1 -1
- package/ts/{congodb/errors/CongoErrors.ts → tsmdb/errors/TsmdbErrors.ts} +34 -34
- package/ts/{congodb → tsmdb}/index.ts +16 -7
- package/ts/{congodb → tsmdb}/server/CommandRouter.ts +114 -5
- package/ts/{congodb/server/CongoServer.ts → tsmdb/server/TsmdbServer.ts} +11 -8
- package/ts/{congodb → tsmdb}/server/WireProtocol.ts +1 -1
- package/ts/{congodb → tsmdb}/server/handlers/AdminHandler.ts +116 -11
- package/ts/{congodb → tsmdb}/server/handlers/AggregateHandler.ts +1 -1
- package/ts/{congodb → tsmdb}/server/handlers/DeleteHandler.ts +18 -3
- package/ts/{congodb → tsmdb}/server/handlers/FindHandler.ts +43 -14
- package/ts/{congodb → tsmdb}/server/handlers/HelloHandler.ts +1 -1
- package/ts/{congodb → tsmdb}/server/handlers/IndexHandler.ts +1 -1
- package/ts/{congodb → tsmdb}/server/handlers/InsertHandler.ts +7 -1
- package/ts/{congodb → tsmdb}/server/handlers/UpdateHandler.ts +34 -5
- package/ts/{congodb → tsmdb}/server/index.ts +2 -2
- package/ts/{congodb → tsmdb}/storage/FileStorageAdapter.ts +90 -7
- package/ts/{congodb → tsmdb}/storage/IStorageAdapter.ts +8 -2
- package/ts/{congodb → tsmdb}/storage/MemoryStorageAdapter.ts +14 -2
- package/ts/{congodb → tsmdb}/storage/OpLog.ts +1 -1
- package/ts/tsmdb/storage/WAL.ts +375 -0
- package/ts/{congodb → tsmdb}/types/interfaces.ts +3 -3
- package/ts/tsmdb/utils/checksum.ts +88 -0
- package/ts/tsmdb/utils/index.ts +1 -0
- package/dist_ts/congodb/congodb.plugins.js +0 -14
- package/dist_ts/congodb/engine/AggregationEngine.js +0 -189
- package/dist_ts/congodb/engine/IndexEngine.js +0 -376
- package/dist_ts/congodb/engine/QueryEngine.js +0 -271
- package/dist_ts/congodb/engine/TransactionEngine.js +0 -287
- package/dist_ts/congodb/engine/UpdateEngine.js +0 -461
- package/dist_ts/congodb/errors/CongoErrors.js +0 -155
- package/dist_ts/congodb/index.js +0 -26
- package/dist_ts/congodb/server/CommandRouter.d.ts +0 -51
- package/dist_ts/congodb/server/CommandRouter.js +0 -132
- package/dist_ts/congodb/server/CongoServer.js +0 -227
- package/dist_ts/congodb/server/WireProtocol.js +0 -298
- package/dist_ts/congodb/server/handlers/AdminHandler.js +0 -568
- package/dist_ts/congodb/server/handlers/AggregateHandler.js +0 -277
- package/dist_ts/congodb/server/handlers/DeleteHandler.js +0 -83
- package/dist_ts/congodb/server/handlers/FindHandler.js +0 -261
- package/dist_ts/congodb/server/handlers/IndexHandler.js +0 -183
- package/dist_ts/congodb/server/handlers/InsertHandler.js +0 -76
- package/dist_ts/congodb/server/handlers/UpdateHandler.js +0 -270
- package/dist_ts/congodb/server/handlers/index.js +0 -10
- package/dist_ts/congodb/server/index.js +0 -7
- package/dist_ts/congodb/storage/FileStorageAdapter.js +0 -396
- package/dist_ts/congodb/storage/MemoryStorageAdapter.js +0 -367
- package/dist_ts/congodb/storage/OpLog.js +0 -221
- package/ts/congodb/engine/IndexEngine.ts +0 -479
- /package/dist_ts/{congodb → tsmdb}/engine/AggregationEngine.d.ts +0 -0
- /package/dist_ts/{congodb → tsmdb}/engine/QueryEngine.d.ts +0 -0
- /package/dist_ts/{congodb → tsmdb}/engine/UpdateEngine.d.ts +0 -0
- /package/dist_ts/{congodb → tsmdb}/server/handlers/index.d.ts +0 -0
- /package/dist_ts/{congodb/congodb.plugins.d.ts → tsmdb/tsmdb.plugins.d.ts} +0 -0
- /package/ts/{congodb → tsmdb}/server/handlers/index.ts +0 -0
- /package/ts/{congodb/congodb.plugins.ts → tsmdb/tsmdb.plugins.ts} +0 -0
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
import * as plugins from '../congodb.plugins.js';
|
|
2
|
-
// Import mingo Aggregator
|
|
3
|
-
import { Aggregator } from 'mingo';
|
|
4
|
-
/**
|
|
5
|
-
* Aggregation engine using mingo for MongoDB-compatible aggregation pipeline execution
|
|
6
|
-
*/
|
|
7
|
-
export class AggregationEngine {
|
|
8
|
-
/**
|
|
9
|
-
* Execute an aggregation pipeline on a collection of documents
|
|
10
|
-
*/
|
|
11
|
-
static aggregate(documents, pipeline, options) {
|
|
12
|
-
if (!pipeline || pipeline.length === 0) {
|
|
13
|
-
return documents;
|
|
14
|
-
}
|
|
15
|
-
// Create mingo aggregator with the pipeline
|
|
16
|
-
const aggregator = new Aggregator(pipeline, {
|
|
17
|
-
collation: options?.collation,
|
|
18
|
-
});
|
|
19
|
-
// Run the aggregation
|
|
20
|
-
const result = aggregator.run(documents);
|
|
21
|
-
return Array.isArray(result) ? result : [];
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Execute aggregation and return an iterator for lazy evaluation
|
|
25
|
-
*/
|
|
26
|
-
static *aggregateIterator(documents, pipeline, options) {
|
|
27
|
-
const aggregator = new Aggregator(pipeline, {
|
|
28
|
-
collation: options?.collation,
|
|
29
|
-
});
|
|
30
|
-
// Get the cursor from mingo
|
|
31
|
-
const cursor = aggregator.stream(documents);
|
|
32
|
-
for (const doc of cursor) {
|
|
33
|
-
yield doc;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Execute a $lookup stage manually (for cross-collection lookups)
|
|
38
|
-
* This is used when the lookup references another collection in the same database
|
|
39
|
-
*/
|
|
40
|
-
static executeLookup(documents, lookupSpec, foreignCollection) {
|
|
41
|
-
const { localField, foreignField, as } = lookupSpec;
|
|
42
|
-
return documents.map(doc => {
|
|
43
|
-
const localValue = this.getNestedValue(doc, localField);
|
|
44
|
-
const matches = foreignCollection.filter(foreignDoc => {
|
|
45
|
-
const foreignValue = this.getNestedValue(foreignDoc, foreignField);
|
|
46
|
-
return this.valuesMatch(localValue, foreignValue);
|
|
47
|
-
});
|
|
48
|
-
return {
|
|
49
|
-
...doc,
|
|
50
|
-
[as]: matches,
|
|
51
|
-
};
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Execute a $graphLookup stage manually
|
|
56
|
-
*/
|
|
57
|
-
static executeGraphLookup(documents, graphLookupSpec, foreignCollection) {
|
|
58
|
-
const { startWith, connectFromField, connectToField, as, maxDepth = 10, depthField, restrictSearchWithMatch, } = graphLookupSpec;
|
|
59
|
-
return documents.map(doc => {
|
|
60
|
-
const startValue = typeof startWith === 'string' && startWith.startsWith('$')
|
|
61
|
-
? this.getNestedValue(doc, startWith.slice(1))
|
|
62
|
-
: startWith;
|
|
63
|
-
const results = [];
|
|
64
|
-
const visited = new Set();
|
|
65
|
-
const queue = [];
|
|
66
|
-
// Initialize with start value(s)
|
|
67
|
-
const startValues = Array.isArray(startValue) ? startValue : [startValue];
|
|
68
|
-
for (const val of startValues) {
|
|
69
|
-
queue.push({ value: val, depth: 0 });
|
|
70
|
-
}
|
|
71
|
-
while (queue.length > 0) {
|
|
72
|
-
const { value, depth } = queue.shift();
|
|
73
|
-
if (depth > maxDepth)
|
|
74
|
-
continue;
|
|
75
|
-
const valueKey = JSON.stringify(value);
|
|
76
|
-
if (visited.has(valueKey))
|
|
77
|
-
continue;
|
|
78
|
-
visited.add(valueKey);
|
|
79
|
-
// Find matching documents
|
|
80
|
-
for (const foreignDoc of foreignCollection) {
|
|
81
|
-
const foreignValue = this.getNestedValue(foreignDoc, connectToField);
|
|
82
|
-
if (this.valuesMatch(value, foreignValue)) {
|
|
83
|
-
// Check restrictSearchWithMatch
|
|
84
|
-
if (restrictSearchWithMatch) {
|
|
85
|
-
const matchQuery = new plugins.mingo.Query(restrictSearchWithMatch);
|
|
86
|
-
if (!matchQuery.test(foreignDoc))
|
|
87
|
-
continue;
|
|
88
|
-
}
|
|
89
|
-
const resultDoc = depthField
|
|
90
|
-
? { ...foreignDoc, [depthField]: depth }
|
|
91
|
-
: { ...foreignDoc };
|
|
92
|
-
// Avoid duplicates in results
|
|
93
|
-
const docKey = foreignDoc._id.toHexString();
|
|
94
|
-
if (!results.some(r => r._id?.toHexString?.() === docKey)) {
|
|
95
|
-
results.push(resultDoc);
|
|
96
|
-
// Add connected values to queue
|
|
97
|
-
const nextValue = this.getNestedValue(foreignDoc, connectFromField);
|
|
98
|
-
if (nextValue !== undefined) {
|
|
99
|
-
const nextValues = Array.isArray(nextValue) ? nextValue : [nextValue];
|
|
100
|
-
for (const nv of nextValues) {
|
|
101
|
-
queue.push({ value: nv, depth: depth + 1 });
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
return {
|
|
109
|
-
...doc,
|
|
110
|
-
[as]: results,
|
|
111
|
-
};
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Execute a $facet stage manually
|
|
116
|
-
*/
|
|
117
|
-
static executeFacet(documents, facetSpec) {
|
|
118
|
-
const result = {};
|
|
119
|
-
for (const [facetName, pipeline] of Object.entries(facetSpec)) {
|
|
120
|
-
result[facetName] = this.aggregate(documents, pipeline);
|
|
121
|
-
}
|
|
122
|
-
return result;
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* Execute a $unionWith stage
|
|
126
|
-
*/
|
|
127
|
-
static executeUnionWith(documents, otherDocuments, pipeline) {
|
|
128
|
-
let unionDocs = otherDocuments;
|
|
129
|
-
if (pipeline && pipeline.length > 0) {
|
|
130
|
-
unionDocs = this.aggregate(otherDocuments, pipeline);
|
|
131
|
-
}
|
|
132
|
-
return [...documents, ...unionDocs];
|
|
133
|
-
}
|
|
134
|
-
/**
|
|
135
|
-
* Execute a $merge stage (output to another collection)
|
|
136
|
-
* Returns the documents that would be inserted/updated
|
|
137
|
-
*/
|
|
138
|
-
static prepareMerge(documents, mergeSpec) {
|
|
139
|
-
const onField = mergeSpec.on || '_id';
|
|
140
|
-
const whenMatched = mergeSpec.whenMatched || 'merge';
|
|
141
|
-
const whenNotMatched = mergeSpec.whenNotMatched || 'insert';
|
|
142
|
-
return {
|
|
143
|
-
toInsert: [],
|
|
144
|
-
toUpdate: [],
|
|
145
|
-
onField,
|
|
146
|
-
whenMatched,
|
|
147
|
-
whenNotMatched,
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
// ============================================================================
|
|
151
|
-
// Helper Methods
|
|
152
|
-
// ============================================================================
|
|
153
|
-
static getNestedValue(obj, path) {
|
|
154
|
-
const parts = path.split('.');
|
|
155
|
-
let current = obj;
|
|
156
|
-
for (const part of parts) {
|
|
157
|
-
if (current === null || current === undefined) {
|
|
158
|
-
return undefined;
|
|
159
|
-
}
|
|
160
|
-
current = current[part];
|
|
161
|
-
}
|
|
162
|
-
return current;
|
|
163
|
-
}
|
|
164
|
-
static valuesMatch(a, b) {
|
|
165
|
-
if (a === b)
|
|
166
|
-
return true;
|
|
167
|
-
// Handle ObjectId comparison
|
|
168
|
-
if (a instanceof plugins.bson.ObjectId && b instanceof plugins.bson.ObjectId) {
|
|
169
|
-
return a.equals(b);
|
|
170
|
-
}
|
|
171
|
-
// Handle array contains check
|
|
172
|
-
if (Array.isArray(a)) {
|
|
173
|
-
return a.some(item => this.valuesMatch(item, b));
|
|
174
|
-
}
|
|
175
|
-
if (Array.isArray(b)) {
|
|
176
|
-
return b.some(item => this.valuesMatch(a, item));
|
|
177
|
-
}
|
|
178
|
-
// Handle Date comparison
|
|
179
|
-
if (a instanceof Date && b instanceof Date) {
|
|
180
|
-
return a.getTime() === b.getTime();
|
|
181
|
-
}
|
|
182
|
-
// Handle object comparison
|
|
183
|
-
if (typeof a === 'object' && typeof b === 'object' && a !== null && b !== null) {
|
|
184
|
-
return JSON.stringify(a) === JSON.stringify(b);
|
|
185
|
-
}
|
|
186
|
-
return false;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQWdncmVnYXRpb25FbmdpbmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9jb25nb2RiL2VuZ2luZS9BZ2dyZWdhdGlvbkVuZ2luZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHVCQUF1QixDQUFDO0FBR2pELDBCQUEwQjtBQUMxQixPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sT0FBTyxDQUFDO0FBRW5DOztHQUVHO0FBQ0gsTUFBTSxPQUFPLGlCQUFpQjtJQUM1Qjs7T0FFRztJQUNILE1BQU0sQ0FBQyxTQUFTLENBQ2QsU0FBNEIsRUFDNUIsUUFBb0IsRUFDcEIsT0FBMkI7UUFFM0IsSUFBSSxDQUFDLFFBQVEsSUFBSSxRQUFRLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQ3ZDLE9BQU8sU0FBUyxDQUFDO1FBQ25CLENBQUM7UUFFRCw0Q0FBNEM7UUFDNUMsTUFBTSxVQUFVLEdBQUcsSUFBSSxVQUFVLENBQUMsUUFBUSxFQUFFO1lBQzFDLFNBQVMsRUFBRSxPQUFPLEVBQUUsU0FBZ0I7U0FDckMsQ0FBQyxDQUFDO1FBRUgsc0JBQXNCO1FBQ3RCLE1BQU0sTUFBTSxHQUFHLFVBQVUsQ0FBQyxHQUFHLENBQUMsU0FBUyxDQUFDLENBQUM7UUFFekMsT0FBTyxLQUFLLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztJQUM3QyxDQUFDO0lBRUQ7O09BRUc7SUFDSCxNQUFNLENBQUMsQ0FBQyxpQkFBaUIsQ0FDdkIsU0FBNEIsRUFDNUIsUUFBb0IsRUFDcEIsT0FBMkI7UUFFM0IsTUFBTSxVQUFVLEdBQUcsSUFBSSxVQUFVLENBQUMsUUFBUSxFQUFFO1lBQzFDLFNBQVMsRUFBRSxPQUFPLEVBQUUsU0FBZ0I7U0FDckMsQ0FBQyxDQUFDO1FBRUgsNEJBQTRCO1FBQzVCLE1BQU0sTUFBTSxHQUFH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
|
|
@@ -1,376 +0,0 @@
|
|
|
1
|
-
import * as plugins from '../congodb.plugins.js';
|
|
2
|
-
import { CongoDuplicateKeyError, CongoIndexError } from '../errors/CongoErrors.js';
|
|
3
|
-
import { QueryEngine } from './QueryEngine.js';
|
|
4
|
-
/**
|
|
5
|
-
* Index engine for managing indexes and query optimization
|
|
6
|
-
*/
|
|
7
|
-
export class IndexEngine {
|
|
8
|
-
dbName;
|
|
9
|
-
collName;
|
|
10
|
-
storage;
|
|
11
|
-
indexes = new Map();
|
|
12
|
-
initialized = false;
|
|
13
|
-
constructor(dbName, collName, storage) {
|
|
14
|
-
this.dbName = dbName;
|
|
15
|
-
this.collName = collName;
|
|
16
|
-
this.storage = storage;
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Initialize indexes from storage
|
|
20
|
-
*/
|
|
21
|
-
async initialize() {
|
|
22
|
-
if (this.initialized)
|
|
23
|
-
return;
|
|
24
|
-
const storedIndexes = await this.storage.getIndexes(this.dbName, this.collName);
|
|
25
|
-
const documents = await this.storage.findAll(this.dbName, this.collName);
|
|
26
|
-
for (const indexSpec of storedIndexes) {
|
|
27
|
-
const indexData = {
|
|
28
|
-
name: indexSpec.name,
|
|
29
|
-
key: indexSpec.key,
|
|
30
|
-
unique: indexSpec.unique || false,
|
|
31
|
-
sparse: indexSpec.sparse || false,
|
|
32
|
-
expireAfterSeconds: indexSpec.expireAfterSeconds,
|
|
33
|
-
entries: new Map(),
|
|
34
|
-
};
|
|
35
|
-
// Build index entries
|
|
36
|
-
for (const doc of documents) {
|
|
37
|
-
const keyValue = this.extractKeyValue(doc, indexSpec.key);
|
|
38
|
-
if (keyValue !== null || !indexData.sparse) {
|
|
39
|
-
const keyStr = JSON.stringify(keyValue);
|
|
40
|
-
if (!indexData.entries.has(keyStr)) {
|
|
41
|
-
indexData.entries.set(keyStr, new Set());
|
|
42
|
-
}
|
|
43
|
-
indexData.entries.get(keyStr).add(doc._id.toHexString());
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
this.indexes.set(indexSpec.name, indexData);
|
|
47
|
-
}
|
|
48
|
-
this.initialized = true;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Create a new index
|
|
52
|
-
*/
|
|
53
|
-
async createIndex(key, options) {
|
|
54
|
-
await this.initialize();
|
|
55
|
-
// Generate index name if not provided
|
|
56
|
-
const name = options?.name || this.generateIndexName(key);
|
|
57
|
-
// Check if index already exists
|
|
58
|
-
if (this.indexes.has(name)) {
|
|
59
|
-
return name;
|
|
60
|
-
}
|
|
61
|
-
// Create index data structure
|
|
62
|
-
const indexData = {
|
|
63
|
-
name,
|
|
64
|
-
key: key,
|
|
65
|
-
unique: options?.unique || false,
|
|
66
|
-
sparse: options?.sparse || false,
|
|
67
|
-
expireAfterSeconds: options?.expireAfterSeconds,
|
|
68
|
-
entries: new Map(),
|
|
69
|
-
};
|
|
70
|
-
// Build index from existing documents
|
|
71
|
-
const documents = await this.storage.findAll(this.dbName, this.collName);
|
|
72
|
-
for (const doc of documents) {
|
|
73
|
-
const keyValue = this.extractKeyValue(doc, key);
|
|
74
|
-
if (keyValue === null && indexData.sparse) {
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
const keyStr = JSON.stringify(keyValue);
|
|
78
|
-
if (indexData.unique && indexData.entries.has(keyStr)) {
|
|
79
|
-
throw new CongoDuplicateKeyError(`E11000 duplicate key error index: ${this.dbName}.${this.collName}.$${name}`, key, keyValue);
|
|
80
|
-
}
|
|
81
|
-
if (!indexData.entries.has(keyStr)) {
|
|
82
|
-
indexData.entries.set(keyStr, new Set());
|
|
83
|
-
}
|
|
84
|
-
indexData.entries.get(keyStr).add(doc._id.toHexString());
|
|
85
|
-
}
|
|
86
|
-
// Store index
|
|
87
|
-
this.indexes.set(name, indexData);
|
|
88
|
-
await this.storage.saveIndex(this.dbName, this.collName, name, {
|
|
89
|
-
key,
|
|
90
|
-
unique: options?.unique,
|
|
91
|
-
sparse: options?.sparse,
|
|
92
|
-
expireAfterSeconds: options?.expireAfterSeconds,
|
|
93
|
-
});
|
|
94
|
-
return name;
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Drop an index
|
|
98
|
-
*/
|
|
99
|
-
async dropIndex(name) {
|
|
100
|
-
await this.initialize();
|
|
101
|
-
if (name === '_id_') {
|
|
102
|
-
throw new CongoIndexError('cannot drop _id index');
|
|
103
|
-
}
|
|
104
|
-
if (!this.indexes.has(name)) {
|
|
105
|
-
throw new CongoIndexError(`index not found: ${name}`);
|
|
106
|
-
}
|
|
107
|
-
this.indexes.delete(name);
|
|
108
|
-
await this.storage.dropIndex(this.dbName, this.collName, name);
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Drop all indexes except _id
|
|
112
|
-
*/
|
|
113
|
-
async dropAllIndexes() {
|
|
114
|
-
await this.initialize();
|
|
115
|
-
const names = Array.from(this.indexes.keys()).filter(n => n !== '_id_');
|
|
116
|
-
for (const name of names) {
|
|
117
|
-
this.indexes.delete(name);
|
|
118
|
-
await this.storage.dropIndex(this.dbName, this.collName, name);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* List all indexes
|
|
123
|
-
*/
|
|
124
|
-
async listIndexes() {
|
|
125
|
-
await this.initialize();
|
|
126
|
-
return Array.from(this.indexes.values()).map(idx => ({
|
|
127
|
-
v: 2,
|
|
128
|
-
key: idx.key,
|
|
129
|
-
name: idx.name,
|
|
130
|
-
unique: idx.unique || undefined,
|
|
131
|
-
sparse: idx.sparse || undefined,
|
|
132
|
-
expireAfterSeconds: idx.expireAfterSeconds,
|
|
133
|
-
}));
|
|
134
|
-
}
|
|
135
|
-
/**
|
|
136
|
-
* Check if an index exists
|
|
137
|
-
*/
|
|
138
|
-
async indexExists(name) {
|
|
139
|
-
await this.initialize();
|
|
140
|
-
return this.indexes.has(name);
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Update index entries after document insert
|
|
144
|
-
*/
|
|
145
|
-
async onInsert(doc) {
|
|
146
|
-
await this.initialize();
|
|
147
|
-
for (const [name, indexData] of this.indexes) {
|
|
148
|
-
const keyValue = this.extractKeyValue(doc, indexData.key);
|
|
149
|
-
if (keyValue === null && indexData.sparse) {
|
|
150
|
-
continue;
|
|
151
|
-
}
|
|
152
|
-
const keyStr = JSON.stringify(keyValue);
|
|
153
|
-
// Check unique constraint
|
|
154
|
-
if (indexData.unique) {
|
|
155
|
-
const existing = indexData.entries.get(keyStr);
|
|
156
|
-
if (existing && existing.size > 0) {
|
|
157
|
-
throw new CongoDuplicateKeyError(`E11000 duplicate key error collection: ${this.dbName}.${this.collName} index: ${name}`, indexData.key, keyValue);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
if (!indexData.entries.has(keyStr)) {
|
|
161
|
-
indexData.entries.set(keyStr, new Set());
|
|
162
|
-
}
|
|
163
|
-
indexData.entries.get(keyStr).add(doc._id.toHexString());
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Update index entries after document update
|
|
168
|
-
*/
|
|
169
|
-
async onUpdate(oldDoc, newDoc) {
|
|
170
|
-
await this.initialize();
|
|
171
|
-
for (const [name, indexData] of this.indexes) {
|
|
172
|
-
const oldKeyValue = this.extractKeyValue(oldDoc, indexData.key);
|
|
173
|
-
const newKeyValue = this.extractKeyValue(newDoc, indexData.key);
|
|
174
|
-
const oldKeyStr = JSON.stringify(oldKeyValue);
|
|
175
|
-
const newKeyStr = JSON.stringify(newKeyValue);
|
|
176
|
-
// Remove old entry if key changed
|
|
177
|
-
if (oldKeyStr !== newKeyStr) {
|
|
178
|
-
if (oldKeyValue !== null || !indexData.sparse) {
|
|
179
|
-
const oldSet = indexData.entries.get(oldKeyStr);
|
|
180
|
-
if (oldSet) {
|
|
181
|
-
oldSet.delete(oldDoc._id.toHexString());
|
|
182
|
-
if (oldSet.size === 0) {
|
|
183
|
-
indexData.entries.delete(oldKeyStr);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
// Add new entry
|
|
188
|
-
if (newKeyValue !== null || !indexData.sparse) {
|
|
189
|
-
// Check unique constraint
|
|
190
|
-
if (indexData.unique) {
|
|
191
|
-
const existing = indexData.entries.get(newKeyStr);
|
|
192
|
-
if (existing && existing.size > 0) {
|
|
193
|
-
throw new CongoDuplicateKeyError(`E11000 duplicate key error collection: ${this.dbName}.${this.collName} index: ${name}`, indexData.key, newKeyValue);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
if (!indexData.entries.has(newKeyStr)) {
|
|
197
|
-
indexData.entries.set(newKeyStr, new Set());
|
|
198
|
-
}
|
|
199
|
-
indexData.entries.get(newKeyStr).add(newDoc._id.toHexString());
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* Update index entries after document delete
|
|
206
|
-
*/
|
|
207
|
-
async onDelete(doc) {
|
|
208
|
-
await this.initialize();
|
|
209
|
-
for (const indexData of this.indexes.values()) {
|
|
210
|
-
const keyValue = this.extractKeyValue(doc, indexData.key);
|
|
211
|
-
if (keyValue === null && indexData.sparse) {
|
|
212
|
-
continue;
|
|
213
|
-
}
|
|
214
|
-
const keyStr = JSON.stringify(keyValue);
|
|
215
|
-
const set = indexData.entries.get(keyStr);
|
|
216
|
-
if (set) {
|
|
217
|
-
set.delete(doc._id.toHexString());
|
|
218
|
-
if (set.size === 0) {
|
|
219
|
-
indexData.entries.delete(keyStr);
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* Find the best index for a query
|
|
226
|
-
*/
|
|
227
|
-
selectIndex(filter) {
|
|
228
|
-
if (!filter || Object.keys(filter).length === 0) {
|
|
229
|
-
return null;
|
|
230
|
-
}
|
|
231
|
-
// Get filter fields
|
|
232
|
-
const filterFields = new Set(this.getFilterFields(filter));
|
|
233
|
-
// Score each index
|
|
234
|
-
let bestIndex = null;
|
|
235
|
-
let bestScore = 0;
|
|
236
|
-
for (const [name, indexData] of this.indexes) {
|
|
237
|
-
const indexFields = Object.keys(indexData.key);
|
|
238
|
-
let score = 0;
|
|
239
|
-
// Count how many index fields are in the filter
|
|
240
|
-
for (const field of indexFields) {
|
|
241
|
-
if (filterFields.has(field)) {
|
|
242
|
-
score++;
|
|
243
|
-
}
|
|
244
|
-
else {
|
|
245
|
-
break; // Index fields must be contiguous
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
// Prefer unique indexes
|
|
249
|
-
if (indexData.unique && score > 0) {
|
|
250
|
-
score += 0.5;
|
|
251
|
-
}
|
|
252
|
-
if (score > bestScore) {
|
|
253
|
-
bestScore = score;
|
|
254
|
-
bestIndex = { name, data: indexData };
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
return bestIndex;
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* Use index to find candidate document IDs
|
|
261
|
-
*/
|
|
262
|
-
async findCandidateIds(filter) {
|
|
263
|
-
await this.initialize();
|
|
264
|
-
const index = this.selectIndex(filter);
|
|
265
|
-
if (!index)
|
|
266
|
-
return null;
|
|
267
|
-
// Try to use the index for equality matches
|
|
268
|
-
const indexFields = Object.keys(index.data.key);
|
|
269
|
-
const equalityValues = {};
|
|
270
|
-
for (const field of indexFields) {
|
|
271
|
-
const filterValue = this.getFilterValue(filter, field);
|
|
272
|
-
if (filterValue === undefined)
|
|
273
|
-
break;
|
|
274
|
-
// Only use equality matches for index lookup
|
|
275
|
-
if (typeof filterValue === 'object' && filterValue !== null) {
|
|
276
|
-
if (filterValue.$eq !== undefined) {
|
|
277
|
-
equalityValues[field] = filterValue.$eq;
|
|
278
|
-
}
|
|
279
|
-
else if (filterValue.$in !== undefined) {
|
|
280
|
-
// Handle $in with multiple lookups
|
|
281
|
-
const results = new Set();
|
|
282
|
-
for (const val of filterValue.$in) {
|
|
283
|
-
equalityValues[field] = val;
|
|
284
|
-
const keyStr = JSON.stringify(this.buildKeyValue(equalityValues, index.data.key));
|
|
285
|
-
const ids = index.data.entries.get(keyStr);
|
|
286
|
-
if (ids) {
|
|
287
|
-
for (const id of ids) {
|
|
288
|
-
results.add(id);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
return results;
|
|
293
|
-
}
|
|
294
|
-
else {
|
|
295
|
-
break; // Non-equality operator, stop here
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
else {
|
|
299
|
-
equalityValues[field] = filterValue;
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
if (Object.keys(equalityValues).length === 0) {
|
|
303
|
-
return null;
|
|
304
|
-
}
|
|
305
|
-
const keyStr = JSON.stringify(this.buildKeyValue(equalityValues, index.data.key));
|
|
306
|
-
return index.data.entries.get(keyStr) || new Set();
|
|
307
|
-
}
|
|
308
|
-
// ============================================================================
|
|
309
|
-
// Helper Methods
|
|
310
|
-
// ============================================================================
|
|
311
|
-
generateIndexName(key) {
|
|
312
|
-
return Object.entries(key)
|
|
313
|
-
.map(([field, dir]) => `${field}_${dir}`)
|
|
314
|
-
.join('_');
|
|
315
|
-
}
|
|
316
|
-
extractKeyValue(doc, key) {
|
|
317
|
-
const values = [];
|
|
318
|
-
for (const field of Object.keys(key)) {
|
|
319
|
-
const value = QueryEngine.getNestedValue(doc, field);
|
|
320
|
-
values.push(value === undefined ? null : value);
|
|
321
|
-
}
|
|
322
|
-
// For single-field index, return the value directly
|
|
323
|
-
if (values.length === 1) {
|
|
324
|
-
return values[0];
|
|
325
|
-
}
|
|
326
|
-
return values;
|
|
327
|
-
}
|
|
328
|
-
buildKeyValue(values, key) {
|
|
329
|
-
const result = [];
|
|
330
|
-
for (const field of Object.keys(key)) {
|
|
331
|
-
result.push(values[field] !== undefined ? values[field] : null);
|
|
332
|
-
}
|
|
333
|
-
if (result.length === 1) {
|
|
334
|
-
return result[0];
|
|
335
|
-
}
|
|
336
|
-
return result;
|
|
337
|
-
}
|
|
338
|
-
getFilterFields(filter, prefix = '') {
|
|
339
|
-
const fields = [];
|
|
340
|
-
for (const [key, value] of Object.entries(filter)) {
|
|
341
|
-
if (key.startsWith('$')) {
|
|
342
|
-
// Logical operator
|
|
343
|
-
if (key === '$and' || key === '$or' || key === '$nor') {
|
|
344
|
-
for (const subFilter of value) {
|
|
345
|
-
fields.push(...this.getFilterFields(subFilter, prefix));
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
else {
|
|
350
|
-
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
351
|
-
fields.push(fullKey);
|
|
352
|
-
// Check for nested filters
|
|
353
|
-
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
354
|
-
const subKeys = Object.keys(value);
|
|
355
|
-
if (subKeys.length > 0 && !subKeys[0].startsWith('$')) {
|
|
356
|
-
fields.push(...this.getFilterFields(value, fullKey));
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
return fields;
|
|
362
|
-
}
|
|
363
|
-
getFilterValue(filter, field) {
|
|
364
|
-
// Handle dot notation
|
|
365
|
-
const parts = field.split('.');
|
|
366
|
-
let current = filter;
|
|
367
|
-
for (const part of parts) {
|
|
368
|
-
if (current === null || current === undefined) {
|
|
369
|
-
return undefined;
|
|
370
|
-
}
|
|
371
|
-
current = current[part];
|
|
372
|
-
}
|
|
373
|
-
return current;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiSW5kZXhFbmdpbmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi90cy9jb25nb2RiL2VuZ2luZS9JbmRleEVuZ2luZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLHVCQUF1QixDQUFDO0FBU2pELE9BQU8sRUFBRSxzQkFBc0IsRUFBRSxlQUFlLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUNuRixPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFlL0M7O0dBRUc7QUFDSCxNQUFNLE9BQU8sV0FBVztJQUNkLE1BQU0sQ0FBUztJQUNmLFFBQVEsQ0FBUztJQUNqQixPQUFPLENBQWtCO0lBQ3pCLE9BQU8sR0FBNEIsSUFBSSxHQUFHLEVBQUUsQ0FBQztJQUM3QyxXQUFXLEdBQUcsS0FBSyxDQUFDO0lBRTVCLFlBQVksTUFBYyxFQUFFLFFBQWdCLEVBQUUsT0FBd0I7UUFDcEUsSUFBSSxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUM7UUFDckIsSUFBSSxDQUFDLFFBQVEsR0FBRyxRQUFRLENBQUM7UUFDekIsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7SUFDekIsQ0FBQztJQUVEOztPQUVHO0lBQ0gsS0FBSyxDQUFDLFVBQVU7UUFDZCxJQUFJLElBQUksQ0FBQyxXQUFXO1lBQUUsT0FBTztRQUU3QixNQUFNLGFBQWEsR0FBRyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxNQUFNLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1FBQ2hGLE1BQU0sU0FBUyxHQUFHLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUM7UUFFekUsS0FBSyxNQUFNLFNBQVMsSUFBSSxhQUFhLEVBQUUsQ0FBQztZQUN0QyxNQUFNLFNBQVMsR0FBZTtnQkFD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
|