From 346feb51529889937b6c7229d880087eebbbe67a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Wed, 30 Oct 2024 13:47:45 +0100 Subject: [PATCH 1/4] DRIVERS-2808 DRIVERS-2807 Add specification for GridFS renameByName and deleteByName --- source/gridfs/tests/deleteByName.json | 230 +++++++++++++++++++ source/gridfs/tests/deleteByName.yml | 102 +++++++++ source/gridfs/tests/renameByName.json | 313 ++++++++++++++++++++++++++ source/gridfs/tests/renameByName.yml | 113 ++++++++++ 4 files changed, 758 insertions(+) create mode 100644 source/gridfs/tests/deleteByName.json create mode 100644 source/gridfs/tests/deleteByName.yml create mode 100644 source/gridfs/tests/renameByName.json create mode 100644 source/gridfs/tests/renameByName.yml diff --git a/source/gridfs/tests/deleteByName.json b/source/gridfs/tests/deleteByName.json new file mode 100644 index 0000000000..0590ee11ce --- /dev/null +++ b/source/gridfs/tests/deleteByName.json @@ -0,0 +1,230 @@ +{ + "description": "gridfs-deleteByName", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "gridfs-tests" + } + }, + { + "bucket": { + "id": "bucket0", + "database": "database0" + } + }, + { + "collection": { + "id": "bucket0_files_collection", + "database": "database0", + "collectionName": "fs.files" + } + }, + { + "collection": { + "id": "bucket0_chunks_collection", + "database": "database0", + "collectionName": "fs.chunks" + } + } + ], + "initialData": [ + { + "collectionName": "fs.files", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "filename", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "filename", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "length": 2, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "filename", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "length": 8, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "otherfilename", + "metadata": {} + } + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "files_id": { + "$oid": "000000000000000000000002" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "files_id": { + "$oid": "000000000000000000000003" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "files_id": { + "$oid": "000000000000000000000003" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + } + ] + } + ], + "tests": [ + { + "description": "delete when multiple revisions of the file exist", + "operations": [ + { + "name": "deleteByName", + "object": "bucket0", + "arguments": { + "filename": "filename" + } + } + ], + "outcome": [ + { + "collectionName": "fs.files", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000004" + }, + "length": 8, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "otherfilename", + "metadata": {} + } + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000004" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + } + ] + } + ] + }, + { + "description": "delete when file name does not exist", + "operations": [ + { + "name": "deleteByName", + "object": "bucket0", + "arguments": { + "filename": "missing-file" + }, + "expectError": { + "isError": true + } + } + ] + } + ] +} diff --git a/source/gridfs/tests/deleteByName.yml b/source/gridfs/tests/deleteByName.yml new file mode 100644 index 0000000000..b33e740058 --- /dev/null +++ b/source/gridfs/tests/deleteByName.yml @@ -0,0 +1,102 @@ +description: "gridfs-deleteByName" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name gridfs-tests + - bucket: + id: &bucket0 bucket0 + database: *database0 + - collection: + id: &bucket0_files_collection bucket0_files_collection + database: *database0 + collectionName: &bucket0_files_collectionName fs.files + - collection: + id: &bucket0_chunks_collection bucket0_chunks_collection + database: *database0 + collectionName: &bucket0_chunks_collectionName fs.chunks + +initialData: + - collectionName: *bucket0_files_collectionName + databaseName: *database0Name + documents: + - &file1 + _id: { "$oid": "000000000000000000000001" } + length: 0 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "filename" + metadata: {} + - &file2 + _id: { "$oid": "000000000000000000000002" } + length: 0 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "filename" + metadata: {} + - &file3 + _id: { "$oid": "000000000000000000000003" } + length: 2 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "filename" + metadata: {} + - &file4 + _id: { "$oid": "000000000000000000000004" } + length: 8 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "otherfilename" + metadata: {} + - collectionName: *bucket0_chunks_collectionName + databaseName: *database0Name + documents: + - &file2_chunk0 + _id: { "$oid": "000000000000000000000001" } + files_id: { "$oid": "000000000000000000000002" } + n: 0 + data: { "$binary": { "base64": "", "subType": "00" } } + - &file3_chunk0 + _id: { "$oid": "000000000000000000000002" } + files_id: { "$oid": "000000000000000000000003" } + n: 0 + data: { "$binary": { "base64": "", "subType": "00" } } + - &file3_chunk1 + _id: { "$oid": "000000000000000000000003" } + files_id: { "$oid": "000000000000000000000003" } + n: 0 + data: { "$binary": { "base64": "", "subType": "00" } } + - &file4_chunk0 + _id: { "$oid": "000000000000000000000004" } + files_id: { "$oid": "000000000000000000000004" } + n: 0 + data: { "$binary": { "base64": "", "subType": "00" } } + +tests: + - description: "delete when multiple revisions of the file exist" + operations: + - name: deleteByName + object: *bucket0 + arguments: + filename: filename + outcome: + - collectionName: *bucket0_files_collectionName + databaseName: *database0Name + documents: + - <<: *file4 + - collectionName: *bucket0_chunks_collectionName + databaseName: *database0Name + documents: + - *file4_chunk0 + - description: "delete when file name does not exist" + operations: + - name: deleteByName + object: *bucket0 + arguments: + filename: missing-file + expectError: { isError: true } # FileNotFound diff --git a/source/gridfs/tests/renameByName.json b/source/gridfs/tests/renameByName.json new file mode 100644 index 0000000000..81d36b4ee8 --- /dev/null +++ b/source/gridfs/tests/renameByName.json @@ -0,0 +1,313 @@ +{ + "description": "gridfs-renameByName", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0" + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "gridfs-tests" + } + }, + { + "bucket": { + "id": "bucket0", + "database": "database0" + } + }, + { + "collection": { + "id": "bucket0_files_collection", + "database": "database0", + "collectionName": "fs.files" + } + }, + { + "collection": { + "id": "bucket0_chunks_collection", + "database": "database0", + "collectionName": "fs.chunks" + } + } + ], + "initialData": [ + { + "collectionName": "fs.files", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "filename", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "filename", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "length": 2, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "filename", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "length": 8, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "otherfilename", + "metadata": {} + } + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "files_id": { + "$oid": "000000000000000000000002" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "files_id": { + "$oid": "000000000000000000000003" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 1, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + } + ] + } + ], + "tests": [ + { + "description": "rename when multiple revisions of the file exist", + "operations": [ + { + "name": "renameByName", + "object": "bucket0", + "arguments": { + "filename": "filename", + "newFilename": "newfilename" + } + } + ], + "outcome": [ + { + "collectionName": "fs.files", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "newfilename", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "length": 0, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "newfilename", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "length": 2, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "newfilename", + "metadata": {} + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "length": 8, + "chunkSize": 4, + "uploadDate": { + "$date": "1970-01-01T00:00:00.000Z" + }, + "filename": "otherfilename", + "metadata": {} + } + ] + }, + { + "collectionName": "fs.chunks", + "databaseName": "gridfs-tests", + "documents": [ + { + "_id": { + "$oid": "000000000000000000000001" + }, + "files_id": { + "$oid": "000000000000000000000002" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000002" + }, + "files_id": { + "$oid": "000000000000000000000003" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000003" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 0, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + }, + { + "_id": { + "$oid": "000000000000000000000004" + }, + "files_id": { + "$oid": "000000000000000000000004" + }, + "n": 1, + "data": { + "$binary": { + "base64": "", + "subType": "00" + } + } + } + ] + } + ] + }, + { + "description": "rename when file name does not exist", + "operations": [ + { + "name": "renameByName", + "object": "bucket0", + "arguments": { + "filename": "missing-file", + "newFilename": "newfilename" + }, + "expectError": { + "isError": true + } + } + ] + } + ] +} diff --git a/source/gridfs/tests/renameByName.yml b/source/gridfs/tests/renameByName.yml new file mode 100644 index 0000000000..2904b78f0f --- /dev/null +++ b/source/gridfs/tests/renameByName.yml @@ -0,0 +1,113 @@ +description: "gridfs-renameByName" + +schemaVersion: "1.0" + +createEntities: + - client: + id: &client0 client0 + - database: + id: &database0 database0 + client: *client0 + databaseName: &database0Name gridfs-tests + - bucket: + id: &bucket0 bucket0 + database: *database0 + - collection: + id: &bucket0_files_collection bucket0_files_collection + database: *database0 + collectionName: &bucket0_files_collectionName fs.files + - collection: + id: &bucket0_chunks_collection bucket0_chunks_collection + database: *database0 + collectionName: &bucket0_chunks_collectionName fs.chunks + +initialData: + - collectionName: *bucket0_files_collectionName + databaseName: *database0Name + documents: + - &file1 + _id: { "$oid": "000000000000000000000001" } + length: 0 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "filename" + metadata: {} + - &file2 + _id: { "$oid": "000000000000000000000002" } + length: 0 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "filename" + metadata: {} + - &file3 + _id: { "$oid": "000000000000000000000003" } + length: 2 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "filename" + metadata: {} + - &file4 + _id: { "$oid": "000000000000000000000004" } + length: 8 + chunkSize: 4 + uploadDate: { "$date": "1970-01-01T00:00:00.000Z" } + filename: "otherfilename" + metadata: {} + - collectionName: *bucket0_chunks_collectionName + databaseName: *database0Name + documents: + - &file2_chunk0 + _id: { "$oid": "000000000000000000000001" } + files_id: { "$oid": "000000000000000000000002" } + n: 0 + data: { "$binary": { "base64": "", "subType": "00" } } + - &file3_chunk0 + _id: { "$oid": "000000000000000000000002" } + files_id: { "$oid": "000000000000000000000003" } + n: 0 + data: { "$binary": { "base64": "", "subType": "00" } } + - &file4_chunk0 + _id: { "$oid": "000000000000000000000003" } + files_id: { "$oid": "000000000000000000000004" } + n: 0 + data: { "$binary": { "base64": "", "subType": "00" } } + - &file4_chunk1 + _id: { "$oid": "000000000000000000000004" } + files_id: { "$oid": "000000000000000000000004" } + n: 1 + data: { "$binary": { "base64": "", "subType": "00" } } + +tests: + - description: "rename when multiple revisions of the file exist" + operations: + - name: renameByName + object: *bucket0 + arguments: + filename: filename + newFilename: newfilename + outcome: + - collectionName: *bucket0_files_collectionName + databaseName: *database0Name + documents: + - <<: *file1 + filename: newfilename + - <<: *file2 + filename: newfilename + - <<: *file3 + filename: newfilename + - <<: *file4 + - collectionName: *bucket0_chunks_collectionName + databaseName: *database0Name + documents: + - *file2_chunk0 + - *file3_chunk0 + - *file4_chunk0 + - *file4_chunk1 + - description: "rename when file name does not exist" + operations: + - name: renameByName + object: *bucket0 + arguments: + filename: missing-file + newFilename: newfilename + expectError: { isError: true } # FileNotFound From 327511f8493e3caee7e002646864777fc9432f82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Wed, 30 Oct 2024 17:27:04 +0100 Subject: [PATCH 2/4] Update GridFS spec to add delete_by_name and rename_by_name --- source/gridfs/gridfs-spec.md | 54 ++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/source/gridfs/gridfs-spec.md b/source/gridfs/gridfs-spec.md index c675dd6e1c..e272563225 100644 --- a/source/gridfs/gridfs-spec.md +++ b/source/gridfs/gridfs-spec.md @@ -649,6 +649,32 @@ orphaned chunks with files_id equal to id before raising the error. If a networking or server error occurs, drivers MUST raise an error. +### File deletion by filename + +To rename all revisions of a stored file with the specified filename, drivers SHOULD provide the method +`delete_by_name`: + +```javascript +class GridFSBucket { + + /** + * Deletes all stored files with the specified @filename from a GridFS bucket. + */ + void delete_by_name(string filename); + +} +``` + +This method is an optimisation over deleting each revision of a stored file individually. + +**Implementation details:** + +Drivers MUST first find the `_id` field of all files collection documents with the given filename. Drivers MUST then +delete all chunks with `files_id` in the found ids that was just deleted. Drivers MUST then delete all files collection +documents with the found ids. + +If there are no files collection documents with the given filename, drivers MUST raise an error. + ### Generic Find on Files Collection ```javascript @@ -821,11 +847,34 @@ Sets the filename field in the stored file's files collection document to the ne Drivers construct and execute an update_one command on the files collection using `{ _id: @id }` as the filter and `{ $set : { filename : "new_filename" } }` as the update parameter. -To rename multiple revisions of the same filename, users must retrieve the full list of files collection documents for a -given filename and execute "rename" on each corresponding `_id`. +If `renameByName` is not implemented to rename multiple revisions of the same filename, users must retrieve the full +list of files collection documents for a given filename and execute "rename" on each corresponding `_id`. If there is no file with the given id, drivers MUST raise an error. +### Renaming stored files by filename + +To rename all revisions of a stored file with the specified filename, drivers SHOULD provide the method +`rename_by_name`: + +```javascript +class GridFSBucket { + + /** + * Renames all revisions of the stored file with the specified @filename. + */ + void rename_by_name(string filename, string new_filename); + +} +``` + +**Implementation details:** + +Drivers construct and execute an update_many command on the files collection using `{ filename: @filename }` as the +filter and `{ $set : { filename : "new_filename" } }` as the update parameter. + +If there is no file with the given filename, drivers MUST raise an error. + ### Dropping an entire GridFS bucket ```javascript @@ -1042,6 +1091,7 @@ system?") it is a potential area of growth for the future. ## Changelog +- 2024-10-30: Add `delete_by_name` and `rename_by_name` - 2024-10-28: Removed deprecated fields from tests: `md5`, `contentType`, `aliases` - 2024-02-27: Migrated from reStructuredText to Markdown. - 2016-05-10: Support custom file ids From fd6fd076689f84392392fdbe805e49a2229278a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Thu, 31 Oct 2024 00:21:16 +0100 Subject: [PATCH 3/4] Change order or delete operations --- source/gridfs/gridfs-spec.md | 4 ++-- source/gridfs/tests/deleteByName.json | 2 +- source/gridfs/tests/deleteByName.yml | 2 +- source/gridfs/tests/renameByName.json | 2 +- source/gridfs/tests/renameByName.yml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source/gridfs/gridfs-spec.md b/source/gridfs/gridfs-spec.md index e272563225..4052b94973 100644 --- a/source/gridfs/gridfs-spec.md +++ b/source/gridfs/gridfs-spec.md @@ -670,8 +670,8 @@ This method is an optimisation over deleting each revision of a stored file indi **Implementation details:** Drivers MUST first find the `_id` field of all files collection documents with the given filename. Drivers MUST then -delete all chunks with `files_id` in the found ids that was just deleted. Drivers MUST then delete all files collection -documents with the found ids. +delete all files collection documents with the found ids. Drivers MUST then delete all chunks with `files_id` in the +found ids that was just deleted. If there are no files collection documents with the given filename, drivers MUST raise an error. diff --git a/source/gridfs/tests/deleteByName.json b/source/gridfs/tests/deleteByName.json index 0590ee11ce..884d0300ce 100644 --- a/source/gridfs/tests/deleteByName.json +++ b/source/gridfs/tests/deleteByName.json @@ -221,7 +221,7 @@ "filename": "missing-file" }, "expectError": { - "isError": true + "isClientError": true } } ] diff --git a/source/gridfs/tests/deleteByName.yml b/source/gridfs/tests/deleteByName.yml index b33e740058..b63b15633a 100644 --- a/source/gridfs/tests/deleteByName.yml +++ b/source/gridfs/tests/deleteByName.yml @@ -99,4 +99,4 @@ tests: object: *bucket0 arguments: filename: missing-file - expectError: { isError: true } # FileNotFound + expectError: { isClientError: true } # FileNotFound diff --git a/source/gridfs/tests/renameByName.json b/source/gridfs/tests/renameByName.json index 81d36b4ee8..26f04fb9e0 100644 --- a/source/gridfs/tests/renameByName.json +++ b/source/gridfs/tests/renameByName.json @@ -304,7 +304,7 @@ "newFilename": "newfilename" }, "expectError": { - "isError": true + "isClientError": true } } ] diff --git a/source/gridfs/tests/renameByName.yml b/source/gridfs/tests/renameByName.yml index 2904b78f0f..f628ca9c5f 100644 --- a/source/gridfs/tests/renameByName.yml +++ b/source/gridfs/tests/renameByName.yml @@ -110,4 +110,4 @@ tests: arguments: filename: missing-file newFilename: newfilename - expectError: { isError: true } # FileNotFound + expectError: { isClientError: true } # FileNotFound From 0c3084e1e96ef630c1cb950242dc1c9365d9e25d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Tamarelle?= Date: Thu, 31 Oct 2024 18:41:13 +0100 Subject: [PATCH 4/4] Wording --- source/gridfs/gridfs-spec.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/gridfs/gridfs-spec.md b/source/gridfs/gridfs-spec.md index 4052b94973..e7fe407df2 100644 --- a/source/gridfs/gridfs-spec.md +++ b/source/gridfs/gridfs-spec.md @@ -671,7 +671,7 @@ This method is an optimisation over deleting each revision of a stored file indi Drivers MUST first find the `_id` field of all files collection documents with the given filename. Drivers MUST then delete all files collection documents with the found ids. Drivers MUST then delete all chunks with `files_id` in the -found ids that was just deleted. +found ids that were just deleted. If there are no files collection documents with the given filename, drivers MUST raise an error.