mongoose 6.1.9 → 6.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/.eslintrc.json +154 -0
  2. package/CHANGELOG.md +59 -0
  3. package/dist/browser.umd.js +233 -222
  4. package/index.js +5 -1
  5. package/lib/aggregate.js +23 -28
  6. package/lib/browserDocument.js +1 -1
  7. package/lib/cast/number.js +2 -3
  8. package/lib/cast.js +9 -7
  9. package/lib/connection.js +76 -24
  10. package/lib/cursor/AggregationCursor.js +12 -7
  11. package/lib/cursor/QueryCursor.js +11 -6
  12. package/lib/document.js +107 -107
  13. package/lib/drivers/node-mongodb-native/collection.js +12 -4
  14. package/lib/drivers/node-mongodb-native/connection.js +11 -0
  15. package/lib/error/cast.js +3 -2
  16. package/lib/error/index.js +11 -0
  17. package/lib/error/syncIndexes.js +30 -0
  18. package/lib/helpers/clone.js +51 -29
  19. package/lib/helpers/common.js +2 -2
  20. package/lib/helpers/cursor/eachAsync.js +18 -15
  21. package/lib/helpers/document/compile.js +7 -4
  22. package/lib/helpers/getFunctionName.js +6 -4
  23. package/lib/helpers/isMongooseObject.js +9 -8
  24. package/lib/helpers/isObject.js +4 -4
  25. package/lib/helpers/model/discriminator.js +2 -1
  26. package/lib/helpers/path/parentPaths.js +10 -5
  27. package/lib/helpers/populate/assignRawDocsToIdStructure.js +4 -2
  28. package/lib/helpers/populate/assignVals.js +8 -4
  29. package/lib/helpers/populate/getModelsMapForPopulate.js +4 -4
  30. package/lib/helpers/populate/markArraySubdocsPopulated.js +3 -1
  31. package/lib/helpers/populate/modelNamesFromRefPath.js +4 -3
  32. package/lib/helpers/printJestWarning.js +2 -2
  33. package/lib/helpers/projection/applyProjection.js +77 -0
  34. package/lib/helpers/projection/hasIncludedChildren.js +36 -0
  35. package/lib/helpers/projection/isExclusive.js +5 -2
  36. package/lib/helpers/projection/isInclusive.js +5 -1
  37. package/lib/helpers/query/cast$expr.js +279 -0
  38. package/lib/helpers/query/castUpdate.js +6 -2
  39. package/lib/helpers/query/isOperator.js +5 -2
  40. package/lib/helpers/schema/applyPlugins.js +11 -0
  41. package/lib/helpers/schema/getPath.js +4 -2
  42. package/lib/helpers/timestamps/setupTimestamps.js +3 -8
  43. package/lib/index.js +28 -26
  44. package/lib/internal.js +1 -1
  45. package/lib/model.js +161 -122
  46. package/lib/options/SchemaTypeOptions.js +1 -1
  47. package/lib/plugins/trackTransaction.js +5 -4
  48. package/lib/query.js +159 -146
  49. package/lib/queryhelpers.js +10 -10
  50. package/lib/schema/SubdocumentPath.js +4 -3
  51. package/lib/schema/array.js +30 -21
  52. package/lib/schema/buffer.js +1 -1
  53. package/lib/schema/date.js +1 -1
  54. package/lib/schema/decimal128.js +1 -1
  55. package/lib/schema/documentarray.js +9 -11
  56. package/lib/schema/number.js +1 -1
  57. package/lib/schema/objectid.js +2 -2
  58. package/lib/schema/string.js +4 -4
  59. package/lib/schema.js +9 -8
  60. package/lib/schematype.js +77 -30
  61. package/lib/types/ArraySubdocument.js +2 -1
  62. package/lib/types/DocumentArray/index.js +10 -27
  63. package/lib/types/DocumentArray/isMongooseDocumentArray.js +5 -0
  64. package/lib/types/DocumentArray/methods/index.js +15 -3
  65. package/lib/types/array/index.js +22 -21
  66. package/lib/types/array/isMongooseArray.js +5 -0
  67. package/lib/types/array/methods/index.js +22 -23
  68. package/lib/types/buffer.js +3 -3
  69. package/lib/types/map.js +2 -3
  70. package/lib/utils.js +10 -7
  71. package/package.json +19 -151
  72. package/tools/repl.js +1 -1
  73. package/tsconfig.json +8 -0
  74. package/types/PipelineStage.d.ts +272 -0
  75. package/{index.d.ts → types/index.d.ts} +156 -357
  76. package/lib/types/array/ArrayWrapper.js +0 -981
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mongoose",
3
3
  "description": "Mongoose MongoDB ODM",
