diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 3089b69e90e..804597275f7 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -37,6 +37,14 @@ body: validations: required: true + - type: input + id: typescript-version + attributes: + label: Typescript version (if applicable) + placeholder: 4.8.x + validations: + required: false + - type: textarea id: description attributes: diff --git a/docs/guide.md b/docs/guide.md index 54651a6ed35..454dac270cf 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -744,9 +744,9 @@ The read option also allows us to specify _tag sets_. These tell the [driver](https://github.com/mongodb/node-mongodb-native/) from which members of the replica-set it should attempt to read. Read more about tag sets [here](http://docs.mongodb.org/manual/applications/replication/#tag-sets) and -[here](http://mongodb.github.com/node-mongodb-native/driver-articles/anintroductionto1_1and2_2.html#read-preferences). +[here](https://www.mongodb.com/docs/manual/core/read-preference). -_NOTE: you may also specify the driver read pref [strategy](http://mongodb.github.com/node-mongodb-native/api-generated/replset.html?highlight=strategy) +_NOTE: you may also specify the driver read preference [strategy](https://www.mongodb.com/docs/manual/core/read-preference/#read-preference-modes) option when connecting:_ ```javascript diff --git a/lib/aggregate.js b/lib/aggregate.js index 2426a256ca1..7178cce7526 100644 --- a/lib/aggregate.js +++ b/lib/aggregate.js @@ -111,11 +111,11 @@ Aggregate.prototype.model = function(model) { this._model = model; if (model.schema != null) { if (this.options.readPreference == null && - model.schema.options.read != null) { + model.schema.options.read != null) { this.options.readPreference = model.schema.options.read; } if (this.options.collation == null && - model.schema.options.collation != null) { + model.schema.options.collation != null) { this.options.collation = model.schema.options.collation; } } @@ -158,7 +158,7 @@ Aggregate.prototype.append = function() { * Requires MongoDB v3.4+ to work * * #### Example: - * + * * // adding new fields based on existing fields * aggregate.addFields({ * newField: '$b.nested' @@ -328,6 +328,28 @@ Aggregate.prototype.project = function(arg) { * @api public */ +/** + * Appends a new $fill operator to this aggregate pipeline. + * + * #### Example: + * + * aggregate.fill({ + * output: { + * bootsSold: { value: 0 }, + * sandalsSold: { value: 0 }, + * sneakersSold: { value: 0 } + * } + * }); + * + * @see $fill https://www.mongodb.com/docs/manual/reference/operator/aggregation/fill/ + * @method fill + * @memberOf Aggregate + * @instance + * @param {Object} arg $fill operator contents + * @return {Aggregate} + * @api public + */ + /** * Appends a new $geoNear operator to this aggregate pipeline. * @@ -366,7 +388,7 @@ Aggregate.prototype.near = function(arg) { * define methods */ -'group match skip limit out densify'.split(' ').forEach(function($operator) { +'group match skip limit out densify fill'.split(' ').forEach(function($operator) { Aggregate.prototype[$operator] = function(arg) { const op = {}; op['$' + $operator] = arg; @@ -702,7 +724,7 @@ Aggregate.prototype.readConcern = function(level) { Aggregate.prototype.redact = function(expression, thenExpr, elseExpr) { if (arguments.length === 3) { if ((typeof thenExpr === 'string' && !validRedactStringValues.has(thenExpr)) || - (typeof elseExpr === 'string' && !validRedactStringValues.has(elseExpr))) { + (typeof elseExpr === 'string' && !validRedactStringValues.has(elseExpr))) { throw new Error('If thenExpr or elseExpr is string, it must be either $$DESCEND, $$PRUNE or $$KEEP'); } @@ -1099,9 +1121,7 @@ Aggregate.prototype.catch = function(reject) { if (Symbol.asyncIterator != null) { Aggregate.prototype[Symbol.asyncIterator] = function() { - return this.cursor({ useMongooseAggCursor: true }). - transformNull(). - _transformForAsyncIterator(); + return this.cursor({ useMongooseAggCursor: true }).transformNull()._transformForAsyncIterator(); }; } diff --git a/lib/connection.js b/lib/connection.js index ccf14825d18..98fb871c115 100644 --- a/lib/connection.js +++ b/lib/connection.js @@ -1534,6 +1534,17 @@ Connection.prototype.syncIndexes = async function syncIndexes(options = {}) { * * Returns a new connection object, with the new db. * + * #### Example: + * + * // Connect to `initialdb` first + * const conn = await mongoose.createConnection('mongodb://localhost:27017/initialdb').asPromise(); + * + * // Creates an un-cached connection to `mydb` + * const db = conn.useDb('mydb'); + * // Creates a cached connection to `mydb2`. All calls to `conn.useDb('mydb2', { useCache: true })` will return the same + * // connection instance as opposed to creating a new connection instance + * const db2 = conn.useDb('mydb2', { useCache: true }); + * * @method useDb * @memberOf Connection * @param {String} name The database name diff --git a/test/aggregate.test.js b/test/aggregate.test.js index 168db7a7e34..91acbf8716e 100644 --- a/test/aggregate.test.js +++ b/test/aggregate.test.js @@ -389,6 +389,25 @@ describe('aggregate: ', function() { }); }); + describe('fill', function() { + it('works', function() { + const aggregate = new Aggregate(); + const obj = { + output: + { + bootsSold: { value: 0 }, + sandalsSold: { value: 0 }, + sneakersSold: { value: 0 } + } + }; + + aggregate.fill(obj); + + assert.equal(aggregate._pipeline.length, 1); + assert.deepEqual(aggregate._pipeline[0].$fill, obj); + }); + }); + describe('model()', function() { it('works', function() { const aggregate = new Aggregate(); diff --git a/test/types/PipelineStage.test.ts b/test/types/PipelineStage.test.ts index 29c4696caca..28670945721 100644 --- a/test/types/PipelineStage.test.ts +++ b/test/types/PipelineStage.test.ts @@ -311,6 +311,85 @@ const setWindowFields4: PipelineStage = { } }; +const setWindowFieldsLinearFill: PipelineStage = { + $setWindowFields: { + partitionBy: '$stock', + sortBy: { date: 1 }, + output: { + price: { $linearFill: '$price' } + } + } +}; + +const setWindowFieldsLocf: PipelineStage = { + $setWindowFields: { + partitionBy: '$stock', + sortBy: { date: 1 }, + output: { + price: { $locf: '$price' } + } + } +}; + +const fillWithOutput: PipelineStage = { + $fill: { + output: { + bootsSold: { value: 0 } + } + } +}; + +const fillWithPartitionBy: PipelineStage = { + $fill: { + partitionBy: 'date', + output: { + bootsSold: { value: 0 } + } + } +}; + +const fillWithPartitionByFields: PipelineStage = { + $fill: { + partitionByFields: ['date'], + output: { + bootsSold: { value: 0 } + } + } +}; + +const fillWithSortBy: PipelineStage = { + $fill: { + sortBy: { + date: -1 + }, + output: { + bootsSold: { value: 0 } + } + } +}; + +const fillWithOutputMethodLinear: PipelineStage = { + $fill: { + sortBy: { + date: -1 + }, + output: { + bootsSold: { method: 'linear' } + } + } +}; + +const fillWithOutputMethodLocf: PipelineStage = { + $fill: { + sortBy: { + date: -1 + }, + output: { + bootsSold: { method: 'locf' } + } + } +}; + const group1: PipelineStage = { $group: { _id: null, ageStdDev: { $stdDevSamp: '$age' } } }; const group2: PipelineStage = { $group: { diff --git a/types/aggregate.d.ts b/types/aggregate.d.ts index df5b4efdcc6..5352e22d0f6 100644 --- a/types/aggregate.d.ts +++ b/types/aggregate.d.ts @@ -133,6 +133,9 @@ declare module 'mongoose' { /** Combines multiple aggregation pipelines. */ facet(options: PipelineStage.Facet['$facet']): this; + /** Appends a new $fill operator to this aggregate pipeline */ + fill(arg: PipelineStage.Fill['$fill']): this; + /** Appends new custom $graphLookup operator(s) to this aggregate pipeline, performing a recursive search on a collection. */ graphLookup(options: PipelineStage.GraphLookup['$graphLookup']): this; diff --git a/types/expressions.d.ts b/types/expressions.d.ts index c2d83ed87dd..cf48bef8538 100644 --- a/types/expressions.d.ts +++ b/types/expressions.d.ts @@ -1180,6 +1180,26 @@ declare module 'mongoose' { $last: Expression; } + export interface LinearFill { + /** + * Fills null and missing fields in a window using linear interpolation based on surrounding field values. + * + * @version 5.3 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/linearFill + */ + $linearFill: Expression + } + + export interface Locf { + /** + * Last observation carried forward. Sets values for null and missing fields in a window to the last non-null value for the field. + * + * @version 5.2 + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/locf + */ + $locf: Expression + } + export interface Map { /** * Applies a subexpression to each element of an array and returns the array of resulting values in order. Accepts named parameters. @@ -2735,6 +2755,8 @@ declare module 'mongoose' { Expression.First | Expression.Integral | Expression.Last | + Expression.LinearFill | + Expression.Locf | Expression.Max | Expression.Min | Expression.Push | diff --git a/types/pipelinestage.d.ts b/types/pipelinestage.d.ts index 9086a4afb30..f58e4b61f27 100644 --- a/types/pipelinestage.d.ts +++ b/types/pipelinestage.d.ts @@ -8,7 +8,9 @@ declare module 'mongoose' { | PipelineStage.BucketAuto | PipelineStage.CollStats | PipelineStage.Count + | PipelineStage.Densify | PipelineStage.Facet + | PipelineStage.Fill | PipelineStage.GeoNear | PipelineStage.GraphLookup | PipelineStage.Group @@ -89,6 +91,16 @@ declare module 'mongoose' { } } + export interface Fill { + /** [`$fill` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/fill/) */ + $fill: { + partitionBy?: Expression, + partitionByFields?: string[], + sortBy?: Record, + output: Record + } + } + export interface Facet { /** [`$facet` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/facet/) */ $facet: Record; @@ -201,7 +213,7 @@ declare module 'mongoose' { export interface ReplaceWith { /** [`$replaceWith` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/replaceWith/) */ - $replaceWith: ObjectExpressionOperator; + $replaceWith: ObjectExpressionOperator | { [field: string]: Expression }; } export interface Sample {