-
Notifications
You must be signed in to change notification settings - Fork 1.8k
feat(NODE-4034)!: make internal bulk result private #3515
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
6625a45
d592a0f
8ea38e5
80bdaa3
cb834ea
4971fee
4b6bea5
1badb1b
318ffcb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -123,11 +123,7 @@ export type AnyBulkWriteOperation<TSchema extends Document = Document> = | |
| { deleteOne: DeleteOneModel<TSchema> } | ||
| { deleteMany: DeleteManyModel<TSchema> }; | ||
|
||
/** | ||
* @public | ||
* | ||
* @deprecated Will be made internal in 5.0 | ||
*/ | ||
/** @internal */ | ||
export interface BulkResult { | ||
ok: number; | ||
writeErrors: WriteError[]; | ||
|
@@ -172,54 +168,44 @@ export class Batch<T = Document> { | |
* The result of a bulk write. | ||
*/ | ||
export class BulkWriteResult { | ||
/** @deprecated Will be removed in 5.0 */ | ||
result: BulkResult; | ||
|
||
/** | ||
* Create a new BulkWriteResult instance | ||
* @internal | ||
*/ | ||
constructor(bulkResult: BulkResult) { | ||
this.result = bulkResult; | ||
} | ||
|
||
private readonly result: BulkResult; | ||
/** Number of documents inserted. */ | ||
get insertedCount(): number { | ||
return this.result.nInserted ?? 0; | ||
} | ||
readonly insertedCount: number; | ||
/** Number of documents matched for update. */ | ||
get matchedCount(): number { | ||
return this.result.nMatched ?? 0; | ||
} | ||
readonly matchedCount: number; | ||
/** Number of documents modified. */ | ||
get modifiedCount(): number { | ||
return this.result.nModified ?? 0; | ||
} | ||
readonly modifiedCount: number; | ||
/** Number of documents deleted. */ | ||
get deletedCount(): number { | ||
return this.result.nRemoved ?? 0; | ||
} | ||
readonly deletedCount: number; | ||
/** Number of documents upserted. */ | ||
get upsertedCount(): number { | ||
return this.result.upserted.length ?? 0; | ||
} | ||
|
||
readonly upsertedCount: number; | ||
/** Upserted document generated Id's, hash key is the index of the originating operation */ | ||
get upsertedIds(): { [key: number]: any } { | ||
const upserted: { [index: number]: any } = {}; | ||
for (const doc of this.result.upserted ?? []) { | ||
upserted[doc.index] = doc._id; | ||
readonly upsertedIds: { [key: number]: any }; | ||
/** Inserted document generated Id's, hash key is the index of the originating operation */ | ||
readonly insertedIds: { [key: number]: any }; | ||
|
||
private static generateIdMap(ids: Document[]): { [key: number]: any } { | ||
const idMap: { [index: number]: any } = {}; | ||
for (const doc of ids) { | ||
idMap[doc.index] = doc._id; | ||
} | ||
return upserted; | ||
return idMap; | ||
} | ||
|
||
/** Inserted document generated Id's, hash key is the index of the originating operation */ | ||
get insertedIds(): { [key: number]: any } { | ||
const inserted: { [index: number]: any } = {}; | ||
for (const doc of this.result.insertedIds ?? []) { | ||
inserted[doc.index] = doc._id; | ||
} | ||
return inserted; | ||
/** | ||
* Create a new BulkWriteResult instance | ||
* @internal | ||
*/ | ||
constructor(bulkResult: BulkResult) { | ||
this.result = bulkResult; | ||
this.insertedCount = this.result.nInserted ?? 0; | ||
this.matchedCount = this.result.nMatched ?? 0; | ||
this.modifiedCount = this.result.nModified ?? 0; | ||
this.deletedCount = this.result.nRemoved ?? 0; | ||
this.upsertedCount = this.result.upserted.length ?? 0; | ||
this.upsertedIds = BulkWriteResult.generateIdMap(this.result.upserted); | ||
this.insertedIds = BulkWriteResult.generateIdMap(this.result.insertedIds); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have you had a chance to check with any other drivers to see why they aren't running into the same insertedIds issues? It may be good to know if they are also implementing an internal work around, or aren't spec compliant themselves, or perhaps interpreting the spec differently. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ruby iterates over the keys in the expected result and checks the actual object for that specific property, so it does not error when the actual result has more properties than the expected. Python does the same. It seems we are probably the only driver looking for an exact match of the actual and expected object properties, which makes us less flexible on adding other properties to our result classes. The CRUD spec is clear about required and optional fields in the result objects, but does not say if additional properties on the result classes are forbidden. It seems like Node took the spec strictly but other drivers allowed a bit more flexibility - neither is incorrect in my opinion. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dariakp Are you okay with this answer? This is the last outstanding thread on this PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The "Evaluating Matches" section of the unified runner spec is very explicit about asserting that the properties of a subdocument inside an expected result property must match exactly: also
So, per the spec, we are correct, and the spec tests must account for allowed optional properties via special test operators (basically, what you have filed in the DRIVERS-2523 is addressing a bug in the test implementation for RewrapManyDataKey; it is also an omission for the unified spec that we don't have the rewrapManyDataKey operation defined in the unified format spec and are also not referencing the spec where that operation is expected to be defined). This is an issue for insertMany tests, too, and for the "acknowledged" property in general (which perhaps no driver implements); surprised that is not coming up here (unless we already have the logic to handle it). I don't think we should treat BulkWriteResult as a root level document, because we do know what optional fields are allowed to exist on it, and we do not want it to have arbitrary fields beyond the ones defined by the spec. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added a comment to the drivers ticket, we don't need to keep this PR open on that account There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since the test updates merged for DRIVERS-2523 I just went ahead and added them here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @durran Should that ticket be updated to "teams implementing"? (That way we can tag the generated node ticket number in this PR and it'll auto-link in jira) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did update it but for some reason it didn't generate any individual language tickets. Hmm. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Description updated with NODE-4973 now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also linked all 3 tickets to NODE-4034 now in JIRA. |
||
Object.defineProperty(this, 'result', { value: this.result, enumerable: false }); | ||
} | ||
|
||
/** Evaluates to true if the bulk operation correctly executes */ | ||
|
@@ -314,13 +300,8 @@ export class BulkWriteResult { | |
} | ||
} | ||
|
||
/* @deprecated Will be removed in 5.0 release */ | ||
toJSON(): BulkResult { | ||
return this.result; | ||
} | ||
|
||
toString(): string { | ||
return `BulkWriteResult(${this.toJSON()})`; | ||
return `BulkWriteResult(${this.result})`; | ||
} | ||
|
||
isOk(): boolean { | ||
|
@@ -550,12 +531,8 @@ function executeCommands( | |
} | ||
|
||
// Merge the results together | ||
mergeBatchResults(batch, bulkOperation.s.bulkResult, err, result); | ||
const writeResult = new BulkWriteResult(bulkOperation.s.bulkResult); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just clarification for further reviewers. Before the changes in this PR, Now, we assign the properties directly in the constructor, so we need to create the |
||
const mergeResult = mergeBatchResults(batch, bulkOperation.s.bulkResult, err, result); | ||
if (mergeResult != null) { | ||
return callback(undefined, writeResult); | ||
} | ||
|
||
if (bulkOperation.handleWriteError(callback, writeResult)) return; | ||
|
||
// Execute the next command in line | ||
|
@@ -1246,7 +1223,6 @@ export abstract class BulkOperationBase { | |
this.s.executed = true; | ||
const finalOptions = { ...this.s.options, ...options }; | ||
const operation = new BulkWriteShimOperation(this, finalOptions); | ||
|
||
return executeOperation(this.s.collection.s.db.s.client, operation); | ||
}, callback); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason to assign
result
here and then override it in line 207?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The override was to set it as non-enumerable but typescript complained if I did it all in one go here. (It couldn't figure out that
Object.defineProperty
was settingthis.result
.