ts-cache-mongoose 0.0.3 → 0.0.5

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 (41) hide show
  1. package/README.md +5 -3
  2. package/dist/cjs/crypto.js +7 -20
  3. package/dist/cjs/crypto.js.map +1 -1
  4. package/dist/cjs/extend/aggregate.js +41 -0
  5. package/dist/cjs/extend/aggregate.js.map +1 -0
  6. package/dist/cjs/extend/query.js +64 -0
  7. package/dist/cjs/extend/query.js.map +1 -0
  8. package/dist/cjs/plugin.js +10 -61
  9. package/dist/cjs/plugin.js.map +1 -1
  10. package/dist/cjs/types/crypto.d.ts +0 -1
  11. package/dist/cjs/types/crypto.d.ts.map +1 -1
  12. package/dist/cjs/types/extend/aggregate.d.ts +4 -0
  13. package/dist/cjs/types/extend/aggregate.d.ts.map +1 -0
  14. package/dist/cjs/types/extend/query.d.ts +4 -0
  15. package/dist/cjs/types/extend/query.d.ts.map +1 -0
  16. package/dist/cjs/types/plugin.d.ts +9 -2
  17. package/dist/cjs/types/plugin.d.ts.map +1 -1
  18. package/dist/esm/crypto.js +6 -18
  19. package/dist/esm/crypto.js.map +1 -1
  20. package/dist/esm/extend/aggregate.js +37 -0
  21. package/dist/esm/extend/aggregate.js.map +1 -0
  22. package/dist/esm/extend/query.js +60 -0
  23. package/dist/esm/extend/query.js.map +1 -0
  24. package/dist/esm/plugin.js.map +1 -1
  25. package/dist/esm/plugin.mjs +10 -61
  26. package/dist/esm/types/crypto.d.ts +0 -1
  27. package/dist/esm/types/crypto.d.ts.map +1 -1
  28. package/dist/esm/types/extend/aggregate.d.ts +4 -0
  29. package/dist/esm/types/extend/aggregate.d.ts.map +1 -0
  30. package/dist/esm/types/extend/query.d.ts +4 -0
  31. package/dist/esm/types/extend/query.d.ts.map +1 -0
  32. package/dist/esm/types/plugin.d.ts +9 -2
  33. package/dist/esm/types/plugin.d.ts.map +1 -1
  34. package/package.json +5 -4
  35. package/src/crypto.ts +6 -23
  36. package/src/extend/aggregate.ts +55 -0
  37. package/src/extend/query.ts +80 -0
  38. package/src/plugin.ts +20 -79
  39. package/tests/cache-memory.test.ts +118 -0
  40. package/tests/{cache.test.ts → cache-redis.test.ts} +41 -28
  41. package/tests/crypto.test.ts +33 -8
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../../src/extend/query.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,KAAK,KAAK,MAAM,gBAAgB,CAAA;AAIvC,MAAM,CAAC,OAAO,UAAU,WAAW,CAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAwE3E"}
@@ -8,15 +8,22 @@ declare module 'mongoose' {
8
8
  _ttl?: string;
9
9
  getCacheTTL: (this: Query<ResultType, DocType, THelpers, RawDocType>) => string | undefined;
10
10
  op?: string;
11
- _fields?: unknown;
12
11
  _path?: unknown;
12
+ _fields?: unknown;
13
13
  _distinct?: unknown;
14
+ _conditions?: unknown;
15
+ }
16
+ interface Aggregate<ResultType> {
17
+ cache: (this: Aggregate<ResultType>, ttl?: string, customKey?: string) => this;
18
+ _key?: string;
19
+ getCacheKey: (this: Aggregate<ResultType>) => string;
20
+ _ttl?: string;
21
+ getCacheTTL: (this: Aggregate<ResultType>) => string | undefined;
14
22
  }
15
23
  }
