ts-cache-mongoose 1.7.7 → 2.1.0
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 +88 -66
- package/biome.json +1 -1
- package/dist/index.cjs +140 -93
- package/dist/index.d.cts +48 -11
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +48 -11
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +140 -93
- package/dist/nest/index.cjs +113 -0
- package/dist/nest/index.d.cts +40 -0
- package/dist/nest/index.d.cts.map +1 -0
- package/dist/nest/index.d.mts +40 -0
- package/dist/nest/index.d.mts.map +1 -0
- package/dist/nest/index.mjs +110 -0
- package/package.json +44 -22
- package/src/cache/Cache.ts +5 -5
- package/src/cache/engine/MemoryCacheEngine.ts +4 -4
- package/src/cache/engine/RedisCacheEngine.ts +4 -4
- package/src/extend/aggregate.ts +5 -6
- package/src/extend/query.ts +5 -6
- package/src/index.ts +7 -7
- package/src/key.ts +2 -2
- package/src/ms.ts +66 -0
- package/src/nest/cache.module.ts +79 -0
- package/src/nest/cache.service.ts +37 -0
- package/src/nest/index.ts +4 -0
- package/src/nest/interfaces.ts +17 -0
- package/src/sort-keys.ts +38 -0
- package/src/types.ts +4 -4
- package/src/version.ts +1 -2
- package/tests/ms.test.ts +113 -0
- package/tests/nest.test.ts +158 -0
- package/tests/sort-keys.test.ts +80 -0
- package/tsconfig.json +3 -3
package/README.md
CHANGED
|
@@ -17,14 +17,14 @@ Cache query and aggregate in mongoose using in-memory or redis
|
|
|
17
17
|
|
|
18
18
|
ts-cache-mongoose is a plugin for mongoose
|
|
19
19
|
\
|
|
20
|
-
|
|
20
|
+
I need a way to cache mongoose queries and aggregations to improve application performance. It should support both in-memory and Redis cache engines, work with all major Node.js frameworks, and be easy to use with a simple `.cache()` method on queries and aggregations.
|
|
21
21
|
|
|
22
22
|
## Supports and tested with
|
|
23
23
|
|
|
24
24
|
```json
|
|
25
25
|
{
|
|
26
|
-
"node": "
|
|
27
|
-
"mongoose": ">=6.6.x || 7.x || 8.x",
|
|
26
|
+
"node": "20.x || 22.x || 24.x",
|
|
27
|
+
"mongoose": ">=6.6.x || 7.x || 8.x || 9.x",
|
|
28
28
|
}
|
|
29
29
|
```
|
|
30
30
|
|
|
@@ -42,105 +42,127 @@ Caching queries is a good way to improve performance of your application
|
|
|
42
42
|
|
|
43
43
|
## Installation
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
`mongoose` is a peer dependency — install it alongside `ts-cache-mongoose`.
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
|
-
npm install ts-cache-mongoose
|
|
49
|
-
pnpm add ts-cache-mongoose
|
|
50
|
-
yarn add ts-cache-mongoose
|
|
51
|
-
bun add ts-cache-mongoose
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
- This plugin requires mongoose `>=6.6.x || 7.x || 8.x` to be installed as a peer dependency
|
|
55
|
-
|
|
56
|
-
```bash
|
|
57
|
-
# For latest mongoose 6
|
|
58
|
-
npm install mongoose@6
|
|
59
|
-
pnpm add mongoose@6
|
|
60
|
-
yarn add mongoose@6
|
|
61
|
-
bun add mongoose@6
|
|
62
|
-
# For latest mongoose 7
|
|
63
|
-
npm install mongoose@7
|
|
64
|
-
pnpm add mongoose@7
|
|
65
|
-
yarn add mongoose@7
|
|
66
|
-
bun add mongoose@7
|
|
67
|
-
# For latest mongoose 8
|
|
68
|
-
npm install mongoose@8
|
|
69
|
-
pnpm add mongoose@8
|
|
70
|
-
yarn add mongoose@8
|
|
71
|
-
bun add mongoose@8
|
|
48
|
+
npm install ts-cache-mongoose mongoose
|
|
49
|
+
pnpm add ts-cache-mongoose mongoose
|
|
50
|
+
yarn add ts-cache-mongoose mongoose
|
|
51
|
+
bun add ts-cache-mongoose mongoose
|
|
72
52
|
```
|
|
73
53
|
|
|
74
54
|
## Example
|
|
75
55
|
|
|
56
|
+
Works with any Node.js framework — Express, Fastify, Koa, Hono, etc:
|
|
57
|
+
|
|
76
58
|
```typescript
|
|
77
|
-
// On your application startup
|
|
78
59
|
import mongoose from 'mongoose'
|
|
79
60
|
import cache from 'ts-cache-mongoose'
|
|
80
61
|
|
|
81
|
-
// In-memory
|
|
82
|
-
|
|
62
|
+
// In-memory
|
|
63
|
+
cache.init(mongoose, {
|
|
83
64
|
defaultTTL: '60 seconds',
|
|
84
65
|
engine: 'memory',
|
|
85
|
-
debug: true, // Debug mode enables hit/miss logs in console, not for production use
|
|
86
66
|
})
|
|
87
67
|
|
|
88
|
-
//
|
|
89
|
-
|
|
90
|
-
// Redis example
|
|
91
|
-
const instance = cache.init(mongoose, {
|
|
68
|
+
// Or Redis
|
|
69
|
+
cache.init(mongoose, {
|
|
92
70
|
defaultTTL: '60 seconds',
|
|
93
71
|
engine: 'redis',
|
|
94
72
|
engineOptions: {
|
|
95
73
|
host: 'localhost',
|
|
96
74
|
port: 6379,
|
|
97
75
|
},
|
|
98
|
-
debug: true, // Debug mode enables hit/miss logs in console, not for production use
|
|
99
76
|
})
|
|
100
77
|
|
|
101
|
-
// Connect to your database
|
|
102
78
|
mongoose.connect('mongodb://localhost:27017/my-database')
|
|
79
|
+
```
|
|
103
80
|
|
|
104
|
-
|
|
105
|
-
const users = await User.find({ role: 'user' }).cache('10 seconds').exec()
|
|
106
|
-
// Cache hit
|
|
107
|
-
const users = await User.find({ role: 'user' }).cache('10 seconds').exec()
|
|
81
|
+
### Query caching
|
|
108
82
|
|
|
83
|
+
```typescript
|
|
84
|
+
const users = await User.find({ role: 'user' }).cache('10 seconds').exec()
|
|
109
85
|
const book = await Book.findById(id).cache('1 hour').exec()
|
|
110
|
-
const
|
|
86
|
+
const count = await Book.countDocuments().cache('1 minute').exec()
|
|
111
87
|
const authors = await Book.distinct('author').cache('30 seconds').exec()
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Aggregate caching
|
|
112
91
|
|
|
92
|
+
```typescript
|
|
113
93
|
const books = await Book.aggregate([
|
|
114
|
-
{
|
|
115
|
-
|
|
116
|
-
genre: 'fantasy',
|
|
117
|
-
},
|
|
118
|
-
},
|
|
119
|
-
{
|
|
120
|
-
$group: {
|
|
121
|
-
_id: '$author',
|
|
122
|
-
count: { $sum: 1 },
|
|
123
|
-
},
|
|
124
|
-
},
|
|
125
|
-
{
|
|
126
|
-
$project: {
|
|
127
|
-
_id: 0,
|
|
128
|
-
author: '$_id',
|
|
129
|
-
count: 1,
|
|
130
|
-
},
|
|
131
|
-
}
|
|
94
|
+
{ $match: { genre: 'fantasy' } },
|
|
95
|
+
{ $group: { _id: '$author', count: { $sum: 1 } } },
|
|
132
96
|
]).cache('1 minute').exec()
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Cache invalidation
|
|
133
100
|
|
|
134
|
-
|
|
101
|
+
```typescript
|
|
102
|
+
const instance = cache.init(mongoose, { engine: 'memory', defaultTTL: '60 seconds' })
|
|
135
103
|
|
|
136
|
-
//
|
|
104
|
+
// Clear all cache
|
|
137
105
|
await instance.clear()
|
|
138
106
|
|
|
139
|
-
//
|
|
140
|
-
const user = await User.findById(
|
|
141
|
-
await instance.clear('
|
|
107
|
+
// Or use custom cache key
|
|
108
|
+
const user = await User.findById(id).cache('1 minute', 'user-key').exec()
|
|
109
|
+
await instance.clear('user-key')
|
|
142
110
|
```
|
|
143
111
|
|
|
112
|
+
### NestJS (because it's special)
|
|
113
|
+
|
|
114
|
+
Import `CacheModule` from `ts-cache-mongoose/nest`:
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
import { CacheModule } from 'ts-cache-mongoose/nest'
|
|
118
|
+
|
|
119
|
+
@Module({
|
|
120
|
+
imports: [
|
|
121
|
+
MongooseModule.forRoot(process.env.MONGO_URI),
|
|
122
|
+
CacheModule.forRoot({
|
|
123
|
+
engine: 'memory',
|
|
124
|
+
defaultTTL: '60 seconds',
|
|
125
|
+
}),
|
|
126
|
+
],
|
|
127
|
+
})
|
|
128
|
+
export class AppModule {}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
With `ConfigService`:
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
CacheModule.forRootAsync({
|
|
135
|
+
inject: [ConfigService],
|
|
136
|
+
useFactory: (config: ConfigService) => ({
|
|
137
|
+
engine: config.get('CACHE_ENGINE', 'memory'),
|
|
138
|
+
defaultTTL: config.get('CACHE_TTL', '60 seconds'),
|
|
139
|
+
}),
|
|
140
|
+
})
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Inject `CacheService` for programmatic cache clearing:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
import { CacheService } from 'ts-cache-mongoose/nest'
|
|
147
|
+
|
|
148
|
+
@Injectable()
|
|
149
|
+
export class SomeService {
|
|
150
|
+
constructor(private readonly cacheService: CacheService) {}
|
|
151
|
+
|
|
152
|
+
async clearUserCache() {
|
|
153
|
+
await this.cacheService.clear('user-cache-key')
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Contributing
|
|
159
|
+
|
|
160
|
+
Check [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
161
|
+
|
|
162
|
+
## License
|
|
163
|
+
|
|
164
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details
|
|
165
|
+
|
|
144
166
|
## Check my other projects
|
|
145
167
|
|
|
146
168
|
- [ts-migrate-mongoose](https://github.com/ilovepixelart/ts-migrate-mongoose) - Migration framework for mongoose
|
package/biome.json
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -1,28 +1,72 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var ms = require('ms');
|
|
4
3
|
var bson = require('bson');
|
|
5
4
|
var IORedis = require('ioredis');
|
|
6
5
|
var mongoose = require('mongoose');
|
|
7
|
-
var semver = require('semver');
|
|
8
6
|
var node_crypto = require('node:crypto');
|
|
9
|
-
var sortKeys = require('sort-keys');
|
|
10
7
|
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
const s = 1e3;
|
|
9
|
+
const m = s * 60;
|
|
10
|
+
const h = m * 60;
|
|
11
|
+
const d = h * 24;
|
|
12
|
+
const w = d * 7;
|
|
13
|
+
const y = d * 365.25;
|
|
14
|
+
const mo = y / 12;
|
|
15
|
+
const UNITS = {
|
|
16
|
+
milliseconds: 1,
|
|
17
|
+
millisecond: 1,
|
|
18
|
+
msecs: 1,
|
|
19
|
+
msec: 1,
|
|
20
|
+
ms: 1,
|
|
21
|
+
seconds: s,
|
|
22
|
+
second: s,
|
|
23
|
+
secs: s,
|
|
24
|
+
sec: s,
|
|
25
|
+
s,
|
|
26
|
+
minutes: m,
|
|
27
|
+
minute: m,
|
|
28
|
+
mins: m,
|
|
29
|
+
min: m,
|
|
30
|
+
m,
|
|
31
|
+
hours: h,
|
|
32
|
+
hour: h,
|
|
33
|
+
hrs: h,
|
|
34
|
+
hr: h,
|
|
35
|
+
h,
|
|
36
|
+
days: d,
|
|
37
|
+
day: d,
|
|
38
|
+
d,
|
|
39
|
+
weeks: w,
|
|
40
|
+
week: w,
|
|
41
|
+
w,
|
|
42
|
+
months: mo,
|
|
43
|
+
month: mo,
|
|
44
|
+
mo,
|
|
45
|
+
years: y,
|
|
46
|
+
year: y,
|
|
47
|
+
yrs: y,
|
|
48
|
+
yr: y,
|
|
49
|
+
y
|
|
13
50
|
};
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
51
|
+
const unitPattern = Object.keys(UNITS).sort((a, b) => b.length - a.length).join("|");
|
|
52
|
+
const RE = new RegExp(String.raw`^(-?(?:\d+)?\.?\d+)\s*(${unitPattern})?$`, "i");
|
|
53
|
+
const ms = (val) => {
|
|
54
|
+
const str = String(val);
|
|
55
|
+
if (str.length > 100) return Number.NaN;
|
|
56
|
+
const match = RE.exec(str);
|
|
57
|
+
if (!match) return Number.NaN;
|
|
58
|
+
const n = Number.parseFloat(match[1] ?? "");
|
|
59
|
+
const type = (match[2] ?? "ms").toLowerCase();
|
|
60
|
+
return n * (UNITS[type] ?? 0);
|
|
61
|
+
};
|
|
62
|
+
|
|
19
63
|
class MemoryCacheEngine {
|
|
64
|
+
#cache;
|
|
20
65
|
constructor() {
|
|
21
|
-
|
|
22
|
-
__privateSet$3(this, _cache, /* @__PURE__ */ new Map());
|
|
66
|
+
this.#cache = /* @__PURE__ */ new Map();
|
|
23
67
|
}
|
|
24
68
|
get(key) {
|
|
25
|
-
const item =
|
|
69
|
+
const item = this.#cache.get(key);
|
|
26
70
|
if (!item || item.expiresAt < Date.now()) {
|
|
27
71
|
this.del(key);
|
|
28
72
|
return void 0;
|
|
@@ -30,25 +74,24 @@ class MemoryCacheEngine {
|
|
|
30
74
|
return item.value;
|
|
31
75
|
}
|
|
32
76
|
set(key, value, ttl) {
|
|
33
|
-
const givenTTL =
|
|
77
|
+
const givenTTL = ttl == null ? void 0 : ms(ttl);
|
|
34
78
|
const actualTTL = givenTTL ?? Number.POSITIVE_INFINITY;
|
|
35
|
-
|
|
79
|
+
this.#cache.set(key, {
|
|
36
80
|
value,
|
|
37
81
|
expiresAt: Date.now() + actualTTL
|
|
38
82
|
});
|
|
39
83
|
}
|
|
40
84
|
del(key) {
|
|
41
|
-
|
|
85
|
+
this.#cache.delete(key);
|
|
42
86
|
}
|
|
43
87
|
clear() {
|
|
44
|
-
|
|
88
|
+
this.#cache.clear();
|
|
45
89
|
}
|
|
46
90
|
close() {
|
|
47
91
|
}
|
|
48
92
|
}
|
|
49
|
-
_cache = new WeakMap();
|
|
50
93
|
|
|
51
|
-
const isMongooseLessThan7 =
|
|
94
|
+
const isMongooseLessThan7 = Number.parseInt(mongoose.version, 10) < 7;
|
|
52
95
|
const convertToObject = (value) => {
|
|
53
96
|
if (isMongooseLessThan7) {
|
|
54
97
|
if (value != null && typeof value === "object" && !Array.isArray(value) && value.toObject) {
|
|
@@ -61,23 +104,15 @@ const convertToObject = (value) => {
|
|
|
61
104
|
return value;
|
|
62
105
|
};
|
|
63
106
|
|
|
64
|
-
var __typeError$2 = (msg) => {
|
|
65
|
-
throw TypeError(msg);
|
|
66
|
-
};
|
|
67
|
-
var __accessCheck$2 = (obj, member, msg) => member.has(obj) || __typeError$2("Cannot " + msg);
|
|
68
|
-
var __privateGet$2 = (obj, member, getter) => (__accessCheck$2(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
69
|
-
var __privateAdd$2 = (obj, member, value) => member.has(obj) ? __typeError$2("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
70
|
-
var __privateSet$2 = (obj, member, value, setter) => (__accessCheck$2(obj, member, "write to private field"), member.set(obj, value), value);
|
|
71
|
-
var _client;
|
|
72
107
|
class RedisCacheEngine {
|
|
108
|
+
#client;
|
|
73
109
|
constructor(options) {
|
|
74
|
-
__privateAdd$2(this, _client);
|
|
75
110
|
options.keyPrefix ??= "cache-mongoose:";
|
|
76
|
-
|
|
111
|
+
this.#client = new IORedis(options);
|
|
77
112
|
}
|
|
78
113
|
async get(key) {
|
|
79
114
|
try {
|
|
80
|
-
const value = await
|
|
115
|
+
const value = await this.#client.get(key);
|
|
81
116
|
if (value === null) {
|
|
82
117
|
return void 0;
|
|
83
118
|
}
|
|
@@ -89,95 +124,116 @@ class RedisCacheEngine {
|
|
|
89
124
|
}
|
|
90
125
|
async set(key, value, ttl) {
|
|
91
126
|
try {
|
|
92
|
-
const givenTTL =
|
|
127
|
+
const givenTTL = ttl == null ? void 0 : ms(ttl);
|
|
93
128
|
const actualTTL = givenTTL ?? Number.POSITIVE_INFINITY;
|
|
94
129
|
const serializedValue = bson.EJSON.stringify(convertToObject(value));
|
|
95
|
-
await
|
|
130
|
+
await this.#client.setex(key, Math.ceil(actualTTL / 1e3), serializedValue);
|
|
96
131
|
} catch (err) {
|
|
97
132
|
console.error(err);
|
|
98
133
|
}
|
|
99
134
|
}
|
|
100
135
|
async del(key) {
|
|
101
|
-
await
|
|
136
|
+
await this.#client.del(key);
|
|
102
137
|
}
|
|
103
138
|
async clear() {
|
|
104
|
-
await
|
|
139
|
+
await this.#client.flushdb();
|
|
105
140
|
}
|
|
106
141
|
async close() {
|
|
107
|
-
await
|
|
142
|
+
await this.#client.quit();
|
|
108
143
|
}
|
|
109
144
|
}
|
|
110
|
-
_client = new WeakMap();
|
|
111
145
|
|
|
112
|
-
var __typeError$1 = (msg) => {
|
|
113
|
-
throw TypeError(msg);
|
|
114
|
-
};
|
|
115
|
-
var __accessCheck$1 = (obj, member, msg) => member.has(obj) || __typeError$1("Cannot " + msg);
|
|
116
|
-
var __privateGet$1 = (obj, member, getter) => (__accessCheck$1(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
117
|
-
var __privateAdd$1 = (obj, member, value) => member.has(obj) ? __typeError$1("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
118
|
-
var __privateSet$1 = (obj, member, value, setter) => (__accessCheck$1(obj, member, "write to private field"), member.set(obj, value), value);
|
|
119
|
-
var _engine, _defaultTTL, _debug, _engines;
|
|
120
146
|
class Cache {
|
|
147
|
+
#engine;
|
|
148
|
+
#defaultTTL;
|
|
149
|
+
#debug;
|
|
150
|
+
#engines = ["memory", "redis"];
|
|
121
151
|
constructor(cacheOptions) {
|
|
122
|
-
|
|
123
|
-
__privateAdd$1(this, _defaultTTL);
|
|
124
|
-
__privateAdd$1(this, _debug);
|
|
125
|
-
__privateAdd$1(this, _engines, ["memory", "redis"]);
|
|
126
|
-
if (!__privateGet$1(this, _engines).includes(cacheOptions.engine)) {
|
|
152
|
+
if (!this.#engines.includes(cacheOptions.engine)) {
|
|
127
153
|
throw new Error(`Invalid engine name: ${cacheOptions.engine}`);
|
|
128
154
|
}
|
|
129
155
|
if (cacheOptions.engine === "redis" && !cacheOptions.engineOptions) {
|
|
130
156
|
throw new Error(`Engine options are required for ${cacheOptions.engine} engine`);
|
|
131
157
|
}
|
|
132
158
|
cacheOptions.defaultTTL ??= "1 minute";
|
|
133
|
-
|
|
159
|
+
this.#defaultTTL = ms(cacheOptions.defaultTTL);
|
|
134
160
|
if (cacheOptions.engine === "redis" && cacheOptions.engineOptions) {
|
|
135
|
-
|
|
161
|
+
this.#engine = new RedisCacheEngine(cacheOptions.engineOptions);
|
|
136
162
|
}
|
|
137
163
|
if (cacheOptions.engine === "memory") {
|
|
138
|
-
|
|
164
|
+
this.#engine = new MemoryCacheEngine();
|
|
139
165
|
}
|
|
140
|
-
|
|
166
|
+
this.#debug = cacheOptions.debug === true;
|
|
141
167
|
}
|
|
142
168
|
async get(key) {
|
|
143
|
-
const cacheEntry = await
|
|
144
|
-
if (
|
|
169
|
+
const cacheEntry = await this.#engine.get(key);
|
|
170
|
+
if (this.#debug) {
|
|
145
171
|
const cacheHit = cacheEntry == null ? "MISS" : "HIT";
|
|
146
172
|
console.log(`[ts-cache-mongoose] GET '${key}' - ${cacheHit}`);
|
|
147
173
|
}
|
|
148
174
|
return cacheEntry;
|
|
149
175
|
}
|
|
150
176
|
async set(key, value, ttl) {
|
|
151
|
-
const givenTTL =
|
|
152
|
-
const actualTTL = givenTTL ??
|
|
153
|
-
await
|
|
154
|
-
if (
|
|
177
|
+
const givenTTL = ttl == null ? null : ms(ttl);
|
|
178
|
+
const actualTTL = givenTTL ?? this.#defaultTTL;
|
|
179
|
+
await this.#engine.set(key, value, actualTTL);
|
|
180
|
+
if (this.#debug) {
|
|
155
181
|
console.log(`[ts-cache-mongoose] SET '${key}' - ttl: ${actualTTL.toFixed(0)} ms`);
|
|
156
182
|
}
|
|
157
183
|
}
|
|
158
184
|
async del(key) {
|
|
159
|
-
await
|
|
160
|
-
if (
|
|
185
|
+
await this.#engine.del(key);
|
|
186
|
+
if (this.#debug) {
|
|
161
187
|
console.log(`[ts-cache-mongoose] DEL '${key}'`);
|
|
162
188
|
}
|
|
163
189
|
}
|
|
164
190
|
async clear() {
|
|
165
|
-
await
|
|
166
|
-
if (
|
|
191
|
+
await this.#engine.clear();
|
|
192
|
+
if (this.#debug) {
|
|
167
193
|
console.log("[ts-cache-mongoose] CLEAR");
|
|
168
194
|
}
|
|
169
195
|
}
|
|
170
196
|
async close() {
|
|
171
|
-
return
|
|
197
|
+
return this.#engine.close();
|
|
172
198
|
}
|
|
173
199
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
200
|
+
|
|
201
|
+
const isPlainObject = (value) => {
|
|
202
|
+
if (typeof value !== "object" || value === null) return false;
|
|
203
|
+
const proto = Object.getPrototypeOf(value);
|
|
204
|
+
return proto === Object.prototype || proto === null;
|
|
205
|
+
};
|
|
206
|
+
const sortKeys = (input) => {
|
|
207
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
208
|
+
const sortObject = (obj) => {
|
|
209
|
+
if (seen.has(obj)) return obj;
|
|
210
|
+
seen.add(obj);
|
|
211
|
+
const sorted = {};
|
|
212
|
+
for (const key of Object.keys(obj).sort((a, b) => a.localeCompare(b))) {
|
|
213
|
+
const value = obj[key];
|
|
214
|
+
if (Array.isArray(value)) {
|
|
215
|
+
sorted[key] = sortArray(value);
|
|
216
|
+
} else if (isPlainObject(value)) {
|
|
217
|
+
sorted[key] = sortObject(value);
|
|
218
|
+
} else {
|
|
219
|
+
sorted[key] = value;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return sorted;
|
|
223
|
+
};
|
|
224
|
+
const sortArray = (arr) => {
|
|
225
|
+
return arr.map((item) => {
|
|
226
|
+
if (Array.isArray(item)) return sortArray(item);
|
|
227
|
+
if (isPlainObject(item)) return sortObject(item);
|
|
228
|
+
return item;
|
|
229
|
+
});
|
|
230
|
+
};
|
|
231
|
+
if (Array.isArray(input)) return sortArray(input);
|
|
232
|
+
return sortObject(input);
|
|
233
|
+
};
|
|
178
234
|
|
|
179
235
|
function getKey(data) {
|
|
180
|
-
const sortedObj = sortKeys(data
|
|
236
|
+
const sortedObj = sortKeys(data);
|
|
181
237
|
const sortedStr = JSON.stringify(sortedObj, (_, val) => {
|
|
182
238
|
return val instanceof RegExp ? String(val) : val;
|
|
183
239
|
});
|
|
@@ -192,7 +248,7 @@ function extendAggregate(mongoose, cache) {
|
|
|
192
248
|
pipeline: this.pipeline()
|
|
193
249
|
});
|
|
194
250
|
};
|
|
195
|
-
mongoose.Aggregate.prototype.
|
|
251
|
+
mongoose.Aggregate.prototype.getDuration = function() {
|
|
196
252
|
return this._ttl;
|
|
197
253
|
};
|
|
198
254
|
mongoose.Aggregate.prototype.cache = function(ttl, customKey) {
|
|
@@ -201,11 +257,11 @@ function extendAggregate(mongoose, cache) {
|
|
|
201
257
|
return this;
|
|
202
258
|
};
|
|
203
259
|
mongoose.Aggregate.prototype.exec = async function(...args) {
|
|
204
|
-
if (!Object.
|
|
260
|
+
if (!Object.hasOwn(this, "_ttl")) {
|
|
205
261
|
return mongooseExec.apply(this, args);
|
|
206
262
|
}
|
|
207
263
|
const key = this.getCacheKey();
|
|
208
|
-
const ttl = this.
|
|
264
|
+
const ttl = this.getDuration();
|
|
209
265
|
const resultCache = await cache.get(key).catch((err) => {
|
|
210
266
|
console.error(err);
|
|
211
267
|
});
|
|
@@ -241,7 +297,7 @@ function extendQuery(mongoose, cache) {
|
|
|
241
297
|
_conditions: this._conditions
|
|
242
298
|
});
|
|
243
299
|
};
|
|
244
|
-
mongoose.Query.prototype.
|
|
300
|
+
mongoose.Query.prototype.getDuration = function() {
|
|
245
301
|
return this._ttl;
|
|
246
302
|
};
|
|
247
303
|
mongoose.Query.prototype.cache = function(ttl, customKey) {
|
|
@@ -250,11 +306,11 @@ function extendQuery(mongoose, cache) {
|
|
|
250
306
|
return this;
|
|
251
307
|
};
|
|
252
308
|
mongoose.Query.prototype.exec = async function(...args) {
|
|
253
|
-
if (!Object.
|
|
309
|
+
if (!Object.hasOwn(this, "_ttl")) {
|
|
254
310
|
return mongooseExec.apply(this, args);
|
|
255
311
|
}
|
|
256
312
|
const key = this.getCacheKey();
|
|
257
|
-
const ttl = this.
|
|
313
|
+
const ttl = this.getDuration();
|
|
258
314
|
const mongooseOptions = this.mongooseOptions();
|
|
259
315
|
const isCount = this.op?.includes("count") ?? false;
|
|
260
316
|
const isDistinct = this.op === "distinct";
|
|
@@ -282,26 +338,20 @@ function extendQuery(mongoose, cache) {
|
|
|
282
338
|
};
|
|
283
339
|
}
|
|
284
340
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
289
|
-
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
290
|
-
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
291
|
-
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), member.set(obj, value), value);
|
|
292
|
-
var _instance;
|
|
293
|
-
const _CacheMongoose = class _CacheMongoose {
|
|
341
|
+
class CacheMongoose {
|
|
342
|
+
static #instance;
|
|
343
|
+
cache;
|
|
294
344
|
constructor() {
|
|
295
345
|
}
|
|
296
346
|
static init(mongoose, cacheOptions) {
|
|
297
|
-
if (!
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
const cache =
|
|
347
|
+
if (!CacheMongoose.#instance) {
|
|
348
|
+
CacheMongoose.#instance = new CacheMongoose();
|
|
349
|
+
CacheMongoose.#instance.cache = new Cache(cacheOptions);
|
|
350
|
+
const cache = CacheMongoose.#instance.cache;
|
|
301
351
|
extendQuery(mongoose, cache);
|
|
302
352
|
extendAggregate(mongoose, cache);
|
|
303
353
|
}
|
|
304
|
-
return
|
|
354
|
+
return CacheMongoose.#instance;
|
|
305
355
|
}
|
|
306
356
|
async clear(customKey) {
|
|
307
357
|
if (customKey == null) {
|
|
@@ -313,9 +363,6 @@ const _CacheMongoose = class _CacheMongoose {
|
|
|
313
363
|
async close() {
|
|
314
364
|
await this.cache.close();
|
|
315
365
|
}
|
|
316
|
-
}
|
|
317
|
-
_instance = new WeakMap();
|
|
318
|
-
__privateAdd(_CacheMongoose, _instance);
|
|
319
|
-
let CacheMongoose = _CacheMongoose;
|
|
366
|
+
}
|
|
320
367
|
|
|
321
368
|
module.exports = CacheMongoose;
|