-
Notifications
You must be signed in to change notification settings - Fork 81
/
Copy pathrepository.ts
183 lines (163 loc) · 5.82 KB
/
repository.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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
import Schema from "../schema/schema";
import Client, { CreateIndexOptions } from "../client";
import Entity from '../entity/entity';
import Search from '../search/search';
import { EntityData } from '../entity/entity';
import HashConverter from "./hash-converter";
import JsonConverter from "./json-converter";
/**
* A repository is the main interaction point for reading, writing, and
* removing {@link Entity | Entities} from Redis. Create one by passing
* in a {@link Schema} and a {@link Client}. Then use the {@link Repository.fetch},
* {@link Repository.save}, and {@link Repository.remove} methods to manage your
* data:
*
* ```typescript
* let repository = new Repository<Foo>(schema, client);
*
* let foo = await repository.fetch('01FK6TCJBDK41RJ766A4SBWDJ9');
* foo.aString = 'bar';
* foo.aBoolean = false;
* await repository.save(foo);
* ```
*
* Be sure to use the repository to create a new instance of {@link Entity} you want
* to create before you save it:
* ```typescript
* let foo = await repository.createEntity();
* foo.aString = 'bar';
* foo.aBoolean = false;
* await repository.save(foo);
* ```
*
* If you want to the {@link Repository.search} method, you need to create an index
* first, and you need RediSearch or RedisJSON installed on your instance of Redis:
*
* ```typescript
* await repository.createIndex();
* let entities = await repository.search()
* .where('aString').eq('bar')
* .and('aBoolean').is.false().returnAll();
* ```
*
* @template TEntity The type of {@link Entity} that this repository manages.
*/
export default class Repository<TEntity extends Entity> {
private schema: Schema<TEntity>;
private client: Client;
private jsonConverter: JsonConverter;
private hashConverter: HashConverter;
/**
* Constructs a new Repository.
* @template TEntity The type of {@link Entity} that this repository manages.
* @param schema The {@link Schema} for this Repository.
* @param client An open {@link Client}.
*/
constructor(schema: Schema<TEntity>, client: Client) {
this.schema = schema;
this.client = client;
this.jsonConverter = new JsonConverter(this.schema.definition);
this.hashConverter = new HashConverter(this.schema.definition);
}
/**
* Creates an index in Redis for use by the {@link Repository.search} method. Requires
* that RediSearch or RedisJSON is installed on your instance of Redis.
*/
async createIndex() {
let options : CreateIndexOptions = {
indexName: this.schema.indexName,
dataStructure: this.schema.dataStructure,
prefix: `${this.schema.prefix}:`,
schema: this.schema.redisSchema
};
if (this.schema.useStopWords === 'OFF') options.stopWords = []
if (this.schema.useStopWords === 'CUSTOM') options.stopWords = this.schema.stopWords
await this.client.createIndex(options);
}
/**
* Removes an existing index from Redis. Use this method if you want to swap out you index
* because your {@link Entity} has changed. Requires that RediSearch or RedisJSON is installed
* on your instance of Redis.
*/
async dropIndex() {
try {
await this.client.dropIndex(this.schema.indexName);
} catch (e) {
if (e instanceof Error && e.message === "Unknown Index name") {
// no-op: the thing we are dropping doesn't exist
} else {
throw e
}
}
}
/**
* Creates an empty {@link Entity} with a populated {@link Entity.entityId} property.
* @returns A newly created Entity.
*/
createEntity(): TEntity {
let id = this.schema.generateId();
return new this.schema.entityCtor(id);
}
/**
* Save the {@link Entity} to Redis. If it already exists, it will be updated. If it doesn't
* exist, it will be created.
* @param entity The Entity to save.
* @returns The ID of the Entity just saved.
*/
async save(entity: TEntity): Promise<string> {
let key = this.makeKey(entity.entityId);
if (Object.keys(entity.entityData).length === 0) {
await this.client.unlink(key);
return entity.entityId;
}
if (this.schema.dataStructure === 'JSON') {
let jsonData = this.jsonConverter.toJsonData(entity.entityData);
await this.client.jsonset(key, jsonData);
} else {
let hashData = this.hashConverter.toHashData(entity.entityData);
await this.client.hsetall(key, hashData);
}
return entity.entityId;
}
/**
* Read and return an {@link Entity} from Redis with the given id. If
* the {@link Entity} is not found, returns an {@link Entity} with all
* properties set to `null`.
* @param id The ID of the {@link Entity} you seek.
* @returns The matching Entity.
*/
async fetch(id: string): Promise<TEntity> {
let key = this.makeKey(id);
let entityData: EntityData = {};
if (this.schema.dataStructure === 'JSON') {
let jsonData = await this.client.jsonget(key);
entityData = this.jsonConverter.toEntityData(jsonData);
} else {
let hashData = await this.client.hgetall(key);
entityData = this.hashConverter.toEntityData(hashData);
}
let entity = new this.schema.entityCtor(id, entityData);
return entity;
}
/**
* Remove an {@link Entity} from Redis with the given id. If the {@link Entity} is
* not found, does nothing.
* @param id The ID of the {@link Entity} you with to delete.
*/
async remove(id: string): Promise<void> {
let key = this.makeKey(id);
await this.client.unlink(key);
}
/**
* Kicks off the processes of building a query. Requires that RediSearch or
* RedisJSON is installed on your instance of Redis.
* @template TEntity The type of {@link Entity} sought.
* @returns A {@link Search} object.
*/
search(): Search<TEntity> {
return new Search<TEntity>(this.schema, this.client);
}
private makeKey(id: string): string {
return `${this.schema.prefix}:${id}`;
}
}