16
24
  declare class CacheMongoose {
17
25
  private static instance;
18
26
  private cache;
19
- private cacheOptions;
20
27
  private constructor();
21
28
  static init(mongoose: Mongoose, cacheOptions: ICacheOptions): CacheMongoose;
22
29
  clear(customKey?: string): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/plugin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,KAAK,aAAa,MAAM,4BAA4B,CAAA;AAG3D,OAAO,QAAQ,UAAU,CAAC;IACxB,UAAU,KAAK,CAAC,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU;QACvD,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;QACzG,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,WAAW,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,KAAK,MAAM,CAAA;QAC/E,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,WAAW,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,KAAK,MAAM,GAAG,SAAS,CAAA;QAC3F,EAAE,CAAC,EAAE,MAAM,CAAA;QACX,OAAO,CAAC,EAAE,OAAO,CAAA;QACjB,KAAK,CAAC,EAAE,OAAO,CAAA;QACf,SAAS,CAAC,EAAE,OAAO,CAAA;KACpB;CACF;AAED,cAAM,aAAa;IAEjB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA2B;IAClD,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,YAAY,CAAgB;IAEpC,OAAO;WAKO,IAAI,CAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,aAAa,GAAG,aAAa;IAiFtE,KAAK,CAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOzC,KAAK,IAAK,OAAO,CAAC,IAAI,CAAC;CAKrC;AAED,eAAe,aAAa,CAAA"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/plugin.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,KAAK,aAAa,MAAM,4BAA4B,CAAA;AAK3D,OAAO,QAAQ,UAAU,CAAC;IACxB,UAAU,KAAK,CAAC,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU;QACvD,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;QACzG,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,WAAW,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,KAAK,MAAM,CAAA;QAC/E,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,WAAW,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,KAAK,MAAM,GAAG,SAAS,CAAA;QAC3F,EAAE,CAAC,EAAE,MAAM,CAAA;QACX,KAAK,CAAC,EAAE,OAAO,CAAA;QACf,OAAO,CAAC,EAAE,OAAO,CAAA;QACjB,SAAS,CAAC,EAAE,OAAO,CAAA;QACnB,WAAW,CAAC,EAAE,OAAO,CAAA;KACtB;IAED,UAAU,SAAS,CAAC,UAAU;QAC5B,KAAK,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,UAAU,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;QAC9E,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,WAAW,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,UAAU,CAAC,KAAK,MAAM,CAAA;QACpD,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,WAAW,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,UAAU,CAAC,KAAK,MAAM,GAAG,SAAS,CAAA;KACjE;CACF;AAED,cAAM,aAAa;IAEjB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA2B;IAClD,OAAO,CAAC,KAAK,CAAQ;IAErB,OAAO;WAIO,IAAI,CAAE,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,aAAa,GAAG,aAAa;IAetE,KAAK,CAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQzC,KAAK,IAAK,OAAO,CAAC,IAAI,CAAC;CAGrC;AAED,eAAe,aAAa,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-cache-mongoose",
3
- "version": "0.0.3",
3
+ "version": "0.0.5",
4
4
  "description": "Cache plugin for mongoose",
5
5
  "author": "Alex Eagle",
6
6
  "license": "MIT",
@@ -73,9 +73,10 @@
73
73
  "release": "npm install && npm run lint && npm run build && np"
74
74
  },
75
75
  "dependencies": {
76
- "ioredis": "^5.3.2",
77
- "lodash": "^4.17.21",
78
- "ms": "^2.1.3"
76
+ "ioredis": "5.3.2",
77
+ "lodash": "4.17.21",
78
+ "ms": "2.1.3",
79
+ "sort-keys": "4.2.0"
79
80
  },