4
- "version": "6.1.9",
4
+ "version": "6.2.2",
5
5
  "author": "Guillermo Rauch <guillermo@learnboost.com>",
6
6
  "keywords": [
7
7
  "mongodb",
@@ -19,14 +19,12 @@
19
19
  ],
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
- "@types/node": "< 17.0.6",
23
22
  "bson": "^4.2.2",
24
- "kareem": "2.3.3",
25
- "mongodb": "4.2.2",
23
+ "kareem": "2.3.4",
24
+ "mongodb": "4.3.1",
26
25
  "mpath": "0.8.4",
27
26
  "mquery": "4.0.2",
28
27
  "ms": "2.1.2",
29
- "regexp-clone": "1.0.0",
30
28
  "sift": "13.5.2"
31
29
  },
32
30
  "devDependencies": {
@@ -45,7 +43,6 @@
45
43
  "eslint": "8.5.0",
46
44
  "eslint-plugin-mocha-no-only": "1.1.0",
47
45
  "highlight.js": "9.18.3",
48
- "js-yaml": "4.1.0",
49
46
  "lodash.isequal": "4.5.0",
50
47
  "lodash.isequalwith": "4.4.0",
51
48
  "marked": "2.1.3",
@@ -58,6 +55,7 @@
58
55
  "q": "1.5.1",
59
56
  "rimraf": "2.6.3",
60
57
  "serve-handler": "6.1.3",
58
+ "tsd": "0.19.1",
61
59
  "typescript": "4.5.3",
62
60
  "uuid": "8.3.2",
63
61
  "webpack": "4.44.1"
@@ -72,12 +70,13 @@
72
70
  "release": "git pull && git push origin master --tags && npm publish",
73
71
  "release-legacy": "git pull origin 5.x && git push origin 5.x --tags && npm publish --tag legacy",
74
72
  "mongo": "node ./tools/repl.js",
75
- "test": "mocha --exit ./test/*.test.js ./test/typescript/main.test.js",
73
+ "test": "mocha --exit ./test/*.test.js",
74
+ "test-tsd": "tsd",
76
75
  "tdd": "mocha ./test/*.test.js ./test/typescript/main.test.js --inspect --watch --recursive --watch-files ./**/*.js",
77
76
  "test-coverage": "nyc --reporter=html --reporter=text npm test"
78
77
  },
79
78
  "main": "./index.js",
80
- "types": "./index.d.ts",
79
+ "types": "./types/index.d.ts",
81
80
  "engines": {
82
81
  "node": ">=12.0.0"
83
82
  },
@@ -98,149 +97,6 @@
98
97
  "test/**/*.js"
99
98
  ]
100
99
  },
