@joystick.js/db-canary 0.0.0-canary.2251 → 0.0.0-canary.2252
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/client/database.js +1 -1
- package/dist/client/index.js +1 -1
- package/dist/server/cluster/master.js +4 -4
- package/dist/server/cluster/worker.js +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/lib/auto_index_manager.js +1 -1
- package/dist/server/lib/backup_manager.js +1 -1
- package/dist/server/lib/index_manager.js +1 -1
- package/dist/server/lib/operation_dispatcher.js +1 -1
- package/dist/server/lib/operations/admin.js +1 -1
- package/dist/server/lib/operations/bulk_write.js +1 -1
- package/dist/server/lib/operations/create_index.js +1 -1
- package/dist/server/lib/operations/delete_many.js +1 -1
- package/dist/server/lib/operations/delete_one.js +1 -1
- package/dist/server/lib/operations/find.js +1 -1
- package/dist/server/lib/operations/find_one.js +1 -1
- package/dist/server/lib/operations/insert_one.js +1 -1
- package/dist/server/lib/operations/update_one.js +1 -1
- package/dist/server/lib/send_response.js +1 -1
- package/dist/server/lib/tcp_protocol.js +1 -1
- package/package.json +2 -2
- package/src/client/database.js +92 -119
- package/src/client/index.js +279 -345
- package/src/server/cluster/master.js +265 -156
- package/src/server/cluster/worker.js +26 -18
- package/src/server/index.js +553 -330
- package/src/server/lib/auto_index_manager.js +85 -23
- package/src/server/lib/backup_manager.js +117 -70
- package/src/server/lib/index_manager.js +63 -25
- package/src/server/lib/operation_dispatcher.js +339 -168
- package/src/server/lib/operations/admin.js +343 -205
- package/src/server/lib/operations/bulk_write.js +458 -194
- package/src/server/lib/operations/create_index.js +127 -34
- package/src/server/lib/operations/delete_many.js +204 -67
- package/src/server/lib/operations/delete_one.js +164 -52
- package/src/server/lib/operations/find.js +552 -319
- package/src/server/lib/operations/find_one.js +530 -304
- package/src/server/lib/operations/insert_one.js +147 -52
- package/src/server/lib/operations/update_one.js +334 -93
- package/src/server/lib/send_response.js +37 -17
- package/src/server/lib/tcp_protocol.js +158 -53
- package/test_data_api_key_1758233848259_cglfjzhou/data.mdb +0 -0
- package/test_data_api_key_1758233848259_cglfjzhou/lock.mdb +0 -0
- package/test_data_api_key_1758233848502_urlje2utd/data.mdb +0 -0
- package/test_data_api_key_1758233848502_urlje2utd/lock.mdb +0 -0
- package/test_data_api_key_1758233848738_mtcpfe5ns/data.mdb +0 -0
- package/test_data_api_key_1758233848738_mtcpfe5ns/lock.mdb +0 -0
- package/test_data_api_key_1758233848856_9g97p6gag/data.mdb +0 -0
- package/test_data_api_key_1758233848856_9g97p6gag/lock.mdb +0 -0
- package/test_data_api_key_1758233857008_0tl9zzhj8/data.mdb +0 -0
- package/test_data_api_key_1758233857008_0tl9zzhj8/lock.mdb +0 -0
- package/test_data_api_key_1758233857120_60c2f2uhu/data.mdb +0 -0
- package/test_data_api_key_1758233857120_60c2f2uhu/lock.mdb +0 -0
- package/test_data_api_key_1758233857232_aw7fkqgd9/data.mdb +0 -0
- package/test_data_api_key_1758233857232_aw7fkqgd9/lock.mdb +0 -0
- package/test_data_api_key_1758234881285_4aeflubjb/data.mdb +0 -0
- package/test_data_api_key_1758234881285_4aeflubjb/lock.mdb +0 -0
- package/test_data_api_key_1758234881520_kb0amvtqb/data.mdb +0 -0
- package/test_data_api_key_1758234881520_kb0amvtqb/lock.mdb +0 -0
- package/test_data_api_key_1758234881756_k04gfv2va/data.mdb +0 -0
- package/test_data_api_key_1758234881756_k04gfv2va/lock.mdb +0 -0
- package/test_data_api_key_1758234881876_wn90dpo1z/data.mdb +0 -0
- package/test_data_api_key_1758234881876_wn90dpo1z/lock.mdb +0 -0
- package/test_data_api_key_1758234889461_26xz3dmbr/data.mdb +0 -0
- package/test_data_api_key_1758234889461_26xz3dmbr/lock.mdb +0 -0
- package/test_data_api_key_1758234889572_uziz7e0p5/data.mdb +0 -0
- package/test_data_api_key_1758234889572_uziz7e0p5/lock.mdb +0 -0
- package/test_data_api_key_1758234889684_5f9wmposh/data.mdb +0 -0
- package/test_data_api_key_1758234889684_5f9wmposh/lock.mdb +0 -0
- package/test_data_api_key_1758235657729_prwgm6mxr/data.mdb +0 -0
- package/test_data_api_key_1758235657729_prwgm6mxr/lock.mdb +0 -0
- package/test_data_api_key_1758235657961_rc2da0dc2/data.mdb +0 -0
- package/test_data_api_key_1758235657961_rc2da0dc2/lock.mdb +0 -0
- package/test_data_api_key_1758235658193_oqqxm0sny/data.mdb +0 -0
- package/test_data_api_key_1758235658193_oqqxm0sny/lock.mdb +0 -0
- package/test_data_api_key_1758235658309_vggac1pj6/data.mdb +0 -0
- package/test_data_api_key_1758235658309_vggac1pj6/lock.mdb +0 -0
- package/test_data_api_key_1758235665968_61ko07dd1/data.mdb +0 -0
- package/test_data_api_key_1758235665968_61ko07dd1/lock.mdb +0 -0
- package/test_data_api_key_1758235666082_50lrt6sq8/data.mdb +0 -0
- package/test_data_api_key_1758235666082_50lrt6sq8/lock.mdb +0 -0
- package/test_data_api_key_1758235666194_ykvauwlzh/data.mdb +0 -0
- package/test_data_api_key_1758235666194_ykvauwlzh/lock.mdb +0 -0
- package/test_data_api_key_1758236187207_9c4paeh09/data.mdb +0 -0
- package/test_data_api_key_1758236187207_9c4paeh09/lock.mdb +0 -0
- package/test_data_api_key_1758236187441_4n3o3gkkl/data.mdb +0 -0
- package/test_data_api_key_1758236187441_4n3o3gkkl/lock.mdb +0 -0
- package/test_data_api_key_1758236187672_jt6b21ye0/data.mdb +0 -0
- package/test_data_api_key_1758236187672_jt6b21ye0/lock.mdb +0 -0
- package/test_data_api_key_1758236187788_oo84fz9u6/data.mdb +0 -0
- package/test_data_api_key_1758236187788_oo84fz9u6/lock.mdb +0 -0
- package/test_data_api_key_1758236195507_o9zeznwlm/data.mdb +0 -0
- package/test_data_api_key_1758236195507_o9zeznwlm/lock.mdb +0 -0
- package/test_data_api_key_1758236195619_qsqd60y41/data.mdb +0 -0
- package/test_data_api_key_1758236195619_qsqd60y41/lock.mdb +0 -0
- package/test_data_api_key_1758236195731_im13iq284/data.mdb +0 -0
- package/test_data_api_key_1758236195731_im13iq284/lock.mdb +0 -0
|
@@ -5,6 +5,77 @@ import create_logger from '../logger.js';
|
|
|
5
5
|
|
|
6
6
|
const { create_context_logger } = create_logger('update_one');
|
|
7
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Applies $set operator to document.
|
|
10
|
+
* @param {Object} document - Document to update
|
|
11
|
+
* @param {Object} operations - Set operations
|
|
12
|
+
* @returns {Object} Updated document
|
|
13
|
+
*/
|
|
14
|
+
const apply_set_operator = (document, operations) => {
|
|
15
|
+
return { ...document, ...operations };
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Applies $unset operator to document.
|
|
20
|
+
* @param {Object} document - Document to update
|
|
21
|
+
* @param {Object} operations - Unset operations
|
|
22
|
+
* @returns {Object} Updated document
|
|
23
|
+
*/
|
|
24
|
+
const apply_unset_operator = (document, operations) => {
|
|
25
|
+
const updated_document = { ...document };
|
|
26
|
+
for (const field of Object.keys(operations)) {
|
|
27
|
+
delete updated_document[field];
|
|
28
|
+
}
|
|
29
|
+
return updated_document;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Applies $inc operator to document.
|
|
34
|
+
* @param {Object} document - Document to update
|
|
35
|
+
* @param {Object} operations - Increment operations
|
|
36
|
+
* @returns {Object} Updated document
|
|
37
|
+
*/
|
|
38
|
+
const apply_inc_operator = (document, operations) => {
|
|
39
|
+
const updated_document = { ...document };
|
|
40
|
+
for (const [field, value] of Object.entries(operations)) {
|
|
41
|
+
updated_document[field] = (updated_document[field] || 0) + value;
|
|
42
|
+
}
|
|
43
|
+
return updated_document;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Applies $push operator to document.
|
|
48
|
+
* @param {Object} document - Document to update
|
|
49
|
+
* @param {Object} operations - Push operations
|
|
50
|
+
* @returns {Object} Updated document
|
|
51
|
+
*/
|
|
52
|
+
const apply_push_operator = (document, operations) => {
|
|
53
|
+
const updated_document = { ...document };
|
|
54
|
+
for (const [field, value] of Object.entries(operations)) {
|
|
55
|
+
if (!Array.isArray(updated_document[field])) {
|
|
56
|
+
updated_document[field] = [];
|
|
57
|
+
}
|
|
58
|
+
updated_document[field] = [...updated_document[field], value];
|
|
59
|
+
}
|
|
60
|
+
return updated_document;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Applies $pull operator to document.
|
|
65
|
+
* @param {Object} document - Document to update
|
|
66
|
+
* @param {Object} operations - Pull operations
|
|
67
|
+
* @returns {Object} Updated document
|
|
68
|
+
*/
|
|
69
|
+
const apply_pull_operator = (document, operations) => {
|
|
70
|
+
const updated_document = { ...document };
|
|
71
|
+
for (const [field, value] of Object.entries(operations)) {
|
|
72
|
+
if (Array.isArray(updated_document[field])) {
|
|
73
|
+
updated_document[field] = updated_document[field].filter(item => item !== value);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return updated_document;
|
|
77
|
+
};
|
|
78
|
+
|
|
8
79
|
/**
|
|
9
80
|
* Applies MongoDB-style update operators to a document.
|
|
10
81
|
* @param {Object} document - Original document
|
|
@@ -13,41 +84,28 @@ const { create_context_logger } = create_logger('update_one');
|
|
|
13
84
|
* @throws {Error} When unsupported update operator is used
|
|
14
85
|
*/
|
|
15
86
|
const apply_update_operators = (document, update_operations) => {
|
|
16
|
-
|
|
87
|
+
let updated_document = { ...document };
|
|
17
88
|
|
|
18
89
|
for (const [operator, operations] of Object.entries(update_operations)) {
|
|
19
90
|
switch (operator) {
|
|
20
91
|
case '$set':
|
|
21
|
-
|
|
92
|
+
updated_document = apply_set_operator(updated_document, operations);
|
|
22
93
|
break;
|
|
23
94
|
|
|
24
95
|
case '$unset':
|
|
25
|
-
|
|
26
|
-
delete updated_document[field];
|
|
27
|
-
}
|
|
96
|
+
updated_document = apply_unset_operator(updated_document, operations);
|
|
28
97
|
break;
|
|
29
98
|
|
|
30
99
|
case '$inc':
|
|
31
|
-
|
|
32
|
-
updated_document[field] = (updated_document[field] || 0) + value;
|
|
33
|
-
}
|
|
100
|
+
updated_document = apply_inc_operator(updated_document, operations);
|
|
34
101
|
break;
|
|
35
102
|
|
|
36
103
|
case '$push':
|
|
37
|
-
|
|
38
|
-
if (!Array.isArray(updated_document[field])) {
|
|
39
|
-
updated_document[field] = [];
|
|
40
|
-
}
|
|
41
|
-
updated_document[field].push(value);
|
|
42
|
-
}
|
|
104
|
+
updated_document = apply_push_operator(updated_document, operations);
|
|
43
105
|
break;
|
|
44
106
|
|
|
45
107
|
case '$pull':
|
|
46
|
-
|
|
47
|
-
if (Array.isArray(updated_document[field])) {
|
|
48
|
-
updated_document[field] = updated_document[field].filter(item => item !== value);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
108
|
+
updated_document = apply_pull_operator(updated_document, operations);
|
|
51
109
|
break;
|
|
52
110
|
|
|
53
111
|
default:
|
|
@@ -58,6 +116,17 @@ const apply_update_operators = (document, update_operations) => {
|
|
|
58
116
|
return updated_document;
|
|
59
117
|
};
|
|
60
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Checks if document field matches filter value.
|
|
121
|
+
* @param {Object} document - Document to check
|
|
122
|
+
* @param {string} field - Field name
|
|
123
|
+
* @param {any} value - Expected value
|
|
124
|
+
* @returns {boolean} True if field matches value
|
|
125
|
+
*/
|
|
126
|
+
const field_matches_value = (document, field, value) => {
|
|
127
|
+
return document[field] === value;
|
|
128
|
+
};
|
|
129
|
+
|
|
61
130
|
/**
|
|
62
131
|
* Checks if a document matches the given filter criteria (simple equality check).
|
|
63
132
|
* @param {Object} document - Document to test
|
|
@@ -70,7 +139,7 @@ const matches_filter = (document, filter) => {
|
|
|
70
139
|
}
|
|
71
140
|
|
|
72
141
|
for (const [field, value] of Object.entries(filter)) {
|
|
73
|
-
if (document
|
|
142
|
+
if (!field_matches_value(document, field, value)) {
|
|
74
143
|
return false;
|
|
75
144
|
}
|
|
76
145
|
}
|
|
@@ -79,104 +148,228 @@ const matches_filter = (document, filter) => {
|
|
|
79
148
|
};
|
|
80
149
|
|
|
81
150
|
/**
|
|
82
|
-
*
|
|
83
|
-
* @param {string} database_name -
|
|
84
|
-
* @
|
|
85
|
-
* @param {Object} filter - Filter criteria to match document
|
|
86
|
-
* @param {Object} update - Update operations to apply
|
|
87
|
-
* @param {Object} [options={}] - Update options
|
|
88
|
-
* @param {boolean} [options.upsert] - Create document if not found
|
|
89
|
-
* @returns {Promise<Object>} Update result with acknowledged, matched_count, modified_count, and optional upserted_id
|
|
90
|
-
* @throws {Error} When database name, collection name, filter, or update is invalid
|
|
151
|
+
* Validates database name parameter.
|
|
152
|
+
* @param {string} database_name - Database name to validate
|
|
153
|
+
* @throws {Error} When database name is missing
|
|
91
154
|
*/
|
|
92
|
-
const
|
|
93
|
-
const log = create_context_logger();
|
|
94
|
-
|
|
155
|
+
const validate_database_name = (database_name) => {
|
|
95
156
|
if (!database_name) {
|
|
96
157
|
throw new Error('Database name is required');
|
|
97
158
|
}
|
|
98
|
-
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Validates collection name parameter.
|
|
163
|
+
* @param {string} collection_name - Collection name to validate
|
|
164
|
+
* @throws {Error} When collection name is missing
|
|
165
|
+
*/
|
|
166
|
+
const validate_collection_name = (collection_name) => {
|
|
99
167
|
if (!collection_name) {
|
|
100
168
|
throw new Error('Collection name is required');
|
|
101
169
|
}
|
|
102
|
-
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Validates filter parameter.
|
|
174
|
+
* @param {Object} filter - Filter to validate
|
|
175
|
+
* @throws {Error} When filter is invalid
|
|
176
|
+
*/
|
|
177
|
+
const validate_filter = (filter) => {
|
|
103
178
|
if (!filter || typeof filter !== 'object') {
|
|
104
179
|
throw new Error('Filter must be a valid object');
|
|
105
180
|
}
|
|
106
|
-
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Validates update parameter.
|
|
185
|
+
* @param {Object} update - Update to validate
|
|
186
|
+
* @throws {Error} When update is invalid
|
|
187
|
+
*/
|
|
188
|
+
const validate_update = (update) => {
|
|
107
189
|
if (!update || typeof update !== 'object') {
|
|
108
190
|
throw new Error('Update must be a valid object');
|
|
109
191
|
}
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Validates all update operation parameters.
|
|
196
|
+
* @param {string} database_name - Database name
|
|
197
|
+
* @param {string} collection_name - Collection name
|
|
198
|
+
* @param {Object} filter - Filter criteria
|
|
199
|
+
* @param {Object} update - Update operations
|
|
200
|
+
*/
|
|
201
|
+
const validate_update_parameters = (database_name, collection_name, filter, update) => {
|
|
202
|
+
validate_database_name(database_name);
|
|
203
|
+
validate_collection_name(collection_name);
|
|
204
|
+
validate_filter(filter);
|
|
205
|
+
validate_update(update);
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Attempts to parse document from JSON string.
|
|
210
|
+
* @param {string} value - JSON string
|
|
211
|
+
* @returns {Object|null} Parsed document or null
|
|
212
|
+
*/
|
|
213
|
+
const parse_document_safely = (value) => {
|
|
214
|
+
try {
|
|
215
|
+
return JSON.parse(value);
|
|
216
|
+
} catch (parse_error) {
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Creates current timestamp string.
|
|
223
|
+
* @returns {string} ISO timestamp string
|
|
224
|
+
*/
|
|
225
|
+
const create_current_timestamp = () => {
|
|
226
|
+
return new Date().toISOString();
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Adds updated timestamp to document.
|
|
231
|
+
* @param {Object} document - Document to update
|
|
232
|
+
* @returns {Object} Document with updated timestamp
|
|
233
|
+
*/
|
|
234
|
+
const add_updated_timestamp = (document) => {
|
|
235
|
+
return {
|
|
236
|
+
...document,
|
|
237
|
+
_updated_at: create_current_timestamp()
|
|
238
|
+
};
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Checks if document has been modified.
|
|
243
|
+
* @param {Object} original_document - Original document
|
|
244
|
+
* @param {Object} updated_document - Updated document
|
|
245
|
+
* @returns {boolean} True if document was modified
|
|
246
|
+
*/
|
|
247
|
+
const document_was_modified = (original_document, updated_document) => {
|
|
248
|
+
return JSON.stringify(original_document) !== JSON.stringify(updated_document);
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Creates new document for upsert operation.
|
|
253
|
+
* @param {Object} filter - Filter criteria
|
|
254
|
+
* @param {Object} update - Update operations
|
|
255
|
+
* @returns {Object} New document for upsert
|
|
256
|
+
*/
|
|
257
|
+
const create_upsert_document = (filter, update) => {
|
|
258
|
+
const document_id = generate_document_id();
|
|
259
|
+
const current_timestamp = create_current_timestamp();
|
|
110
260
|
|
|
111
|
-
const
|
|
261
|
+
const base_document = {
|
|
262
|
+
...filter,
|
|
263
|
+
_id: document_id,
|
|
264
|
+
_created_at: current_timestamp,
|
|
265
|
+
_updated_at: current_timestamp
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
return apply_update_operators(base_document, update);
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Processes document update within transaction.
|
|
273
|
+
* @param {Object} db - Database instance
|
|
274
|
+
* @param {string} database_name - Database name
|
|
275
|
+
* @param {string} collection_name - Collection name
|
|
276
|
+
* @param {Object} filter - Filter criteria
|
|
277
|
+
* @param {Object} update - Update operations
|
|
278
|
+
* @param {Object} options - Update options
|
|
279
|
+
* @returns {Object} Update result data
|
|
280
|
+
*/
|
|
281
|
+
const process_document_update = (db, database_name, collection_name, filter, update, options) => {
|
|
112
282
|
let matched_count = 0;
|
|
113
283
|
let modified_count = 0;
|
|
114
284
|
let upserted_id = null;
|
|
115
|
-
|
|
116
285
|
let old_document = null;
|
|
117
286
|
let new_document = null;
|
|
118
287
|
let upserted_document = null;
|
|
119
288
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
document = JSON.parse(value);
|
|
129
|
-
} catch (parse_error) {
|
|
130
|
-
// Skip documents that can't be parsed
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
if (matches_filter(document, filter)) {
|
|
135
|
-
document_found = true;
|
|
136
|
-
matched_count = 1;
|
|
137
|
-
|
|
138
|
-
const updated_document = apply_update_operators(document, update);
|
|
139
|
-
updated_document._updated_at = new Date().toISOString();
|
|
140
|
-
|
|
141
|
-
if (JSON.stringify(document) !== JSON.stringify(updated_document)) {
|
|
142
|
-
db.put(key, JSON.stringify(updated_document));
|
|
143
|
-
old_document = document;
|
|
144
|
-
new_document = updated_document;
|
|
145
|
-
modified_count = 1;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
break;
|
|
149
|
-
}
|
|
289
|
+
const collection_prefix = `${database_name}:${collection_name}:`;
|
|
290
|
+
let document_found = false;
|
|
291
|
+
|
|
292
|
+
const range = db.getRange({ start: collection_prefix, end: collection_prefix + '\xFF' });
|
|
293
|
+
for (const { key, value } of range) {
|
|
294
|
+
const document = parse_document_safely(value);
|
|
295
|
+
if (!document) {
|
|
296
|
+
continue;
|
|
150
297
|
}
|
|
151
298
|
|
|
152
|
-
if (
|
|
153
|
-
|
|
154
|
-
|
|
299
|
+
if (matches_filter(document, filter)) {
|
|
300
|
+
document_found = true;
|
|
301
|
+
matched_count = 1;
|
|
155
302
|
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
_id: document_id,
|
|
159
|
-
_created_at: new Date().toISOString(),
|
|
160
|
-
_updated_at: new Date().toISOString()
|
|
161
|
-
};
|
|
303
|
+
const updated_document = apply_update_operators(document, update);
|
|
304
|
+
const timestamped_document = add_updated_timestamp(updated_document);
|
|
162
305
|
|
|
163
|
-
|
|
164
|
-
|
|
306
|
+
if (document_was_modified(document, timestamped_document)) {
|
|
307
|
+
db.put(key, JSON.stringify(timestamped_document));
|
|
308
|
+
old_document = document;
|
|
309
|
+
new_document = timestamped_document;
|
|
310
|
+
modified_count = 1;
|
|
311
|
+
}
|
|
165
312
|
|
|
166
|
-
|
|
167
|
-
matched_count = 0;
|
|
168
|
-
modified_count = 0;
|
|
313
|
+
break;
|
|
169
314
|
}
|
|
170
|
-
}
|
|
315
|
+
}
|
|
171
316
|
|
|
317
|
+
if (!document_found && options.upsert) {
|
|
318
|
+
upserted_document = create_upsert_document(filter, update);
|
|
319
|
+
const collection_key = build_collection_key(database_name, collection_name, upserted_document._id);
|
|
320
|
+
|
|
321
|
+
db.put(collection_key, JSON.stringify(upserted_document));
|
|
322
|
+
|
|
323
|
+
upserted_id = upserted_document._id;
|
|
324
|
+
matched_count = 0;
|
|
325
|
+
modified_count = 0;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return {
|
|
329
|
+
matched_count,
|
|
330
|
+
modified_count,
|
|
331
|
+
upserted_id,
|
|
332
|
+
old_document,
|
|
333
|
+
new_document,
|
|
334
|
+
upserted_document
|
|
335
|
+
};
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Updates indexes after document modification.
|
|
340
|
+
* @param {string} database_name - Database name
|
|
341
|
+
* @param {string} collection_name - Collection name
|
|
342
|
+
* @param {Object} old_document - Original document
|
|
343
|
+
* @param {Object} new_document - Updated document
|
|
344
|
+
*/
|
|
345
|
+
const update_indexes_after_modification = async (database_name, collection_name, old_document, new_document) => {
|
|
172
346
|
if (old_document && new_document) {
|
|
173
347
|
await update_indexes_on_update(database_name, collection_name, old_document, new_document);
|
|
174
348
|
}
|
|
175
|
-
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Updates indexes after document upsert.
|
|
353
|
+
* @param {string} database_name - Database name
|
|
354
|
+
* @param {string} collection_name - Collection name
|
|
355
|
+
* @param {Object} upserted_document - Upserted document
|
|
356
|
+
*/
|
|
357
|
+
const update_indexes_after_upsert = async (database_name, collection_name, upserted_document) => {
|
|
176
358
|
if (upserted_document) {
|
|
177
359
|
await update_indexes_on_insert(database_name, collection_name, upserted_document);
|
|
178
360
|
}
|
|
179
|
-
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Logs update operation completion.
|
|
365
|
+
* @param {Function} log - Logger function
|
|
366
|
+
* @param {string} database_name - Database name
|
|
367
|
+
* @param {string} collection_name - Collection name
|
|
368
|
+
* @param {number} matched_count - Number of matched documents
|
|
369
|
+
* @param {number} modified_count - Number of modified documents
|
|
370
|
+
* @param {string} upserted_id - ID of upserted document
|
|
371
|
+
*/
|
|
372
|
+
const log_update_completion = (log, database_name, collection_name, matched_count, modified_count, upserted_id) => {
|
|
180
373
|
log.info('Update operation completed', {
|
|
181
374
|
database: database_name,
|
|
182
375
|
collection: collection_name,
|
|
@@ -184,7 +377,16 @@ const update_one_internal = async (database_name, collection_name, filter, updat
|
|
|
184
377
|
modified_count,
|
|
185
378
|
upserted_id
|
|
186
379
|
});
|
|
187
|
-
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Creates update operation result.
|
|
384
|
+
* @param {number} matched_count - Number of matched documents
|
|
385
|
+
* @param {number} modified_count - Number of modified documents
|
|
386
|
+
* @param {string} upserted_id - ID of upserted document
|
|
387
|
+
* @returns {Object} Update result object
|
|
388
|
+
*/
|
|
389
|
+
const create_update_result = (matched_count, modified_count, upserted_id) => {
|
|
188
390
|
const result = {
|
|
189
391
|
acknowledged: true,
|
|
190
392
|
matched_count,
|
|
@@ -198,27 +400,66 @@ const update_one_internal = async (database_name, collection_name, filter, updat
|
|
|
198
400
|
return result;
|
|
199
401
|
};
|
|
200
402
|
|
|
403
|
+
/**
|
|
404
|
+
* Creates write queue operation metadata.
|
|
405
|
+
* @param {string} database_name - Database name
|
|
406
|
+
* @param {string} collection_name - Collection name
|
|
407
|
+
* @param {Object} filter - Filter criteria
|
|
408
|
+
* @returns {Object} Operation metadata
|
|
409
|
+
*/
|
|
410
|
+
const create_write_queue_metadata = (database_name, collection_name, filter) => ({
|
|
411
|
+
operation: 'update_one',
|
|
412
|
+
database: database_name,
|
|
413
|
+
collection: collection_name,
|
|
414
|
+
filter_keys: Object.keys(filter || {})
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Internal implementation of update_one operation without write queue serialization.
|
|
419
|
+
* @param {string} database_name - Name of the database
|
|
420
|
+
* @param {string} collection_name - Name of the collection
|
|
421
|
+
* @param {Object} filter - Filter criteria to match document
|
|
422
|
+
* @param {Object} update - Update operations to apply
|
|
423
|
+
* @param {Object} options - Update options
|
|
424
|
+
* @returns {Promise<Object>} Update result with acknowledged, matched_count, modified_count, and optional upserted_id
|
|
425
|
+
*/
|
|
426
|
+
const update_one_internal = async (database_name, collection_name, filter, update, options = {}) => {
|
|
427
|
+
const log = create_context_logger();
|
|
428
|
+
|
|
429
|
+
validate_update_parameters(database_name, collection_name, filter, update);
|
|
430
|
+
|
|
431
|
+
const db = get_database();
|
|
432
|
+
|
|
433
|
+
const update_result = await db.transaction(() => {
|
|
434
|
+
return process_document_update(db, database_name, collection_name, filter, update, options);
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
const { matched_count, modified_count, upserted_id, old_document, new_document, upserted_document } = update_result;
|
|
438
|
+
|
|
439
|
+
await update_indexes_after_modification(database_name, collection_name, old_document, new_document);
|
|
440
|
+
await update_indexes_after_upsert(database_name, collection_name, upserted_document);
|
|
441
|
+
|
|
442
|
+
log_update_completion(log, database_name, collection_name, matched_count, modified_count, upserted_id);
|
|
443
|
+
|
|
444
|
+
return create_update_result(matched_count, modified_count, upserted_id);
|
|
445
|
+
};
|
|
446
|
+
|
|
201
447
|
/**
|
|
202
448
|
* Updates a single document in a collection with write queue serialization.
|
|
203
449
|
* @param {string} database_name - Name of the database
|
|
204
450
|
* @param {string} collection_name - Name of the collection
|
|
205
451
|
* @param {Object} filter - Filter criteria to match document
|
|
206
452
|
* @param {Object} update - Update operations to apply
|
|
207
|
-
* @param {Object}
|
|
208
|
-
* @param {boolean} [options.upsert] - Create document if not found
|
|
453
|
+
* @param {Object} options - Update options
|
|
209
454
|
* @returns {Promise<Object>} Update result with acknowledged, matched_count, modified_count, and optional upserted_id
|
|
210
455
|
*/
|
|
211
456
|
const update_one = async (database_name, collection_name, filter, update, options = {}) => {
|
|
212
457
|
const write_queue = get_write_queue();
|
|
458
|
+
const operation_metadata = create_write_queue_metadata(database_name, collection_name, filter);
|
|
213
459
|
|
|
214
460
|
return await write_queue.enqueue_write_operation(
|
|
215
461
|
() => update_one_internal(database_name, collection_name, filter, update, options),
|
|
216
|
-
|
|
217
|
-
operation: 'update_one',
|
|
218
|
-
database: database_name,
|
|
219
|
-
collection: collection_name,
|
|
220
|
-
filter_keys: Object.keys(filter || {})
|
|
221
|
-
}
|
|
462
|
+
operation_metadata
|
|
222
463
|
);
|
|
223
464
|
};
|
|
224
465
|
|
|
@@ -8,39 +8,59 @@
|
|
|
8
8
|
import { encode_message } from './tcp_protocol.js';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
11
|
+
* Creates a success response object.
|
|
12
|
+
* @param {Object} data - Response data
|
|
13
|
+
* @returns {Object} Success response object
|
|
14
|
+
*/
|
|
15
|
+
const create_success_response = (data) => ({
|
|
16
|
+
ok: true,
|
|
17
|
+
data: data
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Creates an error response object.
|
|
22
|
+
* @param {Object} error - Error information
|
|
23
|
+
* @returns {Object} Error response object
|
|
24
|
+
*/
|
|
25
|
+
const create_error_response = (error) => ({
|
|
26
|
+
ok: false,
|
|
27
|
+
error: error
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Encodes and writes response to socket.
|
|
12
32
|
* @param {net.Socket} socket - Client socket connection
|
|
13
|
-
* @param {Object}
|
|
33
|
+
* @param {Object} response - Response object to send
|
|
14
34
|
*/
|
|
15
|
-
|
|
16
|
-
const response = {
|
|
17
|
-
ok: true,
|
|
18
|
-
data: data
|
|
19
|
-
};
|
|
20
|
-
|
|
35
|
+
const write_encoded_response = (socket, response) => {
|
|
21
36
|
const encoded_response = encode_message(response);
|
|
22
37
|
socket.write(encoded_response);
|
|
23
38
|
};
|
|
24
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Sends a success response to the client.
|
|
42
|
+
* @param {net.Socket} socket - Client socket connection
|
|
43
|
+
* @param {Object} data - Response data to send
|
|
44
|
+
*/
|
|
45
|
+
export const send_success = (socket, data = {}) => {
|
|
46
|
+
const response = create_success_response(data);
|
|
47
|
+
write_encoded_response(socket, response);
|
|
48
|
+
};
|
|
49
|
+
|
|
25
50
|
/**
|
|
26
51
|
* Sends an error response to the client.
|
|
27
52
|
* @param {net.Socket} socket - Client socket connection
|
|
28
|
-
* @param {Object}
|
|
53
|
+
* @param {Object} error - Error information to send
|
|
29
54
|
*/
|
|
30
55
|
export const send_error = (socket, error = {}) => {
|
|
31
|
-
const response =
|
|
32
|
-
|
|
33
|
-
error: error
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
const encoded_response = encode_message(response);
|
|
37
|
-
socket.write(encoded_response);
|
|
56
|
+
const response = create_error_response(error);
|
|
57
|
+
write_encoded_response(socket, response);
|
|
38
58
|
};
|
|
39
59
|
|
|
40
60
|
/**
|
|
41
61
|
* Sends a simple message response to the client.
|
|
42
62
|
* @param {net.Socket} socket - Client socket connection
|
|
43
|
-
* @param {string}
|
|
63
|
+
* @param {string} message - Message to send
|
|
44
64
|
*/
|
|
45
65
|
export const send_message = (socket, message = '') => {
|
|
46
66
|
send_success(socket, { message: message });
|