/
collection.ts
158 lines (153 loc) · 4.51 KB
/
collection.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
import { axiod } from "./deps.ts";
import { ArangoCursor, CursorOptions } from "./cursor.ts";
/**
* Define Document methods.
*/
export interface DocumentBase {
update(): Promise<void>;
delete(): Promise<boolean>;
}
/**
* Join DocumentData<T> and DocumentBase
*/
export type Document<T> =
& DocumentData<T>
& DocumentBase;
/**
* Document ID fields
*/
export type DocumentID = { _id: string; _key: string; _rev: string };
/**
* Document Data and ID fields.
*/
export type DocumentData<T> = T & DocumentID;
/**
* An object representing a collection on the ArangoDB server.
*/
export class Collection<T> {
constructor(private ax: typeof axiod, public readonly name: string) {}
/**
* Get a document directly by its key
* @param key The document key
* @returns The document specified by the key.
*/
public async get(key: string): Promise<Document<T>> {
const res = await this.ax.get(`/_api/document/${this.name}/${key}`);
if (res.status != 200) {
throw new Error(`Unable to get document: ${res.data.errorMessage}`);
}
return this.makeDocument(res.data);
}
private async updateDocument(
key: string,
data: DocumentData<T>,
): Promise<void> {
const res = await this.ax.patch(`/_api/document/${this.name}/${key}`, data);
switch (res.status) {
case 400:
case 404:
case 412:
throw new Error(`Unable to update document: ${res.data.errorMessage}`);
default: {
const ids = res.data as DocumentID;
data._rev = ids._rev;
}
}
}
private async deleteDocument(key: string): Promise<boolean> {
const res = await this.ax.delete(`/_api/document/${this.name}/${key}`);
switch (res.status) {
case 404:
case 412:
return false;
default:
return true;
}
}
private makeDocument(obj: DocumentData<T>): Document<T> {
const data = Object.assign({}, obj);
return Object.assign(data, {
update: async () => {
await this.updateDocument(data._key, data);
},
delete: async () => {
return await this.deleteDocument(data._key);
},
});
}
/**
* Create a new document in the collection.
* @param data The initial document data.
* @returns The document, as it exists on the server.
*/
public async create(data: T): Promise<Document<T>> {
const res = await this.ax.post(`/_api/document/${this.name}`, data);
switch (res.status) {
case 400:
case 404:
throw new Error(`Unable to create document. ${res.data.errorMessage}`);
default: {
const ids: DocumentID = res.data;
const doc = await this.get(ids._key);
if (doc == null) throw new Error("Unable to get created document.");
return doc;
}
}
}
/**
* Find multiple documents matching the filter provided.
* @param filter Partial document data to filter results by.
* @returns Documents matching the filter provided.
*/
public async find(
filter: Partial<DocumentData<T>>,
): Promise<Document<T>[]> {
const filterStrings: string[] = [];
// deno-lint-ignore no-explicit-any
const filterAny: { [key: string]: any } = filter;
for (const key of Object.keys(filterAny)) {
filterStrings.push(
`FILTER doc.${key} == ${JSON.stringify(filterAny[key])}`,
);
}
const query = `FOR doc IN ${this.name}\n\t${
filterStrings.join("\n\t")
}\n\tRETURN doc`;
return await this.query(query);
}
/**
* Find the first document matching the filter provided.
* @param filter Partial document data to filter results by.
* @returns A document matching the filter provided.
*/
public async findOne(
filter: Partial<DocumentData<T>>,
): Promise<Document<T> | undefined> {
return (await this.find(filter))[0];
}
/**
* Run a query, returning the results as Document objects.
* @param aql The query string to execute.
* @param options Cursor options.
* @returns The collected results of the query, converted to Document objects.
*/
public async query(
aql: string,
options?: CursorOptions,
): Promise<Document<T>[]> {
const res = await new ArangoCursor<DocumentData<T>>(this.ax, aql, options)
.collect();
return res.map((d) => this.makeDocument(d));
}
/**
* Truncate the collection.
*/
public async truncate(): Promise<void> {
const res = await this.ax.put(`/_api/collection/${this.name}/truncate`);
if (res.data.error) {
throw new Error(
`Unable to truncate collection: ${res.data.errorMessage}`,
);
}
}
}