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.
- package/README.md +5 -3
- package/dist/cjs/crypto.js +7 -20
- package/dist/cjs/crypto.js.map +1 -1
- package/dist/cjs/extend/aggregate.js +41 -0
- package/dist/cjs/extend/aggregate.js.map +1 -0
- package/dist/cjs/extend/query.js +64 -0
- package/dist/cjs/extend/query.js.map +1 -0
- package/dist/cjs/plugin.js +10 -61
- package/dist/cjs/plugin.js.map +1 -1
- package/dist/cjs/types/crypto.d.ts +0 -1
- package/dist/cjs/types/crypto.d.ts.map +1 -1
- package/dist/cjs/types/extend/aggregate.d.ts +4 -0
- package/dist/cjs/types/extend/aggregate.d.ts.map +1 -0
- package/dist/cjs/types/extend/query.d.ts +4 -0
- package/dist/cjs/types/extend/query.d.ts.map +1 -0
- package/dist/cjs/types/plugin.d.ts +9 -2
- package/dist/cjs/types/plugin.d.ts.map +1 -1
- package/dist/esm/crypto.js +6 -18
- package/dist/esm/crypto.js.map +1 -1
- package/dist/esm/extend/aggregate.js +37 -0
- package/dist/esm/extend/aggregate.js.map +1 -0
- package/dist/esm/extend/query.js +60 -0
- package/dist/esm/extend/query.js.map +1 -0
- package/dist/esm/plugin.js.map +1 -1
- package/dist/esm/plugin.mjs +10 -61
- package/dist/esm/types/crypto.d.ts +0 -1
- package/dist/esm/types/crypto.d.ts.map +1 -1
- package/dist/esm/types/extend/aggregate.d.ts +4 -0
- package/dist/esm/types/extend/aggregate.d.ts.map +1 -0
- package/dist/esm/types/extend/query.d.ts +4 -0
- package/dist/esm/types/extend/query.d.ts.map +1 -0
- package/dist/esm/types/plugin.d.ts +9 -2
- package/dist/esm/types/plugin.d.ts.map +1 -1
- package/package.json +5 -4
- package/src/crypto.ts +6 -23
- package/src/extend/aggregate.ts +55 -0
- package/src/extend/query.ts +80 -0
- package/src/plugin.ts +20 -79
- package/tests/cache-memory.test.ts +118 -0
- package/tests/{cache.test.ts → cache-redis.test.ts} +41 -28
- 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":"
|
|
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
|
+
"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": "
|
|
77
|
-
"lodash": "
|
|
78
|
-
"ms": "
|
|
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 =
|
|
25
|
-
const sortedStr = JSON.stringify(sortedObj)
|
|
26
|
-
|
|
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
|
-
|
|
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
|
-
|
|
43
|
-
|
|
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 (
|
|
116
|
-
|
|
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
|
-
|
|
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('
|
|
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.
|
|
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).
|
|
46
|
+
expect(user1?.name).toEqual(user2?.name)
|
|
46
47
|
})
|
|
47
48
|
|
|
48
|
-
it('should not use
|
|
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.
|
|
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.
|
|
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).
|
|
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).
|
|
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
|
-
|
|
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
|
})
|