-
Notifications
You must be signed in to change notification settings - Fork 1.8k
feat(NODE-6327): new client bulk write types and builders #4205
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
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
7985beb
feat(NODE-6327): implement client bulk write types and builders
durran b521d50
refactor: use functions not objects
durran a953109
docs: update comments
durran 94a9730
docs: typo
durran 7d50499
chore: more typing
durran 9272b7a
chore: update operation types
durran fd1d467
refactor: next batch comments
durran File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,283 @@ | ||
import { type Document } from '../../bson'; | ||
import { DocumentSequence } from '../../cmap/commands'; | ||
import type { Filter, OptionalId, UpdateFilter, WithoutId } from '../../mongo_types'; | ||
import { type CollationOptions } from '../command'; | ||
import { type Hint } from '../operation'; | ||
import type { | ||
AnyClientBulkWriteModel, | ||
ClientBulkWriteOptions, | ||
ClientDeleteManyModel, | ||
ClientDeleteOneModel, | ||
ClientInsertOneModel, | ||
ClientReplaceOneModel, | ||
ClientUpdateManyModel, | ||
ClientUpdateOneModel | ||
} from './common'; | ||
|
||
/** @internal */ | ||
export interface ClientBulkWriteCommand { | ||
bulkWrite: 1; | ||
errorsOnly: boolean; | ||
ordered: boolean; | ||
ops: DocumentSequence; | ||
nsInfo: DocumentSequence; | ||
bypassDocumentValidation?: boolean; | ||
let?: Document; | ||
} | ||
|
||
/** @internal */ | ||
export class ClientBulkWriteCommandBuilder { | ||
models: AnyClientBulkWriteModel[]; | ||
options: ClientBulkWriteOptions; | ||
|
||
/** | ||
* Create the command builder. | ||
* @param models - The client write models. | ||
*/ | ||
constructor(models: AnyClientBulkWriteModel[], options: ClientBulkWriteOptions) { | ||
this.models = models; | ||
this.options = options; | ||
} | ||
|
||
/** | ||
* Gets the errorsOnly value for the command, which is the inverse of the | ||
* user provided verboseResults option. Defaults to true. | ||
*/ | ||
get errorsOnly(): boolean { | ||
if ('verboseResults' in this.options) { | ||
return !this.options.verboseResults; | ||
} | ||
return true; | ||
} | ||
|
||
/** | ||
* Build the bulk write commands from the models. | ||
*/ | ||
buildCommands(): ClientBulkWriteCommand[] { | ||
// Iterate the models to build the ops and nsInfo fields. | ||
const operations = []; | ||
let currentNamespaceIndex = 0; | ||
const namespaces = new Map<string, number>(); | ||
for (const model of this.models) { | ||
const ns = model.namespace; | ||
const index = namespaces.get(ns); | ||
if (index != null) { | ||
operations.push(buildOperation(model, index)); | ||
} else { | ||
namespaces.set(ns, currentNamespaceIndex); | ||
operations.push(buildOperation(model, currentNamespaceIndex)); | ||
currentNamespaceIndex++; | ||
} | ||
} | ||
|
||
const nsInfo = Array.from(namespaces.keys(), ns => ({ ns })); | ||
|
||
// The base command. | ||
const command: ClientBulkWriteCommand = { | ||
bulkWrite: 1, | ||
errorsOnly: this.errorsOnly, | ||
ordered: this.options.ordered ?? true, | ||
ops: new DocumentSequence(operations), | ||
nsInfo: new DocumentSequence(nsInfo) | ||
}; | ||
// Add bypassDocumentValidation if it was present in the options. | ||
if (this.options.bypassDocumentValidation != null) { | ||
command.bypassDocumentValidation = this.options.bypassDocumentValidation; | ||
} | ||
// Add let if it was present in the options. | ||
if (this.options.let) { | ||
command.let = this.options.let; | ||
} | ||
return [command]; | ||
} | ||
} | ||
|
||
/** @internal */ | ||
interface ClientInsertOperation { | ||
insert: number; | ||
document: OptionalId<Document>; | ||
} | ||
|
||
/** | ||
* Build the insert one operation. | ||
* @param model - The insert one model. | ||
* @param index - The namespace index. | ||
* @returns the operation. | ||
*/ | ||
export const buildInsertOneOperation = ( | ||
model: ClientInsertOneModel, | ||
index: number | ||
): ClientInsertOperation => { | ||
const document: ClientInsertOperation = { | ||
insert: index, | ||
document: model.document | ||
}; | ||
return document; | ||
}; | ||
|
||
/** @internal */ | ||
export interface ClientDeleteOperation { | ||
delete: number; | ||
multi: boolean; | ||
filter: Filter<Document>; | ||
hint?: Hint; | ||
collation?: CollationOptions; | ||
} | ||
|
||
/** | ||
* Build the delete one operation. | ||
* @param model - The insert many model. | ||
* @param index - The namespace index. | ||
* @returns the operation. | ||
*/ | ||
export const buildDeleteOneOperation = (model: ClientDeleteOneModel, index: number): Document => { | ||
return createDeleteOperation(model, index, false); | ||
}; | ||
|
||
/** | ||
* Build the delete many operation. | ||
* @param model - The delete many model. | ||
* @param index - The namespace index. | ||
* @returns the operation. | ||
*/ | ||
export const buildDeleteManyOperation = (model: ClientDeleteManyModel, index: number): Document => { | ||
return createDeleteOperation(model, index, true); | ||
}; | ||
|
||
/** | ||
* Creates a delete operation based on the parameters. | ||
*/ | ||
function createDeleteOperation( | ||
model: ClientDeleteOneModel | ClientDeleteManyModel, | ||
index: number, | ||
multi: boolean | ||
): ClientDeleteOperation { | ||
const document: ClientDeleteOperation = { | ||
delete: index, | ||
multi: multi, | ||
filter: model.filter | ||
}; | ||
if (model.hint) { | ||
document.hint = model.hint; | ||
} | ||
if (model.collation) { | ||
document.collation = model.collation; | ||
} | ||
return document; | ||
} | ||
|
||
/** @internal */ | ||
export interface ClientUpdateOperation { | ||
update: number; | ||
multi: boolean; | ||
filter: Filter<Document>; | ||
updateMods: UpdateFilter<Document> | Document[]; | ||
hint?: Hint; | ||
upsert?: boolean; | ||
arrayFilters?: Document[]; | ||
} | ||
|
||
/** | ||
* Build the update one operation. | ||
* @param model - The update one model. | ||
* @param index - The namespace index. | ||
* @returns the operation. | ||
*/ | ||
export const buildUpdateOneOperation = ( | ||
model: ClientUpdateOneModel, | ||
index: number | ||
): ClientUpdateOperation => { | ||
return createUpdateOperation(model, index, false); | ||
}; | ||
|
||
/** | ||
* Build the update many operation. | ||
* @param model - The update many model. | ||
* @param index - The namespace index. | ||
* @returns the operation. | ||
*/ | ||
export const buildUpdateManyOperation = ( | ||
model: ClientUpdateManyModel, | ||
index: number | ||
): ClientUpdateOperation => { | ||
return createUpdateOperation(model, index, true); | ||
}; | ||
|
||
/** | ||
* Creates a delete operation based on the parameters. | ||
baileympearson marked this conversation as resolved.
Show resolved
Hide resolved
|
||
*/ | ||
function createUpdateOperation( | ||
model: ClientUpdateOneModel | ClientUpdateManyModel, | ||
index: number, | ||
multi: boolean | ||
): ClientUpdateOperation { | ||
const document: ClientUpdateOperation = { | ||
update: index, | ||
multi: multi, | ||
filter: model.filter, | ||
updateMods: model.update | ||
}; | ||
if (model.hint) { | ||
document.hint = model.hint; | ||
} | ||
if (model.upsert) { | ||
document.upsert = model.upsert; | ||
} | ||
if (model.arrayFilters) { | ||
document.arrayFilters = model.arrayFilters; | ||
} | ||
return document; | ||
} | ||
|
||
/** @internal */ | ||
export interface ClientReplaceOneOperation { | ||
update: number; | ||
multi: boolean; | ||
filter: Filter<Document>; | ||
updateMods: WithoutId<Document>; | ||
hint?: Hint; | ||
upsert?: boolean; | ||
} | ||
|
||
/** | ||
* Build the replace one operation. | ||
* @param model - The replace one model. | ||
* @param index - The namespace index. | ||
* @returns the operation. | ||
*/ | ||
export const buildReplaceOneOperation = ( | ||
model: ClientReplaceOneModel, | ||
index: number | ||
): ClientReplaceOneOperation => { | ||
const document: ClientReplaceOneOperation = { | ||
update: index, | ||
multi: false, | ||
filter: model.filter, | ||
updateMods: model.replacement | ||
}; | ||
if (model.hint) { | ||
document.hint = model.hint; | ||
} | ||
if (model.upsert) { | ||
document.upsert = model.upsert; | ||
} | ||
return document; | ||
}; | ||
|
||
/** @internal */ | ||
export function buildOperation(model: AnyClientBulkWriteModel, index: number): Document { | ||
switch (model.name) { | ||
case 'insertOne': | ||
return buildInsertOneOperation(model, index); | ||
case 'deleteOne': | ||
return buildDeleteOneOperation(model, index); | ||
case 'deleteMany': | ||
return buildDeleteManyOperation(model, index); | ||
case 'updateOne': | ||
return buildUpdateOneOperation(model, index); | ||
case 'updateMany': | ||
return buildUpdateManyOperation(model, index); | ||
case 'replaceOne': | ||
return buildReplaceOneOperation(model, index); | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.