80
81
  "devDependencies": {
81
82
  "@shelf/jest-mongodb": "4.1.7",
package/src/crypto.ts CHANGED
@@ -1,27 +1,10 @@
1
- import _ from 'lodash'
2
1
  import { createHash } from 'crypto'
3
-
4
- export function sortDeep (data: Record<string, unknown>[] | Record<string, unknown>): unknown {
5
- if (_.isObjectLike(data) && _.isArray(data)) {
6
- return data.map(sortDeep)
7
- }
8
-
9
- if (_.isObjectLike(data) && !_.isArray(data)) {
10
- const sortedKeys = _.keys(data).sort((a, b) => a.localeCompare(b))
11
- const sortedObj: Record<string, unknown> = {}
12
-
13
- sortedKeys.forEach((key) => {
14
- sortedObj[key] = sortDeep(data[key] as Record<string, unknown>[] | Record<string, unknown>)
15
- })
16
-
17
- return sortedObj
18
- }
19
-
20
- return data
21
- }
2
+ import sortKeys from 'sort-keys'
22
3
 
23
4
  export function getKey (data: Record<string, unknown>[] | Record<string, unknown>): string {
24
- const sortedObj = sortDeep(data)
25
- const sortedStr = JSON.stringify(sortedObj)
26
- return createHash('sha256').update(sortedStr).digest('hex')
5
+ const sortedObj = sortKeys(data, { deep: true })
6
+ const sortedStr = JSON.stringify(sortedObj, (_key, val: unknown) => {
7
+ return val instanceof RegExp ? String(val) : val
8
+ })
9
+ return createHash('sha1').update(sortedStr).digest('hex')
27
10
  }
@@ -0,0 +1,55 @@
1
+ import _ from 'lodash'
2
+
3
+ import type { Mongoose } from 'mongoose'
4
+ import type Cache from '../cache/Cache'
5
+
6
+ import { getKey } from '../crypto'
7
+
8
+ export default function extendQuery (mongoose: Mongoose, cache: Cache): void {
9
+ // eslint-disable-next-line @typescript-eslint/unbound-method
10
+ const mongooseExec = mongoose.Aggregate.prototype.exec
11
+
12
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
13
+ mongoose.Aggregate.prototype.getCacheKey = function () {
14
+ return getKey({
15
+ pipeline: this.pipeline()
16
+ })
17
+ }
18
+
19
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
20
+ mongoose.Aggregate.prototype.getCacheTTL = function () {
21
+ return this._ttl
22
+ }
23
+
24
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
25
+ mongoose.Aggregate.prototype.cache = function (ttl?: string, customKey?: string) {
26
+ this._ttl = ttl
27
+ this._key = customKey
28
+ return this
29
+ }
30
+
31
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
32
+ mongoose.Aggregate.prototype.exec = async function () {
33
+ if (!_.has(this, '_ttl')) {
34
+ return mongooseExec.apply(this)
35
+ }
36
+
37
+ const key = this.getCacheKey()
38
+ const ttl = this.getCacheTTL()
39
+
40
+ const resultCache = await cache.get(key).catch((err) => {
41
+ console.error(err)
42
+ })
43
+
44
+ if (resultCache) {
45
+ return resultCache
46
+ }
47
+
48
+ const result = await mongooseExec.call(this) as Record<string, unknown>[] | Record<string, unknown>
49
+ cache.set(key, result, ttl).catch((err) => {
50
+ console.error(err)
51
+ })
52
+
53
+ return result
54
+ }
55
+ }
@@ -0,0 +1,80 @@
1
+ import _ from 'lodash'
2
+
3
+ import type { Mongoose } from 'mongoose'
4
+ import type Cache from '../cache/Cache'
5
+
6
+ import { getKey } from '../crypto'
7
+
8
+ export default function extendQuery (mongoose: Mongoose, cache: Cache): void {
9
+ // eslint-disable-next-line @typescript-eslint/unbound-method
10
+ const mongooseExec = mongoose.Query.prototype.exec
11
+
12
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
13
+ mongoose.Query.prototype.getCacheKey = function () {
14
+ if (this._key) return this._key
15
+
16
+ const filter = this.getFilter()
17
+ const update = this.getUpdate()
18
+ const options = this.getOptions()
19
+ const mongooseOptions = this.mongooseOptions()
20
+
21
+ const data: Record<string, unknown> = {
22
+ model: this.model.modelName,
23
+ op: this.op,
24
+ filter,
25
+ update,
26
+ options,
27
+ mongooseOptions,
28
+ _path: this._path,
29
+ _fields: this._fields,
30
+ _distinct: this._distinct,
31
+ _conditions: this._conditions
32
+ }
33
+
34
+ return getKey(data)
35
+ }
36
+
37
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
38
+ mongoose.Query.prototype.getCacheTTL = function () {
39
+ return this._ttl
40
+ }
41
+
42
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
43
+ mongoose.Query.prototype.cache = function (ttl?: string, customKey?: string) {
44
+ this._ttl = ttl
45
+ this._key = customKey
46
+ return this
47
+ }
48
+
49
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
50
+ mongoose.Query.prototype.exec = async function () {
51
+ if (!_.has(this, '_ttl')) {
52
+ return mongooseExec.apply(this)
53
+ }
54
+
55
+ const key = this.getCacheKey()
56
+ const ttl = this.getCacheTTL()
57
+
58
+ const model = this.model.modelName
59
+
60
+ const resultCache = await cache.get(key).catch((err) => {
61
+ console.error(err)
62
+ })
63
+
64
+ if (resultCache) {
65
+ const constructor = mongoose.model(model)
66
+ if (_.isArray(resultCache)) {
67
+ return resultCache.map((item) => constructor.hydrate(item) as Record<string, unknown>)
68
+ } else {
69
+ return constructor.hydrate(resultCache) as Record<string, unknown>
70
+ }
71
+ }
72
+
73
+ const result = await mongooseExec.call(this) as Record<string, unknown>[] | Record<string, unknown>
74
+ cache.set(key, result, ttl).catch((err) => {
75
+ console.error(err)
76
+ })
77
+
78
+ return result
79
+ }
80
+ }
package/src/plugin.ts CHANGED
@@ -1,9 +1,10 @@
1
- import _ from 'lodash'
2
1
  import Cache from './cache/Cache'
3
2
 
4
3
  import type { Mongoose } from 'mongoose'
5
4
  import type ICacheOptions from './interfaces/ICacheOptions'
6
- import { getKey } from './crypto'
5
+
6
+ import extendQuery from './extend/query'
7
+ import extendAggregate from './extend/aggregate'
7
8
 
8
9
  declare module 'mongoose' {
9
10
  interface Query<ResultType, DocType, THelpers, RawDocType> {
@@ -13,9 +14,18 @@ declare module 'mongoose' {
13
14
  _ttl?: string
14
15
  getCacheTTL: (this: Query<ResultType, DocType, THelpers, RawDocType>) => string | undefined
15
16
  op?: string
16
- _fields?: unknown
17
17
  _path?: unknown
18
+ _fields?: unknown
18
19
  _distinct?: unknown
20
+ _conditions?: unknown
21
+ }
22
+
23
+ interface Aggregate<ResultType> {
24
+ cache: (this: Aggregate<ResultType>, ttl?: string, customKey?: string) => this
25
+ _key?: string
26
+ getCacheKey: (this: Aggregate<ResultType>) => string
27
+ _ttl?: string
28
+ getCacheTTL: (this: Aggregate<ResultType>) => string | undefined
19
29
  }
20
30
  }
21
31
 
@@ -23,105 +33,36 @@ class CacheMongoose {
23
33
  // eslint-disable-next-line no-use-before-define
24
34
  private static instance: CacheMongoose | undefined
25
35
  private cache!: Cache
26
- private cacheOptions!: ICacheOptions
27
36
 
28
37
  private constructor () {
29
38
  // Private constructor to prevent external instantiation
30
39
  }
31
40
 
32
- // eslint-disable-next-line sonarjs/cognitive-complexity
33
41
  public static init (mongoose: Mongoose, cacheOptions: ICacheOptions): CacheMongoose {
34
42
  if (typeof mongoose.Model.hydrate !== 'function') throw new Error('Cache is only compatible with versions of mongoose that implement the `model.hydrate` method')
35
43
  if (!this.instance) {
36
44
  this.instance = new CacheMongoose()
37
45
  this.instance.cache = new Cache(cacheOptions)
38
- this.instance.cacheOptions = cacheOptions
39
46
 
40
47
  const cache = this.instance.cache
41
48
 
42
- // eslint-disable-next-line @typescript-eslint/unbound-method
43
- const mongooseExec = mongoose.Query.prototype.exec
44
-
45
- // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
46
- mongoose.Query.prototype.getCacheKey = function () {
47
- if (this._key) return this._key
48
-
49
- const filter = this.getFilter()
50
- const update = this.getUpdate()
51
- const options = this.getOptions()
52
-
53
- const data: Record<string, unknown> = {
54
- model: this.model.modelName,
55
- op: this.op,
56
- filter,
57
- update,
58
- skip: options.skip,
59
- limit: options.limit,
60
- sort: options.sort,
61
- _fields: this._fields,
62
- _path: this._path,
63
- _distinct: this._distinct
64
- }
65
-
66
- return getKey(data)
67
- }
68
-
69
- // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
70
- mongoose.Query.prototype.getCacheTTL = function () {
71
- return this._ttl
72
- }
73
-
74
- // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
75
- mongoose.Query.prototype.cache = function (ttl?: string, customKey?: string) {
76
- this._ttl = ttl
77
- this._key = customKey
78
- return this
79
- }
80
-
81
- // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
82
- mongoose.Query.prototype.exec = async function () {
83
- if (!this._ttl) {
84
- return mongooseExec.apply(this)
85
- }
86
-
87
- const key = this.getCacheKey()
88
- const ttl = this.getCacheTTL()
89
-
90
- const model = this.model.modelName
91
-
92
- const resultCache = await cache.get(key)
93
- if (resultCache) {
94
- const constructor = mongoose.model(model)
95
- if (_.isArray(resultCache)) {
96
- return resultCache.map((item) => constructor.hydrate(item) as Record<string, unknown>)
97
- } else {
98
- return constructor.hydrate(resultCache) as Record<string, unknown>
99
- }
100
- }
101
-
102
- const result = await mongooseExec.call(this) as Record<string, unknown>[] | Record<string, unknown>
103
- cache.set(key, result, ttl).catch((err) => {
104
- console.error(err)
105
- })
106
-
107
- return result
108
- }
49
+ extendQuery(mongoose, cache)
50
+ extendAggregate(mongoose, cache)
109
51
  }
110
52
 
111
53
  return this.instance
112
54
  }
113
55
 
114
56
  public async clear (customKey?: string): Promise<void> {
115
- if (!customKey) {
116
- return this.cache.clear()
57
+ if (customKey) {
58
+ await this.cache.del(customKey)
59
+ } else {
60
+ await this.cache.clear()
117
61
  }
118
- return this.cache.del(customKey)
119
62
  }
120
63
 
121
64
  public async close (): Promise<void> {
122
- if (this.cacheOptions.engine === 'redis') {
123
- await this.cache.close()
124
- }
65
+ await this.cache.close()
125
66
  }
126
67
  }
127
68
 
@@ -0,0 +1,118 @@
1
+ import mongoose, { model } from 'mongoose'
2
+ import CacheMongoose from '../src/plugin'
3
+
4
+ import UserSchema from './schemas/UserSchema'
5
+
6
+ describe('CacheMongoose', () => {
7
+ const uri = `${globalThis.__MONGO_URI__}${globalThis.__MONGO_DB_NAME__}`
8
+ const User = model('User', UserSchema)
9
+
10
+ const cache = CacheMongoose.init(mongoose, {
11
+ engine: 'memory'
12
+ })
13
+
14
+ beforeAll(async () => {
15
+ await mongoose.connect(uri)
16
+ })
17
+
18
+ afterAll(async () => {
19
+ await mongoose.connection.close()
20
+ await cache.close()
21
+ })
22
+
23
+ beforeEach(async () => {
24
+ await mongoose.connection.collection('users').deleteMany({})
25
+ })
26
+
27
+ describe('memory scenarios', () => {
28
+ it('should use memory cache', async () => {
29
+ const user = await User.create({
30
+ name: 'John Doe',
31
+ role: 'admin'
32
+ })
33
+
34
+ const user1 = await User.findById(user._id).cache()
35
+ await User.findOneAndUpdate({ _id: user._id }, { name: 'John Doe 2' })
36
+ const user2 = await User.findById(user._id).cache()
37
+
38
+ expect(user1).not.toBeNull()
39
+ expect(user2).not.toBeNull()
40
+ expect(user1?._id).toEqual(user2?._id)
41
+ expect(user1?.name).toEqual(user2?.name)
42
+ })
43
+
44
+ it('should not use cache', async () => {
45
+ const user = await User.create({
46
+ name: 'John Doe',
47
+ role: 'admin'
48
+ })
49
+
50
+ const cache1 = await User.findById(user._id).cache().exec()
51
+ await User.findByIdAndUpdate(user._id, { name: 'John Doe 2' })
52
+ await cache.clear()
53
+ const cache2 = await User.findById(user._id).cache().exec()
54
+
55
+ expect(cache1).not.toBeNull()
56
+ expect(cache2).not.toBeNull()
57
+ expect(cache1?._id).toEqual(cache2?._id)
58
+ expect(cache1?.name).not.toEqual(cache2?.name)
59
+ })
60
+
61
+ it('should use memory cache with custom key', async () => {
62
+ const user = await User.create({
63
+ name: 'John Doe',
64
+ role: 'admin'
65
+ })
66
+
67
+ const cache1 = await User.findById(user._id).cache('1 minute', 'test-custom-key').exec()
68
+ await User.findOneAndUpdate({ _id: user._id }, { name: 'John Doe 2' })
69
+ const cache2 = await User.findById(user._id).cache('1 minute', 'test-custom-key').exec()
70
+
71
+ expect(cache1).not.toBeNull()
72
+ expect(cache2).not.toBeNull()
73
+ expect(cache1?._id).toEqual(cache2?._id)
74
+ expect(cache1?.name).toEqual(cache2?.name)
75
+ })
76
+
77
+ it('should use memory cache and clear custom key', async () => {
78
+ const user = await User.create({
79
+ name: 'John Doe',
80
+ role: 'admin'
81
+ })
82
+
83
+ const cache1 = await User.findById(user._id).cache('1 minute', 'test-custom-key-second').exec()
84
+ await User.updateOne({ _id: user._id }, { name: 'John Doe 2' })
85
+ await cache.clear('test-custom-key-second')
86
+ const cache2 = await User.findById(user._id).cache('1 minute', 'test-custom-key-second').exec()
87
+
88
+ expect(cache1).not.toBeNull()
89
+ expect(cache2).not.toBeNull()
90
+ expect(cache1?._id).toEqual(cache2?._id)
91
+ expect(cache1?.name).not.toEqual(cache2?.name)
92
+ })
93
+
94
+ it('should use memory cache and aggregate', async () => {
95
+ await User.create([
96
+ { name: 'John', role: 'admin' },
97
+ { name: 'Bob', role: 'admin' },
98
+ { name: 'Alice', role: 'user' }
99
+ ])
100
+
101
+ const cache1 = await User.aggregate([
102
+ { $match: { role: 'admin' } },
103
+ { $group: { _id: '$role', count: { $sum: 1 } } }
104
+ ]).cache()
105
+
106
+ await User.create({ name: 'Mark', role: 'admin' })
107
+
108
+ const cache2 = await User.aggregate([
109
+ { $match: { role: 'admin' } },
110
+ { $group: { _id: '$role', count: { $sum: 1 } } }
111
+ ]).cache()
112
+
113
+ expect(cache1).not.toBeNull()
114
+ expect(cache2).not.toBeNull()
115
+ expect(cache1?.[0].count).toEqual(cache2?.[0].count)
116
+ })
117
+ })
118
+ })
@@ -1,21 +1,28 @@
1
1
  import mongoose, { model } from 'mongoose'
2
2
  import CacheMongoose from '../src/plugin'
3
3
 
4
- import type ICacheOptions from '../src/interfaces/ICacheOptions'
5
-
6
4
  import UserSchema from './schemas/UserSchema'
7
5
 
8
- describe('CacheMongoose', () => {
6
+ describe('cache redis', () => {
9
7
  const uri = `${globalThis.__MONGO_URI__}${globalThis.__MONGO_DB_NAME__}`
10
-
11
8
  const User = model('User', UserSchema)
12
9
 
10
+ const cache = CacheMongoose.init(mongoose, {
11
+ engine: 'redis',
12
+ engineOptions: {
13
+ host: 'localhost',
14
+ port: 6379
15
+ },
16
+ defaultTTL: '6 minutes'
17
+ })
18
+
13
19
  beforeAll(async () => {
14
20
  await mongoose.connect(uri)
15
21
  })
16
22
 
17
23
  afterAll(async () => {
18
24
  await mongoose.connection.close()
25
+ await cache.close()
19
26
  })
20
27
 
21
28
  beforeEach(async () => {
@@ -23,12 +30,6 @@ describe('CacheMongoose', () => {
23
30
  })
24
31
 
25
32
  describe('memory scenarios', () => {
26
- const cacheOptions: ICacheOptions = {
27
- engine: 'memory'
28
- }
29
-
30
- const cache = CacheMongoose.init(mongoose, cacheOptions)
31
-
32
33
  it('should use memory cache', async () => {
33
34
  const user = await User.create({
34
35
  name: 'John Doe',
@@ -36,23 +37,23 @@ describe('CacheMongoose', () => {
36
37
  })
37
38
 
38
39
  const user1 = await User.findById(user._id).cache()
39
- await User.updateOne({ _id: user._id }, { name: 'John Doe 2' })
40
+ await User.findOneAndUpdate({ _id: user._id }, { name: 'John Doe 2' })
40
41
  const user2 = await User.findById(user._id).cache()
41
42
 
42
43
  expect(user1).not.toBeNull()
43
44
  expect(user2).not.toBeNull()
44
45
  expect(user1?._id).toEqual(user2?._id)
45
- expect(user1?.name).not.toEqual(user2?.name)
46
+ expect(user1?.name).toEqual(user2?.name)
46
47
  })
47
48
 
48
- it('should not use memory cache', async () => {
49
+ it('should not use cache', async () => {
49
50
  const user = await User.create({
50
51
  name: 'John Doe',
51
52
  role: 'admin'
52
53
  })
53
54
 
54
55
  const cache1 = await User.findById(user._id).cache().exec()
55
- await User.updateOne({ _id: user._id }, { name: 'John Doe 2' })
56
+ await User.findByIdAndUpdate(user._id, { name: 'John Doe 2' })
56
57
  await cache.clear()
57
58
  const cache2 = await User.findById(user._id).cache().exec()
58
59
 
@@ -69,7 +70,7 @@ describe('CacheMongoose', () => {
69
70
  })
70
71
 
71
72
  const cache1 = await User.findById(user._id).cache('1 minute', 'test-custom-key').exec()
72
- await User.updateOne({ _id: user._id }, { name: 'John Doe 2' })
73
+ await User.findOneAndUpdate({ _id: user._id }, { name: 'John Doe 2' })
73
74
  const cache2 = await User.findById(user._id).cache('1 minute', 'test-custom-key').exec()
74
75
 
75
76
  expect(cache1).not.toBeNull()
@@ -97,16 +98,6 @@ describe('CacheMongoose', () => {
97
98
  })
98
99
 
99
100
  it('should use redis cache', async () => {
100
- const cacheOptions: ICacheOptions = {
101
- engine: 'redis',
102
- engineOptions: {
103
- host: 'localhost',
104
- port: 6379
105
- },
106
- defaultTTL: '6 minutes'
107
- }
108
-
109
- const cache = CacheMongoose.init(mongoose, cacheOptions)
110
101
  expect(cache).toBeDefined()
111
102
 
112
103
  const user = await User.create({
@@ -121,7 +112,7 @@ describe('CacheMongoose', () => {
121
112
  expect(cache1).not.toBeNull()
122
113
  expect(cache2).not.toBeNull()
123
114
  expect(cache1?._id).toEqual(cache2?._id)
124
- expect(cache1?.name).not.toEqual(cache2?.name)
115
+ expect(cache1?.name).toEqual(cache2?.name)
125
116
 
126
117
  await User.create([
127
118
  {
@@ -141,8 +132,30 @@ describe('CacheMongoose', () => {
141
132
  expect(cache3).not.toBeNull()
142
133
  expect(cache4).not.toBeNull()
143
134
  expect(cache3?.length).toEqual(cache4?.length)
144
- expect(cache3?.[0].name).not.toEqual(cache4?.[0].name)
135
+ expect(cache3?.[0].name).toEqual(cache4?.[0].name)
136
+ })
137
+
138
+ it('should use redis cache and aggregate', async () => {
139
+ await User.create([
140
+ { name: 'John', role: 'admin' },
141
+ { name: 'Bob', role: 'admin' },
142
+ { name: 'Alice', role: 'user' }
143
+ ])
145
144
 
146
- cache.close()
145
+ const cache1 = await User.aggregate([
146
+ { $match: { role: 'admin' } },
147
+ { $group: { _id: '$role', count: { $sum: 1 } } }
148
+ ]).cache('1 minute')
149
+
150
+ await User.create({ name: 'Mark', role: 'admin' })
151
+
152
+ const cache2 = await User.aggregate([
153
+ { $match: { role: 'admin' } },
154
+ { $group: { _id: '$role', count: { $sum: 1 } } }
155
+ ]).cache('1 minute')
156
+
157
+ expect(cache1).not.toBeNull()
158
+ expect(cache2).not.toBeNull()
159
+ expect(cache1?.[0].count).toEqual(cache2?.[0].count)
147
160
  })
148
161
  })