-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathDataStore.ts
151 lines (127 loc) · 5.05 KB
/
DataStore.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import { MongoClient, Db, Collection, Document, Filter, UpdateFilter, Sort, ClientSession, FindOptions, WithId, OptionalUnlessRequiredId } from 'mongodb';
// Types
type DataStoreConfig = {
url: string;
dbName: string;
};
type QueryOptions<T extends Document> = {
filter?: Filter<T>;
skip?: number;
limit?: number;
sort?: Sort;
projection?: Document;
};
class MongoDBWrapper {
private client: MongoClient;
private db: Db;
constructor(private config: DataStoreConfig) {
this.client = new MongoClient(config.url);
this.db = this.client.db(config.dbName);
}
async connect(): Promise<void> {
try {
await this.client.connect();
} catch (error) {
console.error("Failed to connect to MongoDB", error);
throw error;
}
}
async disconnect(): Promise<void> {
try {
await this.client.close();
} catch (error) {
console.error("Failed to disconnect from MongoDB", error);
throw error;
}
}
async executeInTransaction<T>(func: (session: ClientSession) => Promise<T>): Promise<T> {
const session = this.client.startSession();
try {
session.startTransaction();
const result = await func(session);
await session.commitTransaction();
return result;
} catch (error) {
await session.abortTransaction();
throw error;
} finally {
session.endSession();
}
}
async insertOne<T extends Document>(collectionName: string, doc: OptionalUnlessRequiredId<T>): Promise<WithId<T>> {
const collection = this.getCollection<T>(collectionName);
const result = await collection.insertOne(doc);
return { ...doc, _id: result.insertedId } as WithId<T>;
}
async insertMany<T extends Document>(collectionName: string, docs: OptionalUnlessRequiredId<T>[]): Promise<WithId<T>[]> {
const collection = this.getCollection<T>(collectionName);
const result = await collection.insertMany(docs);
return docs.map((doc, index) => ({ ...doc, _id: result.insertedIds[index] })) as WithId<T>[];
}
async updateOne<T extends Document>(collectionName: string, query: QueryOptions<T>, update: UpdateFilter<T>): Promise<void> {
const collection = this.getCollection<T>(collectionName);
await collection.updateOne(query.filter || {}, update);
}
async updateMany<T extends Document>(collectionName: string, query: QueryOptions<T>, update: UpdateFilter<T>): Promise<void> {
const collection = this.getCollection<T>(collectionName);
await collection.updateMany(query.filter || {}, update);
}
async findOne<T extends Document>(collectionName: string, query: QueryOptions<T>): Promise<T | null> {
const collection = this.getCollection<T>(collectionName);
const options: FindOptions = {
skip: query.skip,
sort: query.sort,
projection: query.projection,
};
return await collection.findOne(query.filter || {}, options) as T | null;
}
async find<T extends Document>(collectionName: string, query: QueryOptions<T>): Promise<T[]> {
const collection = this.getCollection<T>(collectionName);
const options: FindOptions = {
skip: query.skip,
limit: query.limit,
sort: query.sort,
projection: query.projection,
};
return await collection.find(query.filter || {}, options).toArray() as T[];
}
async deleteOne<T extends Document>(collectionName: string, query: QueryOptions<T>): Promise<void> {
const collection = this.getCollection<T>(collectionName);
await collection.deleteOne(query.filter || {});
}
async deleteMany<T extends Document>(collectionName: string, query: QueryOptions<T>): Promise<void> {
const collection = this.getCollection<T>(collectionName);
await collection.deleteMany(query.filter || {});
}
async count<T extends Document>(collectionName: string, query: QueryOptions<T>): Promise<number> {
const collection = this.getCollection<T>(collectionName);
return await collection.countDocuments(query.filter || {});
}
// Query builder functions
static createQuery<T extends Document>(options: QueryOptions<T> = {}): QueryOptions<T> {
return options;
}
static withFilter<T extends Document>(query: QueryOptions<T>, filter: Filter<T>): QueryOptions<T> {
return { ...query, filter };
}
static withSkip<T extends Document>(query: QueryOptions<T>, skip: number): QueryOptions<T> {
return { ...query, skip };
}
static withLimit<T extends Document>(query: QueryOptions<T>, limit: number): QueryOptions<T> {
return { ...query, limit };
}
static withSort<T extends Document>(query: QueryOptions<T>, sort: Sort): QueryOptions<T> {
return { ...query, sort };
}
static withProjection<T extends Document>(query: QueryOptions<T>, projection: Document): QueryOptions<T> {
return { ...query, projection };
}
private getCollection<T extends Document>(name: string): Collection<T> {
return this.db.collection<T>(name);
}
}
export {
DataStoreConfig,
QueryOptions,
MongoDBWrapper,
};