Skip to content
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

feat(NODE-3333): support 'let' option for CRUD commands #2829

Merged
merged 3 commits into from
Jun 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/operations/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export interface DeleteOptions extends CommandOperationOptions, WriteConcernOpti
collation?: CollationOptions;
/** Specify that the update query should only consider plans using the hinted index */
hint?: string | Document;
/** Map of parameter names and values that can be accessed using $$var (requires MongoDB 5.0). */
let?: Document;

/** @deprecated use `removeOne` or `removeMany` to implicitly specify the limit */
single?: boolean;
Expand Down Expand Up @@ -74,6 +76,10 @@ export class DeleteOperation extends CommandOperation<Document> {
ordered
};

if (options.let) {
command.let = options.let;
}

if (options.explain !== undefined && maxWireVersion(server) < 3) {
return callback
? callback(new MongoError(`server ${server.name} does not support explain on delete`))
Expand Down
6 changes: 6 additions & 0 deletions src/operations/find.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ export interface FindOptions<TSchema = Document> extends CommandOperationOptions
allowPartialResults?: boolean;
/** Determines whether to return the record identifier for each document. If true, adds a field $recordId to the returned documents. */
showRecordId?: boolean;
/** Map of parameter names and values that can be accessed using $$var (requires MongoDB 5.0). */
let?: Document;
}

const SUPPORTS_WRITE_CONCERN_AND_COLLATION = 5;
Expand Down Expand Up @@ -286,6 +288,10 @@ function makeFindCommand(ns: MongoDBNamespace, filter: Document, options: FindOp
findCommand.allowDiskUse = options.allowDiskUse;
}

if (options.let) {
findCommand.let = options.let;
}

return findCommand;
}

Expand Down
11 changes: 11 additions & 0 deletions src/operations/find_and_modify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export interface FindOneAndDeleteOptions extends CommandOperationOptions {
projection?: Document;
/** Determines which document the operation modifies if the query selects multiple documents. */
sort?: Sort;
/** Map of parameter names and values that can be accessed using $$var (requires MongoDB 5.0). */
let?: Document;
}

/** @public */
Expand All @@ -43,6 +45,8 @@ export interface FindOneAndReplaceOptions extends CommandOperationOptions {
sort?: Sort;
/** Upsert the document if it does not exist. */
upsert?: boolean;
/** Map of parameter names and values that can be accessed using $$var (requires MongoDB 5.0). */
let?: Document;
}

/** @public */
Expand All @@ -61,6 +65,8 @@ export interface FindOneAndUpdateOptions extends CommandOperationOptions {
sort?: Sort;
/** Upsert the document if it does not exist. */
upsert?: boolean;
/** Map of parameter names and values that can be accessed using $$var (requires MongoDB 5.0). */
let?: Document;
}

/** @internal */
Expand All @@ -74,6 +80,7 @@ interface FindAndModifyCmdBase {
bypassDocumentValidation?: boolean;
arrayFilters?: Document[];
maxTimeMS?: number;
let?: Document;
writeConcern?: WriteConcern | WriteConcernSettings;
}

Expand Down Expand Up @@ -129,6 +136,10 @@ class FindAndModifyOperation extends CommandOperation<Document> {
this.cmdBase.writeConcern = options.writeConcern;
}

if (options.let) {
this.cmdBase.let = options.let;
}

// force primary read preference
this.readPreference = ReadPreference.primary;

Expand Down
6 changes: 6 additions & 0 deletions src/operations/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ export interface UpdateOptions extends CommandOperationOptions {
hint?: string | Document;
/** When true, creates a new document if no document matches the query */
upsert?: boolean;
/** Map of parameter names and values that can be accessed using $$var (requires MongoDB 5.0). */
let?: Document;
}

/** @public */
Expand Down Expand Up @@ -97,6 +99,10 @@ export class UpdateOperation extends CommandOperation<Document> {
command.bypassDocumentValidation = options.bypassDocumentValidation;
}

if (options.let) {
command.let = options.let;
}