101
- "eslintConfig": {
102
- "extends": [
103
- "eslint:recommended"
104
- ],
105
- "overrides": [
106
- {
107
- "files": [
108
- "**/*.{ts,tsx}"
109
- ],
110
- "extends": [
111
- "plugin:@typescript-eslint/eslint-recommended",
112
- "plugin:@typescript-eslint/recommended"
113
- ],
114
- "plugins": [
115
- "@typescript-eslint"
116
- ],
117
- "rules": {
118
- "@typescript-eslint/no-explicit-any": "off",
119
- "@typescript-eslint/ban-types": "off",
120
- "@typescript-eslint/no-unused-vars": "off",
121
- "@typescript-eslint/explicit-module-boundary-types": "off"
122
- }
123
- }
124
- ],
125
- "plugins": [
126
- "mocha-no-only"
127
- ],
128
- "parserOptions": {
129
- "ecmaVersion": 2020
130
- },
131
- "env": {
132
- "node": true,
133
- "es6": true
134
- },
135
- "rules": {
136
- "comma-style": "error",
137
- "indent": [
138
- "error",
139
- 2,
140
- {
141
- "SwitchCase": 1,
142
- "VariableDeclarator": 2
143
- }
144
- ],
145
- "keyword-spacing": "error",
146
- "no-whitespace-before-property": "error",
147
- "no-buffer-constructor": "warn",
148
- "no-console": "off",
149
- "no-constant-condition": "off",
150
- "no-multi-spaces": "error",
151
- "func-call-spacing": "error",
152
- "no-trailing-spaces": "error",
153
- "no-undef": "error",
154
- "no-unneeded-ternary": "error",
155
- "no-const-assign": "error",
156
- "no-useless-rename": "error",
157
- "no-dupe-keys": "error",
158
- "space-in-parens": [
159
- "error",
160
- "never"
161
- ],
162
- "spaced-comment": [
163
- "error",
164
- "always",
165
- {
166
- "block": {
167
- "markers": [
168
- "!"
169
- ],
170
- "balanced": true
171
- }
172
- }
173
- ],
174
- "key-spacing": [
175
- "error",
176
- {
177
- "beforeColon": false,
178
- "afterColon": true
179
- }
180
- ],
181
- "comma-spacing": [
182
- "error",
183
- {
184
- "before": false,
185
- "after": true
186
- }
187
- ],
188
- "array-bracket-spacing": 1,
189
- "arrow-spacing": [
190
- "error",
191
- {
192
- "before": true,
193
- "after": true
194
- }
195
- ],
196
- "object-curly-spacing": [
197
- "error",
198
- "always"
199
- ],
200
- "comma-dangle": [
201
- "error",
202
- "never"
203
- ],
204
- "no-unreachable": "error",
205
- "quotes": [
206
- "error",
207
- "single"
208
- ],
209
- "quote-props": [
210
- "error",
211
- "as-needed"
212
- ],
213
- "semi": "error",
214
- "no-extra-semi": "error",
215
- "semi-spacing": "error",
216
- "no-spaced-func": "error",
217
- "no-throw-literal": "error",
218
- "space-before-blocks": "error",
219
- "space-before-function-paren": [
220
- "error",
221
- "never"
222
- ],
223
- "space-infix-ops": "error",
224
- "space-unary-ops": "error",
225
- "no-var": "warn",
226
- "prefer-const": "warn",
227
- "strict": [
228
- "error",
229
- "global"
230
- ],
231
- "no-restricted-globals": [
232
- "error",
233
- {
234
- "name": "context",
235
- "message": "Don't use Mocha's global context"
236
- }
237
- ],
238
- "no-prototype-builtins": "off",
239
- "mocha-no-only/mocha-no-only": [
240
- "error"
241
- ]
242
- }
243
- },
244
100
  "config": {
245
101
  "mongodbMemoryServer": {
246
102
  "disablePostinstall": true
@@ -249,5 +105,17 @@
249
105
  "funding": {
250
106
  "type": "opencollective",
251
107
  "url": "https://opencollective.com/mongoose"
108
+ },
109
+ "tsd": {
110
+ "directory": "test/types",
111
+ "compilerOptions": {
112
+ "esModuleInterop": false,
113
+ "strict": true,
114
+ "allowSyntheticDefaultImports": true,
115
+ "strictPropertyInitialization": false,
116
+ "noImplicitAny": false,
117
+ "module": "commonjs",
118
+ "target": "ES2017"
119
+ }
252
120
  }
253
121
  }
package/tools/repl.js CHANGED
@@ -11,7 +11,7 @@ async function run () {
11
11
  // Create new instance
12
12
  const replSet = new ReplSet({
13
13
  binary: {
14
- version: process.argv[3]
14
+ version: process.argv[2]
15
15
  },
16
16
  instanceOpts: [
17
17
  // Set the expiry job in MongoDB to run every second
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "compilerOptions": {
3
+ "paths": {
4
+ "mongoose" : ["./types/index.d.ts"]
5
+ }
6
+ },
7
+ "strictNullChecks": true
8
+ }
@@ -0,0 +1,272 @@
1
+ declare module 'mongoose' {
2
+ /**
3
+ * [Stages reference](https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/#aggregation-pipeline-stages)
4
+ */
5
+ export type PipelineStage =
6
+ | PipelineStage.AddFields
7
+ | PipelineStage.Bucket
8
+ | PipelineStage.BucketAuto
9
+ | PipelineStage.CollStats
10
+ | PipelineStage.Count
11
+ | PipelineStage.Facet
12
+ | PipelineStage.GeoNear
13
+ | PipelineStage.GraphLookup
14
+ | PipelineStage.Group
15
+ | PipelineStage.IndexStats
16
+ | PipelineStage.Limit
17
+ | PipelineStage.ListSessions
18
+ | PipelineStage.Lookup
19
+ | PipelineStage.Match
20
+ | PipelineStage.Merge
21
+ | PipelineStage.Out
22
+ | PipelineStage.PlanCacheStats
23
+ | PipelineStage.Project
24
+ | PipelineStage.Redact
25
+ | PipelineStage.ReplaceRoot
26
+ | PipelineStage.ReplaceWith
27
+ | PipelineStage.Sample
28
+ | PipelineStage.Search
29
+ | PipelineStage.Set
30
+ | PipelineStage.SetWindowFields
31
+ | PipelineStage.Skip
32
+ | PipelineStage.Sort
33
+ | PipelineStage.SortByCount
34
+ | PipelineStage.UnionWith
35
+ | PipelineStage.Unset
36
+ | PipelineStage.Unwind
37
+
38
+ export namespace PipelineStage {
39
+ export interface AddFields {
40
+ /** [`$addFields` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/addFields/) */
41
+ $addFields: Record<string, any>
42
+ }
43
+
44
+ export interface Bucket {
45
+ /** [`$bucket` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/bucket/) */
46
+ $bucket: {
47
+ groupBy: any
48
+ boundaries: any[]
49
+ default?: any
50
+ output?: Record<string, { [op in AccumulatorOperator]?: any }>
51
+ }
52
+ }
53
+
54
+ export interface BucketAuto {
55
+ /** [`$bucketAuto` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/bucketAuto/) */
56
+ $bucketAuto: {
57
+ groupBy: any
58
+ buckets: number
59
+ output?: Record<string, { [op in AccumulatorOperator]?: any }>
60
+ granularity?: 'R5' | 'R10' | 'R20' | 'R40' | 'R80' | '1-2-5' | 'E6' | 'E12' | 'E24' | 'E48' | 'E96' | 'E192' | 'POWERSOF2'
61
+ }
62
+ }
63
+
64
+ export interface CollStats {
65
+ /** [`$collStats` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/collStats/) */
66
+ $collStats: {
67
+ latencyStats?: { histograms?: boolean }
68
+ storageStats?: { scale?: number }
69
+ count?: {}
70
+ queryExecStats?: {}
71
+ }
72
+ }
73
+
74
+ export interface Count {
75
+ /** [`$count` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/count/) */
76
+ $count: string
77
+ }
78
+
79
+ export interface Facet {
80
+ /** [`$facet` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/facet/) */
81
+ $facet: Record<string, FacetPipelineStage[]>
82
+ }
83
+
84
+ export type FacetPipelineStage = Exclude<PipelineStage, PipelineStage.CollStats | PipelineStage.Facet | PipelineStage.GeoNear | PipelineStage.IndexStats | PipelineStage.Out | PipelineStage.Merge | PipelineStage.PlanCacheStats>
85
+
86
+ export interface GeoNear {
87
+ /** [`$geoNear` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/geoNear/) */
88
+ $geoNear: {
89
+ near: { type: 'Point'; coordinates: [number, number] } | [number, number]
90
+ distanceField: string
91
+ distanceMultiplier?: number
92
+ includeLocs?: string
93
+ key?: string
94
+ maxDistance?: number
95
+ minDistance?: number
96
+ query?: AnyObject
97
+ spherical?: boolean
98
+ uniqueDocs?: boolean
99
+ }
100
+ }
101
+
102
+ export interface GraphLookup {
103
+ /** [`$graphLookup` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/graphLookup/) */
104
+ $graphLookup: {
105
+ from: string
106
+ startWith: any
107
+ connectFromField: string
108
+ connectToField: string
109
+ as: string
110
+ maxDepth?: number
111
+ depthField?: string
112
+ restrictSearchWithMatch?: AnyObject
113
+ }
114
+ }
115
+
116
+ export interface Group {
117
+ /** [`$group` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/group) */
118
+ $group: { _id: any } | { [key: string]: { [op in AccumulatorOperator]?: any } }
119
+ }
120
+
121
+ export interface IndexStats {
122
+ /** [`$indexStats` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/indexStats/) */
123
+ $indexStats: {}
124
+ }
125
+
126
+ export interface Limit {
127
+ /** [`$limit` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/limit/) */
128
+ $limit: number
129
+ }
130
+
131
+ export interface ListSessions {
132
+ /** [`$listSessions` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/listSessions/) */
133
+ $listSessions: { users?: { user: string; db: string }[] } | { allUsers?: true }
134
+ }
135
+
136
+ export interface Lookup {
137
+ /** [`$lookup` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/) */
138
+ $lookup: {
139
+ from: string
140
+ as: string
141
+ localField?: string
142
+ foreignField?: string
143
+ let?: Record<string, any>
144
+ pipeline?: Exclude<PipelineStage, PipelineStage.Merge | PipelineStage.Out | PipelineStage.Search>[]
145
+ }
146
+ }
147
+
148
+ export interface Match {
149
+ /** [`$match` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/match/) */
150
+ $match: AnyObject
151
+ }
152
+
153
+ export interface Merge {
154
+ /** [`$merge` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/merge/) */
155
+ $merge: {
156
+ into: string | { db: string; coll: string }
157
+ on?: string | string[]
158
+ let?: Record<string, any>
159
+ whenMatched?: 'replace' | 'keepExisting' | 'merge' | 'fail' | Extract<PipelineStage, PipelineStage.AddFields | PipelineStage.Set | PipelineStage.Project | PipelineStage.Unset | PipelineStage.ReplaceRoot | PipelineStage.ReplaceWith>[]
160
+ whenNotMatched?: 'insert' | 'discard' | 'fail'
161
+ }
162
+ }
163
+
164
+ export interface Out {
165
+ /** [`$out` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/out/) */
166
+ $out: string | { db: string; coll: string }
167
+ }
168
+
169
+ export interface PlanCacheStats {
170
+ /** [`$planCacheStats` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/planCacheStats/) */
171
+ $planCacheStats: {}
172
+ }
173
+
174
+ export interface Project {
175
+ /** [`$project` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/project/) */
176
+ $project: { [field: string]: any }
177
+ }
178
+
179
+ export interface Redact {
180
+ /** [`$redact` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/redact/) */
181
+ $redact: any
182
+ }
183
+
184
+ export interface ReplaceRoot {
185
+ /** [`$replaceRoot` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/replaceRoot/) */
186
+ $replaceRoot: { newRoot: any }
187
+ }
188
+
189
+ export interface ReplaceWith {
190
+ /** [`$replaceWith` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/replaceWith/) */
191
+ $replaceWith: any
192
+ }
193
+
194
+ export interface Sample {
195
+ /** [`$sample` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/sample/) */
196
+ $sample: { size: number }
197
+ }
198
+
199
+ export interface Search {
200
+ /** [`$search` reference](https://docs.atlas.mongodb.com/reference/atlas-search/query-syntax/) */
201
+ $search: {
202
+ index?: string;
203
+ highlight?: {
204
+ /** [`highlightPath` reference](https://docs.atlas.mongodb.com/atlas-search/path-construction/#multiple-field-search) */
205
+ path: string | string[] | { value: string, multi: string};
206
+ maxCharsToExamine?: number;
207
+ maxNumPassages?: number;
208
+ };
209
+ [key: string]: any;
210
+ }
211
+ }
212
+
213
+ export interface Set {
214
+ /** [`$set` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/set/) */
215
+ $set: Record<string, any>
216
+ }
217
+
218
+ export interface SetWindowFields {
219
+ /** [`$setWindowFields` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/setWindowFields/) */
220
+ $setWindowFields: {
221
+ partitionBy?: any
222
+ sortBy?: Record<string, 1 | -1>
223
+ output: Record<
224
+ string,
225
+ { [op in WindowOperator]?: any } & {
226
+ window?: {
227
+ documents?: [string | number, string | number]
228
+ range?: [string | number, string | number]
229
+ unit?: 'year' | 'quarter' | 'month' | 'week' | 'day' | 'hour' | 'minute' | 'second' | 'millisecond'
230
+ }
231
+ }
232
+ >
233
+ }
234
+ }
235
+
236
+ export interface Skip {
237
+ /** [`$skip` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/skip/) */
238
+ $skip: number
239
+ }
240
+
241
+ export interface Sort {
242
+ /** [`$sort` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/sort/) */
243
+ $sort: Record<string, 1 | -1 | { $meta: 'textScore' }>
244
+ }
245
+
246
+ export interface SortByCount {
247
+ /** [`$sortByCount` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/sortByCount/) */
248
+ $sortByCount: any
249
+ }
250
+
251
+ export interface UnionWith {
252
+ /** [`$unionWith` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/unionWith/) */
253
+ $unionWith:
254
+ | string
255
+ | { coll: string; pipeline?: Exclude<PipelineStage, PipelineStage.Out | PipelineStage.Merge>[] }
256
+ }
257
+
258
+ export interface Unset {
259
+ /** [`$unset` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/unset/) */
260
+ $unset: string | string[]
261
+ }
262
+
263
+ export interface Unwind {
264
+ /** [`$unwind` reference](https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/) */
265
+ $unwind: string | { path: string; includeArrayIndex?: string; preserveNullAndEmptyArrays?: boolean }
266
+ }
267
+
268
+ type AccumulatorOperator = '$accumulator' | '$addToSet' | '$avg' | '$count' | '$first' | '$last' | '$max' | '$mergeObjects' | '$min' | '$push' | '$stdDevPop' | '$stdDevSamp' | '$sum'
269
+
270
+ type WindowOperator = '$addToSet' | '$avg' | '$count' | '$covariancePop' | '$covarianceSamp' | '$derivative' | '$expMovingAvg' | '$integral' | '$max' | '$min' | '$push' | '$stdDevSamp' | '$stdDevPop' | '$sum' | '$first' | '$last' | '$shift' | '$denseRank' | '$documentNumber' | '$rank'
271
+ }
272
+ }