diff --git a/.eslintrc.json b/.eslintrc.json index dce0f9f9892..13028b81375 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -6,6 +6,7 @@ "docs", "tools", "dist", + "website.js", "test/files/*", "benchmarks" ], @@ -22,6 +23,8 @@ "@typescript-eslint" ], "rules": { + "@typescript-eslint/triple-slash-reference": "off", + "spaced-comment": ["error", "always", { "markers": ["/"] }], "@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/ban-types": "off", "@typescript-eslint/no-unused-vars": "off", diff --git a/test/types/PipelineStage.test.ts b/test/types/PipelineStage.test.ts new file mode 100644 index 00000000000..0a7175de993 --- /dev/null +++ b/test/types/PipelineStage.test.ts @@ -0,0 +1,131 @@ +import type { PipelineStage } from 'mongoose'; +import { expectError, expectType } from 'tsd'; + +/** + * $addFields: + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/addFields/ + */ +const addFields1: PipelineStage = { + $addFields: { + totalHomework: { $sum: '$homework' }, + totalQuiz: { $sum: '$quiz' } + } +}; + +const addFields2: PipelineStage = { + $addFields: { + totalScore: + { $add: ['$totalHomework', '$totalQuiz', '$extraCredit'] } + } +}; + +const addFields3: PipelineStage = { + $addFields: { + 'specs.fuel_type': 'unleaded' + } +}; + +const addFields4: PipelineStage = { + $addFields: { cats: 20 } +}; + +const addFields5: PipelineStage = { + $addFields: { + _id: '$item', + item: 'fruit' + } +}; + +const addFields6: PipelineStage = { + $addFields: { homework: { $concatArrays: ['$homework', [7]] } } +}; + +/** + * $bucket + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/bucket/ + */ + +const bucket1: PipelineStage = { + $bucket: { + groupBy: '$year_born', // Field to group by + boundaries: [1840, 1850, 1860, 1870, 1880], // Boundaries for the buckets + default: 'Other', // Bucket id for documents which do not fall into a bucket + output: { // Output for each bucket + count: { $sum: 1 }, + artists: + { + $push: { + name: { $concat: ['$first_name', ' ', '$last_name'] }, + year_born: '$year_born' + } + } + } + } +}; + +const bucket2: PipelineStage = { + $facet: { // Top-level $facet stage + price: [ // Output field 1 + { + $bucket: { + groupBy: '$price', // Field to group by + boundaries: [0, 200, 400], // Boundaries for the buckets + default: 'Other', // Bucket id for documents which do not fall into a bucket + output: { // Output for each bucket + count: { $sum: 1 }, + artwork: { $push: { title: '$title', price: '$price' } }, + averagePrice: { $avg: '$price' } + } + } + } + ], + year: [ // Output field 2 + { + $bucket: { + groupBy: '$year', // Field to group by + boundaries: [1890, 1910, 1920, 1940], // Boundaries for the buckets + default: 'Unknown', // Bucket id for documents which do not fall into a bucket + output: { // Output for each bucket + count: { $sum: 1 }, + artwork: { $push: { title: '$title', year: '$year' } } + } + } + } + ] + } +}; + +/** + * $unionWith + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/unionWith/ + */ + +const unionWith1: PipelineStage = { $unionWith: { coll: 'warehouses', pipeline: [{ $project: { state: 1, _id: 0 } }] } }; +const unionWith2: PipelineStage = { $unionWith: { coll: 'sales2019q2', pipeline: [{ $set: { _id: '2019Q2' } }] } }; +const unionWith3: PipelineStage = { $unionWith: 'sales2019q2' }; +const unionWith4: PipelineStage = { $unionWith: { coll: 'sales2019q2', pipeline: [{ $group: { _id: '$item', total: { $sum: '$quantity' } } }] } }; + +/** + * $unset + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/unset/ + */ +const unset1: PipelineStage = { $unset: '' }; +const unset2: PipelineStage = { $unset: ['isbn', 'copies'] }; +const unset3: PipelineStage = { $unset: ['isbn', 'author.first', 'copies.warehouse'] }; + + +/** + * $unwind + * + * @see https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/ + */ + +const unwind1: PipelineStage = { $unwind: '$sizes' }; +const unwind2: PipelineStage = { $unwind: { path: '$sizes' } }; +const unwind3: PipelineStage = { $unwind: { path: '$sizes', includeArrayIndex: 'arrayIndex' } }; +const unwind4: PipelineStage = { $unwind: { path: '$sizes', preserveNullAndEmptyArrays: true } }; +const unwind5: PipelineStage = { $unwind: { path: '$sizes', preserveNullAndEmptyArrays: true } }; \ No newline at end of file diff --git a/types/PipelineStage.d.ts b/types/PipelineStage.d.ts new file mode 100644 index 00000000000..eab3bfc0545 --- /dev/null +++ b/types/PipelineStage.d.ts @@ -0,0 +1,272 @@ +declare module 'mongoose' { + /** + * [Stages reference](https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/#aggregation-pipeline-stages) + */ + export type PipelineStage = + | PipelineStage.AddFields + | PipelineStage.Bucket + | PipelineStage.BucketAuto + | PipelineStage.CollStats + | PipelineStage.Count + | PipelineStage.Facet + | PipelineStage.GeoNear + | PipelineStage.GraphLookup + | PipelineStage.Group + | PipelineStage.IndexStats + | PipelineStage.Limit + | PipelineStage.ListSessions + | PipelineStage.Lookup + | PipelineStage.Match + | PipelineStage.Merge + | PipelineStage.Out + | PipelineStage.PlanCacheStats + | PipelineStage.Project + | PipelineStage.Redact + | PipelineStage.ReplaceRoot + | PipelineStage.ReplaceWith + | PipelineStage.Sample + | PipelineStage.Search + | PipelineStage.Set + | PipelineStage.SetWindowFields + | PipelineStage.Skip + | PipelineStage.Sort + | PipelineStage.SortByCount + | PipelineStage.UnionWith + | PipelineStage.Unset + | PipelineStage.Unwind + + export namespace PipelineStage { + export interface AddFields { + /** [`$addFields` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/addFields/) */ + $addFields: Record + } + + export interface Bucket { + /** [`$bucket` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/bucket/) */ + $bucket: { + groupBy: any + boundaries: any[] + default?: any + output?: Record + } + } + + export interface BucketAuto { + /** [`$bucketAuto` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/bucketAuto/) */ + $bucketAuto: { + groupBy: any + buckets: number + output?: Record + granularity?: 'R5' | 'R10' | 'R20' | 'R40' | 'R80' | '1-2-5' | 'E6' | 'E12' | 'E24' | 'E48' | 'E96' | 'E192' | 'POWERSOF2' + } + } + + export interface CollStats { + /** [`$collStats` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/collStats/) */ + $collStats: { + latencyStats?: { histograms?: boolean } + storageStats?: { scale?: number } + count?: {} + queryExecStats?: {} + } + } + + export interface Count { + /** [`$count` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/count/) */ + $count: string + } + + export interface Facet { + /** [`$facet` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/facet/) */ + $facet: Record + } + + export type FacetPipelineStage = Exclude + + export interface GeoNear { + /** [`$geoNear` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/geoNear/) */ + $geoNear: { + near: { type: 'Point'; coordinates: [number, number] } | [number, number] + distanceField: string + distanceMultiplier?: number + includeLocs?: string + key?: string + maxDistance?: number + minDistance?: number + query?: AnyObject + spherical?: boolean + uniqueDocs?: boolean + } + } + + export interface GraphLookup { + /** [`$graphLookup` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/graphLookup/) */ + $graphLookup: { + from: string + startWith: any + connectFromField: string + connectToField: string + as: string + maxDepth?: number + depthField?: string + restrictSearchWithMatch?: AnyObject + } + } + + export interface Group { + /** [`$group` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/group) */ + $group: { _id: any } | { [key: string]: { [op in AccumulatorOperator]?: any } } + } + + export interface IndexStats { + /** [`$indexStats` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/indexStats/) */ + $indexStats: {} + } + + export interface Limit { + /** [`$limit` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/limit/) */ + $limit: number + } + + export interface ListSessions { + /** [`$listSessions` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/listSessions/) */ + $listSessions: { users?: { user: string; db: string }[] } | { allUsers?: true } + } + + export interface Lookup { + /** [`$lookup` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/) */ + $lookup: { + from: string + as: string + localField?: string + foreignField?: string + let?: Record + pipeline?: Exclude[] + } + } + + export interface Match { + /** [`$match` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/match/) */ + $match: AnyObject + } + + export interface Merge { + /** [`$merge` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/merge/) */ + $merge: { + into: string | { db: string; coll: string } + on?: string | string[] + let?: Record + whenMatched?: 'replace' | 'keepExisting' | 'merge' | 'fail' | Extract[] + whenNotMatched?: 'insert' | 'discard' | 'fail' + } + } + + export interface Out { + /** [`$out` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/out/) */ + $out: string | { db: string; coll: string } + } + + export interface PlanCacheStats { + /** [`$planCacheStats` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/planCacheStats/) */ + $planCacheStats: {} + } + + export interface Project { + /** [`$project` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/project/) */ + $project: { [field: string]: any } + } + + export interface Redact { + /** [`$redact` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/redact/) */ + $redact: any + } + + export interface ReplaceRoot { + /** [`$replaceRoot` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/replaceRoot/) */ + $replaceRoot: { newRoot: any } + } + + export interface ReplaceWith { + /** [`$replaceWith` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/replaceWith/) */ + $replaceWith: any + } + + export interface Sample { + /** [`$sample` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/sample/) */ + $sample: { size: number } + } + + export interface Search { + /** [`$search` reference](https://docs.atlas.mongodb.com/reference/atlas-search/query-syntax/) */ + $search: { + index?: string; + highlight?: { + /** [`highlightPath` reference](https://docs.atlas.mongodb.com/atlas-search/path-construction/#multiple-field-search) */ + path: string | string[] | { value: string, multi: string}; + maxCharsToExamine?: number; + maxNumPassages?: number; + }; + [key: string]: any; + } + } + + export interface Set { + /** [`$set` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/set/) */ + $set: Record + } + + export interface SetWindowFields { + /** [`$setWindowFields` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/setWindowFields/) */ + $setWindowFields: { + partitionBy?: any + sortBy?: Record + output: Record< + string, + { [op in WindowOperator]?: any } & { + window?: { + documents?: [string | number, string | number] + range?: [string | number, string | number] + unit?: 'year' | 'quarter' | 'month' | 'week' | 'day' | 'hour' | 'minute' | 'second' | 'millisecond' + } + } + > + } + } + + export interface Skip { + /** [`$skip` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/skip/) */ + $skip: number + } + + export interface Sort { + /** [`$sort` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/sort/) */ + $sort: Record + } + + export interface SortByCount { + /** [`$sortByCount` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/sortByCount/) */ + $sortByCount: any + } + + export interface UnionWith { + /** [`$unionWith` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/unionWith/) */ + $unionWith: + | string + | { coll: string; pipeline?: Exclude[] } + } + + export interface Unset { + /** [`$unset` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/unset/) */ + $unset: string | string[] + } + + export interface Unwind { + /** [`$unwind` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/) */ + $unwind: string | { path: string; includeArrayIndex?: string; preserveNullAndEmptyArrays?: boolean } + } + + type AccumulatorOperator = '$accumulator' | '$addToSet' | '$avg' | '$count' | '$first' | '$last' | '$max' | '$mergeObjects' | '$min' | '$push' | '$stdDevPop' | '$stdDevSamp' | '$sum' + + type WindowOperator = '$addToSet' | '$avg' | '$count' | '$covariancePop' | '$covarianceSamp' | '$derivative' | '$expMovingAvg' | '$integral' | '$max' | '$min' | '$push' | '$stdDevSamp' | '$stdDevPop' | '$sum' | '$first' | '$last' | '$shift' | '$denseRank' | '$documentNumber' | '$rank' + } + } diff --git a/types/index.d.ts b/types/index.d.ts index c46f07bdcd2..8bd724845cc 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -1,3 +1,5 @@ +/// + import events = require('events'); import mongodb = require('mongodb'); import mongoose = require('mongoose'); @@ -115,7 +117,7 @@ declare module 'mongoose' { /** Gets mongoose options */ export function get(key: K): MongooseOptions[K]; - /*! ignore */ + /* ! ignore */ type CompileModelOptions = { overwriteModels?: boolean, connection?: Connection }; /** @@ -2998,277 +3000,6 @@ declare module 'mongoose' { next(callback: Callback): void; } - /** - * [Stages reference](https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/#aggregation-pipeline-stages) - */ - export type PipelineStage = - | PipelineStage.AddFields - | PipelineStage.Bucket - | PipelineStage.BucketAuto - | PipelineStage.CollStats - | PipelineStage.Count - | PipelineStage.Facet - | PipelineStage.GeoNear - | PipelineStage.GraphLookup - | PipelineStage.Group - | PipelineStage.IndexStats - | PipelineStage.Limit - | PipelineStage.ListSessions - | PipelineStage.Lookup - | PipelineStage.Match - | PipelineStage.Merge - | PipelineStage.Out - | PipelineStage.PlanCacheStats - | PipelineStage.Project - | PipelineStage.Redact - | PipelineStage.ReplaceRoot - | PipelineStage.ReplaceWith - | PipelineStage.Sample - | PipelineStage.Search - | PipelineStage.Set - | PipelineStage.SetWindowFields - | PipelineStage.Skip - | PipelineStage.Sort - | PipelineStage.SortByCount - | PipelineStage.UnionWith - | PipelineStage.Unset - | PipelineStage.Unwind - - export namespace PipelineStage { - export interface AddFields { - /** [`$addFields` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/addFields/) */ - $addFields: Record - } - - export interface Bucket { - /** [`$bucket` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/bucket/) */ - $bucket: { - groupBy: any - boundaries: any[] - default?: any - output?: Record - } - } - - export interface BucketAuto { - /** [`$bucketAuto` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/bucketAuto/) */ - $bucketAuto: { - groupBy: any - buckets: number - output?: Record - granularity?: 'R5' | 'R10' | 'R20' | 'R40' | 'R80' | '1-2-5' | 'E6' | 'E12' | 'E24' | 'E48' | 'E96' | 'E192' | 'POWERSOF2' - } - } - - export interface CollStats { - /** [`$collStats` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/collStats/) */ - $collStats: { - latencyStats?: { histograms?: boolean } - storageStats?: { scale?: number } - count?: {} - queryExecStats?: {} - } - } - - export interface Count { - /** [`$count` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/count/) */ - $count: string - } - - export interface Facet { - /** [`$facet` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/facet/) */ - $facet: Record - } - - export type FacetPipelineStage = Exclude - - export interface GeoNear { - /** [`$geoNear` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/geoNear/) */ - $geoNear: { - near: { type: 'Point'; coordinates: [number, number] } | [number, number] - distanceField: string - distanceMultiplier?: number - includeLocs?: string - key?: string - maxDistance?: number - minDistance?: number - query?: AnyObject - spherical?: boolean - uniqueDocs?: boolean - } - } - - export interface GraphLookup { - /** [`$graphLookup` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/graphLookup/) */ - $graphLookup: { - from: string - startWith: any - connectFromField: string - connectToField: string - as: string - maxDepth?: number - depthField?: string - restrictSearchWithMatch?: AnyObject - } - } - - export interface Group { - /** [`$group` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/group) */ - $group: { _id: any } | { [key: string]: { [op in AccumulatorOperator]?: any } } - } - - export interface IndexStats { - /** [`$indexStats` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/indexStats/) */ - $indexStats: {} - } - - export interface Limit { - /** [`$limit` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/limit/) */ - $limit: number - } - - export interface ListSessions { - /** [`$listSessions` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/listSessions/) */ - $listSessions: { users?: { user: string; db: string }[] } | { allUsers?: true } - } - - export interface Lookup { - /** [`$lookup` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/) */ - $lookup: { - from: string - as: string - localField?: string - foreignField?: string - let?: Record - pipeline?: Exclude[] - } - } - - export interface Match { - /** [`$match` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/match/) */ - $match: AnyObject - } - - export interface Merge { - /** [`$merge` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/merge/) */ - $merge: { - into: string | { db: string; coll: string } - on?: string | string[] - let?: Record - whenMatched?: 'replace' | 'keepExisting' | 'merge' | 'fail' | Extract[] - whenNotMatched?: 'insert' | 'discard' | 'fail' - } - } - - export interface Out { - /** [`$out` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/out/) */ - $out: string | { db: string; coll: string } - } - - export interface PlanCacheStats { - /** [`$planCacheStats` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/planCacheStats/) */ - $planCacheStats: {} - } - - export interface Project { - /** [`$project` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/project/) */ - $project: { [field: string]: any } - } - - export interface Redact { - /** [`$redact` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/redact/) */ - $redact: any - } - - export interface ReplaceRoot { - /** [`$replaceRoot` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/replaceRoot/) */ - $replaceRoot: { newRoot: any } - } - - export interface ReplaceWith { - /** [`$replaceWith` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/replaceWith/) */ - $replaceWith: any - } - - export interface Sample { - /** [`$sample` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/sample/) */ - $sample: { size: number } - } - - export interface Search { - /** [`$search` reference](https://docs.atlas.mongodb.com/reference/atlas-search/query-syntax/) */ - $search: { - [key: string]: any - index?: string - highlight?: { - /** [`highlightPath` reference](https://docs.atlas.mongodb.com/atlas-search/path-construction/#multiple-field-search) */ - path: string | string[] | { value: string, multi: string}; - maxCharsToExamine?: number; - maxNumPassages?: number; - } - } - } - - export interface Set { - /** [`$set` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/set/) */ - $set: Record - } - - export interface SetWindowFields { - /** [`$setWindowFields` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/setWindowFields/) */ - $setWindowFields: { - partitionBy?: any - sortBy?: Record - output: Record< - string, - { [op in WindowOperator]?: any } & { - window?: { - documents?: [string | number, string | number] - range?: [string | number, string | number] - unit?: 'year' | 'quarter' | 'month' | 'week' | 'day' | 'hour' | 'minute' | 'second' | 'millisecond' - } - } - > - } - } - - export interface Skip { - /** [`$skip` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/skip/) */ - $skip: number - } - - export interface Sort { - /** [`$sort` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/sort/) */ - $sort: Record - } - - export interface SortByCount { - /** [`$sortByCount` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/sortByCount/) */ - $sortByCount: any - } - - export interface UnionWith { - /** [`$unionWith` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/unionWith/) */ - $unionWith: - | string - | { coll: string; pipeline?: Exclude[] } - } - - export interface Unset { - /** [`$unset` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/unset/) */ - $unset: string | string[] - } - - export interface Unwind { - /** [`$unwind` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/) */ - $unwind: string | { path: string; includeArrayIndex?: string; preserveNullAndEmptyArrays?: boolean } - } - - type AccumulatorOperator = '$accumulator' | '$addToSet' | '$avg' | '$count' | '$first' | '$last' | '$max' | '$mergeObjects' | '$min' | '$push' | '$stdDevPop' | '$stdDevSamp' | '$sum' - - type WindowOperator = '$addToSet' | '$avg' | '$count' | '$covariancePop' | '$covarianceSamp' | '$derivative' | '$expMovingAvg' | '$integral' | '$max' | '$min' | '$push' | '$stdDevSamp' | '$stdDevPop' | '$sum' | '$first' | '$last' | '$shift' | '$denseRank' | '$documentNumber' | '$rank' - } - class SchemaType { /** SchemaType constructor */ constructor(path: string, options?: AnyObject, instance?: string);