@vizabi/reader-ddfcsv 4.3.2 → 4.3.3
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/dist/reader-ddfcsv-polyfill.js +1 -1
- package/dist/reader-ddfcsv-polyfill.js.map +1 -1
- package/dist/reader-ddfcsv.js +2 -2
- package/dist/reader-ddfcsv.js.map +1 -1
- package/lib/src/ddf-csv.d.ts +2 -1
- package/lib/src/ddf-csv.js +15 -8
- package/lib/src/ddf-csv.js.map +1 -1
- package/lib/src/ddfcsv-reader.js +1 -1
- package/lib/src/ddfcsv-reader.js.map +1 -1
- package/lib/src/resource-selection-optimizer/in-clause-under-conjunction.d.ts +1 -3
- package/lib/src/resource-selection-optimizer/in-clause-under-conjunction.js +38 -83
- package/lib/src/resource-selection-optimizer/in-clause-under-conjunction.js.map +1 -1
- package/lib-web/src/ddf-csv.d.ts +2 -1
- package/lib-web/src/ddf-csv.js +15 -8
- package/lib-web/src/ddf-csv.js.map +1 -1
- package/lib-web/src/ddfcsv-reader.js +1 -1
- package/lib-web/src/ddfcsv-reader.js.map +1 -1
- package/lib-web/src/resource-selection-optimizer/in-clause-under-conjunction.d.ts +1 -3
- package/lib-web/src/resource-selection-optimizer/in-clause-under-conjunction.js +38 -83
- package/lib-web/src/resource-selection-optimizer/in-clause-under-conjunction.js.map +1 -1
- package/package.json +3 -3
- package/src/ddf-csv.ts +13 -9
- package/src/ddfcsv-reader.ts +1 -1
- package/src/resource-selection-optimizer/in-clause-under-conjunction.ts +39 -101
- package/test/definition/datapoints-definition.spec.ts +1 -1
- package/test/main.spec.ts +8 -4
- package/test/result-fixtures/in-clause-under-conjunction-1.json +100 -100
- package/test/result-fixtures/in-clause-under-conjunction-2.json +70169 -70169
- package/test/result-fixtures/multi-instances/entities-sg.json +2 -2
- package/test/tslint.json +3 -2
- package/tslint.json +1 -1
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import * as path from 'path';
|
|
2
1
|
import * as head from 'lodash.head';
|
|
3
2
|
import * as values from 'lodash.values';
|
|
4
3
|
import * as keys from 'lodash.keys';
|
|
5
4
|
import * as get from 'lodash.get';
|
|
6
|
-
import * as flattenDeep from 'lodash.flattendeep';
|
|
7
5
|
import * as isEmpty from 'lodash.isempty';
|
|
8
6
|
import * as startsWith from 'lodash.startswith';
|
|
9
7
|
import * as includes from 'lodash.includes';
|
|
@@ -12,8 +10,6 @@ import { DdfCsvError } from '../ddfcsv-error';
|
|
|
12
10
|
import { IDatapackage, IResourceSelectionOptimizer, IResourceRead, IBaseReaderOptions } from '../interfaces';
|
|
13
11
|
import { QueryFeature, featureDetectors, IQuery } from 'ddf-query-validator';
|
|
14
12
|
|
|
15
|
-
const Papa = require('papaparse');
|
|
16
|
-
|
|
17
13
|
const WHERE_KEYWORD = 'where';
|
|
18
14
|
const JOIN_KEYWORD = 'join';
|
|
19
15
|
const KEY_IN = '$in';
|
|
@@ -21,10 +17,6 @@ const KEY_NIN = '$nin';
|
|
|
21
17
|
const KEY_AND = '$and';
|
|
22
18
|
const KEY_OR = '$or';
|
|
23
19
|
|
|
24
|
-
const getFirstConditionClause = clause => head(values(clause));
|
|
25
|
-
const getFirstKey = obj => head(keys(obj));
|
|
26
|
-
const isOneKeyBased = obj => keys(obj).length === 1;
|
|
27
|
-
|
|
28
20
|
export class InClauseUnderConjunction implements IResourceSelectionOptimizer {
|
|
29
21
|
private flow: any = {};
|
|
30
22
|
private fileReader: IResourceRead;
|
|
@@ -44,11 +36,7 @@ export class InClauseUnderConjunction implements IResourceSelectionOptimizer {
|
|
|
44
36
|
isMatched(): boolean {
|
|
45
37
|
this.flow.joinObject = get(this.query, JOIN_KEYWORD);
|
|
46
38
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return this.query.from === "datapoints";
|
|
50
|
-
// return includes(relatedFeatures, QueryFeature.WhereClauseBasedOnConjunction) &&
|
|
51
|
-
// includes(relatedFeatures, QueryFeature.ConjunctionPartFromWhereClauseCorrespondsToJoin);
|
|
39
|
+
return this.query.from === "datapoints" && this.flow.joinObject;
|
|
52
40
|
}
|
|
53
41
|
|
|
54
42
|
async getRecommendedFilesSet(): Promise<string[]> {
|
|
@@ -59,7 +47,8 @@ export class InClauseUnderConjunction implements IResourceSelectionOptimizer {
|
|
|
59
47
|
|
|
60
48
|
let result;
|
|
61
49
|
try {
|
|
62
|
-
this.collectProcessableClauses();
|
|
50
|
+
this.flow.processableClauses = await this.collectProcessableClauses();
|
|
51
|
+
if (!this.flow.processableClauses) return [];
|
|
63
52
|
this.collectEntityFilesNames();
|
|
64
53
|
const data = await this.collectEntities();
|
|
65
54
|
this.fillEntityValuesHash(data);
|
|
@@ -80,24 +69,26 @@ export class InClauseUnderConjunction implements IResourceSelectionOptimizer {
|
|
|
80
69
|
}
|
|
81
70
|
}
|
|
82
71
|
|
|
83
|
-
private collectProcessableClauses():
|
|
84
|
-
const joinKeys = keys(this.flow.joinObject)
|
|
85
|
-
|
|
86
|
-
|
|
72
|
+
private collectProcessableClauses(): Promise<any> {
|
|
73
|
+
const joinKeys = keys(this.flow.joinObject).filter(
|
|
74
|
+
key => ["entity_domain", "entity_set"].includes(this.options.conceptsLookup.get(key.slice(1))?.concept_type));
|
|
75
|
+
if (!joinKeys.length) return Promise.resolve(false);
|
|
87
76
|
|
|
88
|
-
|
|
77
|
+
return Promise.all(joinKeys.map(joinKey => {
|
|
78
|
+
const key = this.flow.joinObject[joinKey].key;
|
|
89
79
|
const where = get(this.flow.joinObject, `${joinKey}.${WHERE_KEYWORD}`, {});
|
|
90
80
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
this.
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
81
|
+
return this.parent.queryData({
|
|
82
|
+
select: { key: [key] },
|
|
83
|
+
where,
|
|
84
|
+
from: this.options.conceptsLookup.has(key) ? 'entities' : 'concepts'
|
|
85
|
+
}, Object.assign({ joinID: joinKey }, this.options))
|
|
86
|
+
.then(result => ({
|
|
87
|
+
key,
|
|
88
|
+
entities: new Set(result.map(row => row[ key ]))
|
|
89
|
+
}));
|
|
90
|
+
}));
|
|
99
91
|
|
|
100
|
-
return this;
|
|
101
92
|
}
|
|
102
93
|
|
|
103
94
|
private collectEntityFilesNames(): InClauseUnderConjunction {
|
|
@@ -107,15 +98,15 @@ export class InClauseUnderConjunction implements IResourceSelectionOptimizer {
|
|
|
107
98
|
|
|
108
99
|
for (const schemaResourceRecord of this.datapackage.ddfSchema.entities) {
|
|
109
100
|
for (const clause of this.flow.processableClauses) {
|
|
110
|
-
const
|
|
101
|
+
const key = clause.key;
|
|
111
102
|
|
|
112
|
-
if (head(schemaResourceRecord.primaryKey) ===
|
|
103
|
+
if (head(schemaResourceRecord.primaryKey) === key) {
|
|
113
104
|
for (const resourceName of schemaResourceRecord.resources) {
|
|
114
105
|
const resource = this.options.resourcesLookup.get(resourceName);
|
|
115
106
|
|
|
116
107
|
this.flow.entityResources.add(resource);
|
|
117
108
|
this.flow.entityFilesNames.add(resource.path);
|
|
118
|
-
this.flow.fileNameToPrimaryKeyHash.set(resource.path,
|
|
109
|
+
this.flow.fileNameToPrimaryKeyHash.set(resource.path, key);
|
|
119
110
|
}
|
|
120
111
|
}
|
|
121
112
|
}
|
|
@@ -162,30 +153,14 @@ export class InClauseUnderConjunction implements IResourceSelectionOptimizer {
|
|
|
162
153
|
}
|
|
163
154
|
|
|
164
155
|
private getFilesGroupsQueryClause(): InClauseUnderConjunction {
|
|
165
|
-
const getEntitiesExcept = (entityValuesToExclude: string[]): string[] => {
|
|
166
|
-
const result = [];
|
|
167
|
-
|
|
168
|
-
for (const entityKey of this.flow.entityValueToDomainHash.keys()) {
|
|
169
|
-
if (!includes(entityValuesToExclude, entityKey)) {
|
|
170
|
-
result.push(entityKey);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
return result;
|
|
175
|
-
};
|
|
176
156
|
const filesGroupsByClause = new Map();
|
|
177
157
|
|
|
178
158
|
for (const clause of this.flow.processableClauses) {
|
|
179
159
|
const filesGroupByClause = {
|
|
180
|
-
|
|
181
|
-
datapoints: new Set(),
|
|
182
|
-
concepts: new Set()
|
|
160
|
+
datapoints: new Set()
|
|
183
161
|
};
|
|
184
|
-
const firstConditionClause = getFirstConditionClause(clause);
|
|
185
|
-
const entityValuesFromClause = firstConditionClause[KEY_IN] || getEntitiesExcept(firstConditionClause[KEY_NIN]);
|
|
186
162
|
|
|
187
|
-
for (const entityValueFromClause of
|
|
188
|
-
//filesGroupByClause.entities.add(this.flow.entityValueToFileHash.get(entityValueFromClause));
|
|
163
|
+
for (const entityValueFromClause of clause.entities) {
|
|
189
164
|
|
|
190
165
|
const entitiesByQuery = this.flow.entityValueToDomainHash.get(entityValueFromClause);
|
|
191
166
|
|
|
@@ -194,13 +169,12 @@ export class InClauseUnderConjunction implements IResourceSelectionOptimizer {
|
|
|
194
169
|
for (const resourceName of schemaResourceRecord.resources) {
|
|
195
170
|
if (includes(schemaResourceRecord.primaryKey, entityByQuery)) {
|
|
196
171
|
const resource = this.options.resourcesLookup.get(resourceName);
|
|
197
|
-
const constraint = resource.constraints[entityByQuery];
|
|
172
|
+
const constraint = resource.constraints?.[entityByQuery];
|
|
198
173
|
if ( constraint ) {
|
|
199
174
|
if (constraint.includes(entityValueFromClause)) {
|
|
200
175
|
filesGroupByClause.datapoints.add(resource.path);
|
|
201
176
|
}
|
|
202
|
-
}
|
|
203
|
-
else {
|
|
177
|
+
} else {
|
|
204
178
|
filesGroupByClause.datapoints.add(resource.path);
|
|
205
179
|
}
|
|
206
180
|
}
|
|
@@ -209,13 +183,7 @@ export class InClauseUnderConjunction implements IResourceSelectionOptimizer {
|
|
|
209
183
|
}
|
|
210
184
|
}
|
|
211
185
|
|
|
212
|
-
|
|
213
|
-
for (const resourceName of schemaResourceRecord.resources) {
|
|
214
|
-
filesGroupByClause.concepts.add(this.options.resourcesLookup.get(resourceName).path);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
filesGroupsByClause.set(clause, filesGroupByClause);
|
|
186
|
+
filesGroupsByClause.set(clause.key, filesGroupByClause);
|
|
219
187
|
}
|
|
220
188
|
|
|
221
189
|
this.flow.filesGroupsByClause = filesGroupsByClause;
|
|
@@ -224,59 +192,29 @@ export class InClauseUnderConjunction implements IResourceSelectionOptimizer {
|
|
|
224
192
|
}
|
|
225
193
|
|
|
226
194
|
private getOptimalFilesGroup(): string[] {
|
|
227
|
-
const clauseKeys = this.flow.filesGroupsByClause.keys();
|
|
228
195
|
|
|
229
|
-
|
|
230
|
-
let appropriateClauseSize;
|
|
231
|
-
|
|
232
|
-
for (const key of clauseKeys) {
|
|
233
|
-
const size = this.flow.filesGroupsByClause.get(key).datapoints.size +
|
|
234
|
-
this.flow.filesGroupsByClause.get(key).entities.size +
|
|
235
|
-
this.flow.filesGroupsByClause.get(key).concepts.size;
|
|
196
|
+
const entities = this.flow.entityFilesNames;
|
|
236
197
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
198
|
+
const concepts = new Set();
|
|
199
|
+
for (const schemaResourceRecord of this.datapackage.ddfSchema.concepts) {
|
|
200
|
+
for (const resourceName of schemaResourceRecord.resources) {
|
|
201
|
+
concepts.add(this.options.resourcesLookup.get(resourceName).path);
|
|
240
202
|
}
|
|
241
203
|
}
|
|
242
204
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
return [
|
|
248
|
-
...Array.from(this.flow.filesGroupsByClause.get(appropriateClauseKey).concepts),
|
|
249
|
-
...Array.from(this.flow.filesGroupsByClause.get(appropriateClauseKey).entities),
|
|
250
|
-
...Array.from(this.flow.filesGroupsByClause.get(appropriateClauseKey).datapoints)
|
|
251
|
-
] as string[];
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
private getProcessableClauses(clause) {
|
|
255
|
-
const result = [];
|
|
256
|
-
const clauseKeys = keys(clause);
|
|
205
|
+
const clauseKeys = this.flow.filesGroupsByClause.keys();
|
|
206
|
+
let datapoints = Array.from(this.flow.filesGroupsByClause.get(clauseKeys.next().value).datapoints);
|
|
257
207
|
|
|
258
208
|
for (const key of clauseKeys) {
|
|
259
|
-
|
|
260
|
-
// attention! this functionality process only first clause
|
|
261
|
-
// for example, { geo: { '$in': ['world'] } }
|
|
262
|
-
// in this example { geo: { '$in': ['world'] }, foo: { '$in': ['bar', 'baz'] } }]
|
|
263
|
-
// foo: { '$in': ['bar', 'baz'] } will NOT be processed
|
|
264
|
-
const conditionKey = head(keys(clause[key]));
|
|
265
|
-
|
|
266
|
-
if (conditionKey === KEY_IN || conditionKey === KEY_NIN) {
|
|
267
|
-
result.push(clause);
|
|
268
|
-
}
|
|
269
|
-
}
|
|
209
|
+
datapoints = this.intersectArray(datapoints, Array.from(this.flow.filesGroupsByClause.get(key).datapoints));
|
|
270
210
|
}
|
|
271
211
|
|
|
272
|
-
return
|
|
212
|
+
return [...Array.from(concepts), ...Array.from(entities)].concat(datapoints) as string[];
|
|
273
213
|
}
|
|
274
214
|
|
|
275
|
-
private singleAndField(clause): boolean {
|
|
276
|
-
return isOneKeyBased(clause) && !!get(clause, KEY_AND);
|
|
277
|
-
}
|
|
278
215
|
|
|
279
|
-
private
|
|
280
|
-
return
|
|
216
|
+
private intersectArray(array1, array2) {
|
|
217
|
+
return array1.filter(value => array2.includes(value));
|
|
281
218
|
}
|
|
219
|
+
|
|
282
220
|
}
|
package/test/main.spec.ts
CHANGED
|
@@ -127,8 +127,10 @@ describe('General errors in ddfcsv reader', () => {
|
|
|
127
127
|
|
|
128
128
|
expect(error.file).to.equal(expectedPath);
|
|
129
129
|
expect(error.name).to.equal('DdfCsvError');
|
|
130
|
-
expect(error.message).to.equal(`JSON parsing error [filepath: ${expectedPath}]. Unexpected token (
|
|
131
|
-
|
|
130
|
+
expect(error.message).to.equal(`JSON parsing error [filepath: ${expectedPath}]. Unexpected token '(', "(
|
|
131
|
+
"nam"... is not valid JSON.`);
|
|
132
|
+
expect(error.details).to.equal(`Unexpected token '(', "(
|
|
133
|
+
"nam"... is not valid JSON`);
|
|
132
134
|
}
|
|
133
135
|
});
|
|
134
136
|
});
|
|
@@ -245,8 +247,10 @@ describe('General errors in ddfcsv reader', () => {
|
|
|
245
247
|
|
|
246
248
|
expect(error.file).to.equal(expectedPath);
|
|
247
249
|
expect(error.name).to.equal('DdfCsvError');
|
|
248
|
-
expect(error.message).to.equal(`JSON parsing error [filepath: ${expectedPath}]. Unexpected token (
|
|
249
|
-
|
|
250
|
+
expect(error.message).to.equal(`JSON parsing error [filepath: ${expectedPath}]. Unexpected token '(', "(
|
|
251
|
+
"nam"... is not valid JSON.`);
|
|
252
|
+
expect(error.details).to.equal(`Unexpected token '(', "(
|
|
253
|
+
"nam"... is not valid JSON`);
|
|
250
254
|
}
|
|
251
255
|
});
|
|
252
256
|
});
|