const statementWithCollation = this.statements.find(statement => !!statement.collation);
if (
collationNotSupported(server, options) ||
Expand Down
13 changes: 8 additions & 5 deletions test/functional/unified-spec-runner/operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,8 @@ operations.set('createIndex', async ({ entities, operation }) => {

operations.set('deleteOne', async ({ entities, operation }) => {
const collection = entities.getEntity('collection', operation.object);
return collection.deleteOne(operation.arguments.filter);
const { filter, ...options } = operation.arguments;
return collection.deleteOne(filter, options);
});

operations.set('dropCollection', async ({ entities, operation }) => {
Expand All @@ -230,8 +231,8 @@ operations.set('endSession', async ({ entities, operation }) => {

operations.set('find', async ({ entities, operation }) => {
const collection = entities.getEntity('collection', operation.object);
const { filter, sort, batchSize, limit } = operation.arguments;
return collection.find(filter, { sort, batchSize, limit }).toArray();
const { filter, sort, batchSize, limit, let: vars } = operation.arguments;
return collection.find(filter, { sort, batchSize, limit, let: vars }).toArray();
});

operations.set('findOneAndReplace', async ({ entities, operation }) => {
Expand Down Expand Up @@ -398,12 +399,14 @@ operations.set('runCommand', async ({ entities, operation }: OperationFunctionPa

operations.set('updateMany', async ({ entities, operation }) => {
const collection = entities.getEntity('collection', operation.object);
return collection.updateMany(operation.arguments.filter, operation.arguments.update);
const { filter, update, ...options } = operation.arguments;
return collection.updateMany(filter, update, options);
});

operations.set('updateOne', async ({ entities, operation }) => {
const collection = entities.getEntity('collection', operation.object);
return collection.updateOne(operation.arguments.filter, operation.arguments.update);
const { filter, update, ...options } = operation.arguments;
return collection.updateOne(filter, update, options);
});

export async function executeOperationAndCheck(
Expand Down
103 changes: 103 additions & 0 deletions test/spec/crud/unified/aggregate-let.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,109 @@
"minServerVersion": "5.0"
}
],
"operations": [
{
"name": "aggregate",
"object": "collection0",
"arguments": {
"pipeline": [
{
"$match": {
"$expr": {
"$eq": [
"$_id",
"$$id"
]
}
}
},
{
"$project": {
"_id": 0,
"x": "$$x",
"y": "$$y",
"rand": "$$rand"
}
}
],
"let": {
"id": 1,
"x": "foo",
"y": {
"$literal": "bar"
},
"rand": {
"$rand": {}
}
}
},
"expectResult": [
{
"x": "foo",
"y": "bar",
"rand": {
"$$type": "double"
}
}
]
}
],
"expectEvents": [
{
"client": "client0",
"events": [
{
"commandStartedEvent": {
"command": {
"aggregate": "coll0",
"pipeline": [
{
"$match": {
"$expr": {
"$eq": [
"$_id",
"$$id"
]
}
}
},
{
"$project": {
"_id": 0,
"x": "$$x",
"y": "$$y",
"rand": "$$rand"
}
}
],
"let": {
"id": 1,
"x": "foo",
"y": {
"$literal": "bar"
},
"rand": {
"$rand": {}
}
}
}
}
}
]
}
]
},
{
"description": "Aggregate with let option and dollar-prefixed $literal value",
"runOnRequirements": [
{
"minServerVersion": "5.0",
"topologies": [
"single",
"replicaset"
]
}
],
"operations": [
{
"name": "aggregate",
Expand Down
36 changes: 36 additions & 0 deletions test/spec/crud/unified/aggregate-let.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,45 @@ initialData: &initialData
- { _id: 1 }

tests:
# TODO: Once SERVER-57403 is resolved, this test can be removed in favor of
# the "dollar-prefixed $literal value" test below.
- description: "Aggregate with let option"
runOnRequirements:
- minServerVersion: "5.0"
operations:
- name: aggregate
object: *collection0
arguments:
pipeline: &pipeline0
# $match takes a query expression, so $expr is necessary to utilize
# an aggregate expression context and access "let" variables.
- $match: { $expr: { $eq: ["$_id", "$$id"] } }
- $project: { _id: 0, x: "$$x", y: "$$y", rand: "$$rand" }
# Values in "let" must be constant or closed expressions that do not
# depend on document values. This test demonstrates a basic constant
# value, a value wrapped with $literal (to avoid expression parsing),
# and a closed expression (e.g. $rand).
let: &let0
id: 1
x: foo
y: { $literal: "bar" }
rand: { $rand: {} }
expectResult:
- { x: "foo", y: "bar", rand: { $$type: "double" } }
expectEvents:
- client: *client0
events:
- commandStartedEvent:
command:
aggregate: *collection0Name
pipeline: *pipeline0
let: *let0

- description: "Aggregate with let option and dollar-prefixed $literal value"
runOnRequirements:
- minServerVersion: "5.0"
# TODO: Remove topology restrictions once SERVER-57403 is resolved
topologies: ["single", "replicaset"]
operations:
- name: aggregate
object: *collection0
Expand Down
Loading