kourou 0.19.4 → 0.20.2

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.
@@ -6,72 +6,222 @@ const fs_1 = (0, tslib_1.__importDefault)(require("fs"));
6
6
  const path_1 = (0, tslib_1.__importDefault)(require("path"));
7
7
  const cli_ux_1 = (0, tslib_1.__importDefault)(require("cli-ux"));
8
8
  const ndjson_1 = (0, tslib_1.__importDefault)(require("ndjson"));
9
- async function dumpCollectionData(sdk, index, collection, batchSize, destPath, query = {}, format = 'jsonl') {
10
- const collectionDir = path_1.default.join(destPath, collection);
11
- const filename = path_1.default.join(collectionDir, (format.toLowerCase() === 'jsonl' ? 'documents.jsonl' : 'documents.json'));
12
- const writeStream = fs_1.default.createWriteStream(filename);
13
- const waitWrite = new Promise(resolve => writeStream.on('finish', resolve));
14
- const ndjsonStream = ndjson_1.default.stringify();
15
- const options = {
16
- scroll: '1m',
17
- size: batchSize
18
- };
19
- const writeLine = (content) => {
9
+ const common_1 = require("../common");
10
+ /**
11
+ * Flatten an object transform:
12
+ * {
13
+ * title: "kuzzle",
14
+ * info : {
15
+ * tag: "news"
16
+ * }
17
+ * }
18
+ *
19
+ * Into an object like:
20
+ * {
21
+ * title: "kuzzle",
22
+ * info.tag: news
23
+ * }
24
+ *
25
+ * @param {Object} target the object we have to flatten
26
+ * @returns {Object} the flattened object
27
+ */
28
+ function flattenObject(target) {
29
+ const output = {};
30
+ flattenStep(output, target);
31
+ return output;
32
+ }
33
+ function flattenStep(output, object, prev = null) {
34
+ const keys = Object.keys(object);
35
+ for (let i = 0; i < keys.length; i++) {
36
+ const key = keys[i];
37
+ const value = object[key];
38
+ const newKey = prev ? prev + '.' + key : key;
39
+ if (Object.prototype.toString.call(value) === '[object Object]') {
40
+ output[newKey] = value;
41
+ flattenStep(output, value, newKey);
42
+ }
43
+ output[newKey] = value;
44
+ }
45
+ }
46
+ class AbstractDumper {
47
+ constructor(sdk, index, collection, batchSize, destPath, query = {}) {
48
+ this.sdk = sdk;
49
+ this.index = index;
50
+ this.collection = collection;
51
+ this.batchSize = batchSize;
52
+ this.destPath = destPath;
53
+ this.query = query;
54
+ this.collectionDir = path_1.default.join(this.destPath, this.collection);
55
+ this.options = {
56
+ scroll: '2s',
57
+ size: batchSize
58
+ };
59
+ }
60
+ /**
61
+ * One-shot call before the dump. Can be used to
62
+ * perform setup operations before dumping.
63
+ *
64
+ * @returns void
65
+ */
66
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
67
+ async setup() { }
68
+ /**
69
+ * One-shot call before iterating over the data. Can be
70
+ * used to write the header of the dumped output.
71
+ */
72
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
73
+ async writeHeader() { }
74
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
75
+ async tearDown() { }
76
+ /**
77
+ * The loop that iterates over the documents of the collection and
78
+ * calls all the other hooks.
79
+ *
80
+ * @returns a promise resolving when the dump is finished.
81
+ */
82
+ async dump() {
83
+ this.filename = path_1.default.join(this.collectionDir, `documents.${this.fileExtension}`);
84
+ this.writeStream = fs_1.default.createWriteStream(this.filename);
85
+ const waitWrite = new Promise((resolve, reject) => this.writeStream ? this.writeStream.on('finish', resolve) : reject());
86
+ this.writeStream.on('error', error => {
87
+ throw error;
88
+ });
89
+ await this.setup();
90
+ await this.writeHeader();
91
+ let results = await this.sdk.document.search(this.index, this.collection, { query: this.query }, this.options);
92
+ const progressBar = cli_ux_1.default.progress({
93
+ format: `Dumping ${this.collection} |{bar}| {percentage}% || {value}/{total} documents`
94
+ });
95
+ progressBar.start(results.total, 0);
96
+ do {
97
+ progressBar.update(results.fetched);
98
+ for (const hit of results.hits) {
99
+ await this.onResult({
100
+ _id: hit._id,
101
+ _source: hit._source
102
+ });
103
+ }
104
+ } while ((results = await results.next()));
105
+ await this.tearDown();
106
+ progressBar.stop();
107
+ this.writeStream.end();
108
+ return waitWrite;
109
+ }
110
+ }
111
+ class JSONLDumper extends AbstractDumper {
112
+ constructor() {
113
+ super(...arguments);
114
+ this.ndjsonStream = ndjson_1.default.stringify();
115
+ }
116
+ async setup() {
117
+ this.ndjsonStream.on('data', (line) => {
118
+ if (!this.writeStream) {
119
+ throw new Error('Cannot write data: WriteStream is not initialized.');
120
+ }
121
+ this.writeStream.write(line);
122
+ });
123
+ return;
124
+ }
125
+ async writeHeader() {
126
+ await this.writeLine({ type: 'collection', index: this.index, collection: this.collection });
127
+ }
128
+ writeLine(content) {
20
129
  return new Promise(resolve => {
21
- if (ndjsonStream.write(content)) {
130
+ if (this.ndjsonStream.write(content)) {
22
131
  resolve(undefined);
23
132
  }
24
133
  else {
25
- ndjsonStream.once('drain', resolve);
134
+ this.ndjsonStream.once('drain', resolve);
26
135
  }
27
136
  });
28
- };
29
- writeStream.on('error', error => {
30
- throw error;
31
- });
32
- ndjsonStream.on('data', (line) => writeStream.write(line));
33
- fs_1.default.mkdirSync(collectionDir, { recursive: true });
34
- if (format.toLowerCase() === 'jsonl') {
35
- await writeLine({ type: 'collection', index, collection });
36
- }
37
- let results = await sdk.document.search(index, collection, { query }, options);
38
- const progressBar = cli_ux_1.default.progress({
39
- format: `Dumping ${collection} |{bar}| {percentage}% || {value}/{total} documents`
40
- });
41
- progressBar.start(results.total, 0);
42
- const rawDocuments = {
43
- [index]: {
44
- [collection]: []
45
- }
46
- };
47
- do {
48
- progressBar.update(results.fetched);
49
- for (const hit of results.hits) {
50
- let document = null;
51
- if (format.toLocaleLowerCase() === 'jsonl') {
52
- document = {
53
- _id: hit._id,
54
- body: hit._source
55
- };
56
- await writeLine(document);
137
+ }
138
+ onResult(document) {
139
+ return this.writeLine({ _id: document._id, body: document._source });
140
+ }
141
+ get fileExtension() {
142
+ return 'jsonl';
143
+ }
144
+ }
145
+ class KuzzleDumper extends JSONLDumper {
146
+ constructor() {
147
+ super(...arguments);
148
+ this.rawDocuments = {
149
+ [this.index]: {
150
+ [this.collection]: []
57
151
  }
58
- else {
59
- rawDocuments[index][collection].push({
60
- index: {
61
- _id: hit._id
62
- }
63
- });
64
- rawDocuments[index][collection].push(hit._source);
152
+ };
153
+ }
154
+ get fileExtension() {
155
+ return 'json';
156
+ }
157
+ async onResult(document) {
158
+ this.rawDocuments[this.index][this.collection].push({
159
+ index: {
160
+ _id: document._id
161
+ }
162
+ });
163
+ this.rawDocuments[this.index][this.collection].push(document._source);
164
+ return;
165
+ }
166
+ tearDown() {
167
+ return this.writeLine(this.rawDocuments);
168
+ }
169
+ }
170
+ class CSVDumper extends AbstractDumper {
171
+ constructor(sdk, index, collection, batchSize, destPath, query = {}, fields, separator = ',') {
172
+ super(sdk, index, collection, batchSize, destPath, query);
173
+ this.fields = fields;
174
+ this.separator = separator;
175
+ }
176
+ get fileExtension() {
177
+ return 'csv';
178
+ }
179
+ async setup() {
180
+ if (!this.fields.length) {
181
+ // If no field has been selected, then all fields are selected.
182
+ const mappings = await this.sdk.collection.getMapping(this.index, this.collection);
183
+ if (!mappings.properties) {
184
+ return;
65
185
  }
186
+ this.fields = Object.keys(flattenObject(mappings.properties));
187
+ }
188
+ else {
189
+ // Delete '_id' from the selected fields, since IDs are
190
+ // _always_ exported.
191
+ if (this.fields.includes('_id')) {
192
+ this.fields.splice(this.fields.indexOf('_id'), 1);
193
+ }
194
+ }
195
+ }
196
+ writeHeader() {
197
+ return this.writeLine(['_id', ...this.fields].join(this.separator));
198
+ }
199
+ async writeLine(line) {
200
+ if (!this.writeStream) {
201
+ throw new Error('Cannot write data: WriteStream is not initialized.');
66
202
  }
67
- } while ((results = await results.next()));
68
- if (format.toLocaleLowerCase() === 'kuzzle') {
69
- await writeLine(rawDocuments);
70
- }
71
- progressBar.stop();
72
- ndjsonStream.end();
73
- writeStream.end();
74
- return waitWrite;
203
+ this.writeStream.write(line);
204
+ this.writeStream.write('\n');
205
+ return;
206
+ }
207
+ onResult(document) {
208
+ const values = [document._id, ...(0, common_1.pickValues)(document._source, this.fields)];
209
+ return this.writeLine(values.join(this.separator));
210
+ }
211
+ }
212
+ async function dumpCollectionData(sdk, index, collection, batchSize, destPath, query = {}, format = 'jsonl', fields = []) {
213
+ let dumper;
214
+ switch (format.toLowerCase()) {
215
+ case 'jsonl':
216
+ dumper = new JSONLDumper(sdk, index, collection, batchSize, destPath, query);
217
+ return dumper.dump();
218
+ case 'csv':
219
+ dumper = new CSVDumper(sdk, index, collection, batchSize, destPath, query, fields);
220
+ return dumper.dump();
221
+ default:
222
+ dumper = new KuzzleDumper(sdk, index, collection, batchSize, destPath, query);
223
+ return dumper.dump();
224
+ }
75
225
  }
76
226
  exports.dumpCollectionData = dumpCollectionData;
77
227
  async function dumpCollectionMappings(sdk, index, collection, destPath, format = 'jsonl') {