@platforma-sdk/model 1.44.14 → 1.45.17
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/annotations/converter.cjs +29 -0
- package/dist/annotations/converter.cjs.map +1 -0
- package/dist/annotations/converter.d.ts +3 -0
- package/dist/annotations/converter.d.ts.map +1 -0
- package/dist/annotations/converter.js +27 -0
- package/dist/annotations/converter.js.map +1 -0
- package/dist/annotations/index.d.ts +3 -0
- package/dist/annotations/index.d.ts.map +1 -0
- package/dist/annotations/types.d.ts +22 -0
- package/dist/annotations/types.d.ts.map +1 -0
- package/dist/components/PFrameForGraphs.cjs +5 -22
- package/dist/components/PFrameForGraphs.cjs.map +1 -1
- package/dist/components/PFrameForGraphs.d.ts +0 -2
- package/dist/components/PFrameForGraphs.d.ts.map +1 -1
- package/dist/components/PFrameForGraphs.js +7 -22
- package/dist/components/PFrameForGraphs.js.map +1 -1
- package/dist/components/PlAnnotations/filter.d.ts.map +1 -1
- package/dist/components/PlAnnotations/filters_ui.cjs +1 -372
- package/dist/components/PlAnnotations/filters_ui.cjs.map +1 -1
- package/dist/components/PlAnnotations/filters_ui.d.ts +11 -756
- package/dist/components/PlAnnotations/filters_ui.d.ts.map +1 -1
- package/dist/components/PlAnnotations/filters_ui.js +2 -370
- package/dist/components/PlAnnotations/filters_ui.js.map +1 -1
- package/dist/components/PlAnnotations/index.d.ts.map +1 -1
- package/dist/components/PlAnnotations/types.d.ts.map +1 -1
- package/dist/components/PlDataTable.cjs +6 -38
- package/dist/components/PlDataTable.cjs.map +1 -1
- package/dist/components/PlDataTable.d.ts +1 -4
- package/dist/components/PlDataTable.d.ts.map +1 -1
- package/dist/components/PlDataTable.js +7 -38
- package/dist/components/PlDataTable.js.map +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.d.ts.map +1 -1
- package/dist/filters/converter.cjs +101 -0
- package/dist/filters/converter.cjs.map +1 -0
- package/dist/filters/converter.d.ts +5 -0
- package/dist/filters/converter.d.ts.map +1 -0
- package/dist/filters/converter.js +98 -0
- package/dist/filters/converter.js.map +1 -0
- package/dist/filters/index.d.ts +3 -0
- package/dist/filters/index.d.ts.map +1 -0
- package/dist/filters/types.d.ts +102 -0
- package/dist/filters/types.d.ts.map +1 -0
- package/dist/index.cjs +12 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/package.json.cjs +1 -1
- package/dist/package.json.js +1 -1
- package/dist/render/api.cjs +7 -1
- package/dist/render/api.cjs.map +1 -1
- package/dist/render/api.d.ts +3 -3
- package/dist/render/api.d.ts.map +1 -1
- package/dist/render/api.js +7 -1
- package/dist/render/api.js.map +1 -1
- package/dist/render/util/column_collection.cjs +2 -2
- package/dist/render/util/column_collection.cjs.map +1 -1
- package/dist/render/util/column_collection.d.ts +2 -2
- package/dist/render/util/column_collection.d.ts.map +1 -1
- package/dist/render/util/column_collection.js +2 -2
- package/dist/render/util/column_collection.js.map +1 -1
- package/dist/render/util/pcolumn_data.cjs +21 -0
- package/dist/render/util/pcolumn_data.cjs.map +1 -1
- package/dist/render/util/pcolumn_data.d.ts +4 -1
- package/dist/render/util/pcolumn_data.d.ts.map +1 -1
- package/dist/render/util/pcolumn_data.js +21 -2
- package/dist/render/util/pcolumn_data.js.map +1 -1
- package/package.json +7 -6
- package/src/annotations/converter.test.ts +74 -0
- package/src/annotations/converter.ts +28 -0
- package/src/annotations/index.ts +2 -0
- package/src/annotations/types.ts +23 -0
- package/src/components/PFrameForGraphs.ts +5 -23
- package/src/components/PlAnnotations/filter.ts +1 -0
- package/src/components/PlAnnotations/filters_ui.test.ts +1 -0
- package/src/components/PlAnnotations/filters_ui.ts +56 -439
- package/src/components/PlAnnotations/index.ts +1 -0
- package/src/components/PlAnnotations/types.ts +1 -0
- package/src/components/PlDataTable.ts +5 -40
- package/src/components/index.ts +1 -1
- package/src/filters/converter.test.ts +336 -0
- package/src/filters/converter.ts +119 -0
- package/src/filters/index.ts +2 -0
- package/src/filters/types.ts +47 -0
- package/src/index.ts +2 -0
- package/src/render/api.ts +9 -5
- package/src/render/util/column_collection.ts +19 -19
- package/src/render/util/pcolumn_data.ts +24 -0
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import type { SUniversalPColumnId } from '@milaboratories/pl-model-common';
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { convertFilterUiToExpressions } from './converter';
|
|
4
|
+
import { FilterSpec } from './types';
|
|
5
|
+
|
|
6
|
+
describe('convertFilterUiToExpressions', () => {
|
|
7
|
+
it('should compile "or" filter to ptabler expression', () => {
|
|
8
|
+
const uiFilter: FilterSpec = {
|
|
9
|
+
type: 'or',
|
|
10
|
+
filters: [
|
|
11
|
+
{ type: 'isNA', column: 'colA' as unknown as SUniversalPColumnId },
|
|
12
|
+
{ type: 'patternEquals', column: 'colB' as unknown as SUniversalPColumnId, value: 'test' },
|
|
13
|
+
],
|
|
14
|
+
};
|
|
15
|
+
const result = convertFilterUiToExpressions(uiFilter);
|
|
16
|
+
expect(result.type).toBe('or');
|
|
17
|
+
expect((result as any).operands).toHaveLength(2);
|
|
18
|
+
expect((result as any).operands[0].type).toBe('is_na');
|
|
19
|
+
expect((result as any).operands[1].type).toBe('eq');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('should compile "and" filter to ptabler expression', () => {
|
|
23
|
+
const uiFilter: FilterSpec = {
|
|
24
|
+
type: 'and',
|
|
25
|
+
filters: [
|
|
26
|
+
{ type: 'isNA', column: 'colA' as unknown as SUniversalPColumnId },
|
|
27
|
+
{ type: 'greaterThan', column: 'colNum' as unknown as SUniversalPColumnId, x: 10 },
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
const result = convertFilterUiToExpressions(uiFilter);
|
|
31
|
+
expect(result.type).toBe('and');
|
|
32
|
+
expect((result as any).operands).toHaveLength(2);
|
|
33
|
+
expect((result as any).operands[0].type).toBe('is_na');
|
|
34
|
+
expect((result as any).operands[1].type).toBe('gt');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('should compile "not" filter to ptabler expression', () => {
|
|
38
|
+
const uiFilter: FilterSpec = {
|
|
39
|
+
type: 'not',
|
|
40
|
+
filter: { type: 'isNA', column: 'colA' as unknown as SUniversalPColumnId },
|
|
41
|
+
};
|
|
42
|
+
const result = convertFilterUiToExpressions(uiFilter);
|
|
43
|
+
expect(result.type).toBe('not');
|
|
44
|
+
expect((result as any).value.type).toBe('is_na');
|
|
45
|
+
expect((result as any).value.value.type).toBe('col');
|
|
46
|
+
expect((result as any).value.value.name).toBe('colA');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should compile "isNA" filter to ptabler expression', () => {
|
|
50
|
+
const uiFilter: FilterSpec = { type: 'isNA', column: 'colA' as unknown as SUniversalPColumnId };
|
|
51
|
+
const result = convertFilterUiToExpressions(uiFilter);
|
|
52
|
+
expect(result as any).toEqual({
|
|
53
|
+
type: 'is_na',
|
|
54
|
+
value: { type: 'col', name: 'colA' },
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should compile "isNotNA" filter to ptabler expression', () => {
|
|
59
|
+
const uiFilter: FilterSpec = { type: 'isNotNA', column: 'colA' as unknown as SUniversalPColumnId };
|
|
60
|
+
const result = convertFilterUiToExpressions(uiFilter);
|
|
61
|
+
expect(result as any).toEqual({
|
|
62
|
+
type: 'is_not_na',
|
|
63
|
+
value: { type: 'col', name: 'colA' },
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should compile "patternEquals" filter to ptabler expression', () => {
|
|
68
|
+
const uiFilter: FilterSpec = { type: 'patternEquals', column: 'colB' as unknown as SUniversalPColumnId, value: 'abc' };
|
|
69
|
+
const result = convertFilterUiToExpressions(uiFilter);
|
|
70
|
+
expect(result as any).toEqual({
|
|
71
|
+
type: 'eq',
|
|
72
|
+
lhs: { type: 'col', name: 'colB' },
|
|
73
|
+
rhs: { type: 'const', value: 'abc' },
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('should compile "patternNotEquals" filter to ptabler expression', () => {
|
|
78
|
+
const uiFilter: FilterSpec = { type: 'patternNotEquals', column: 'colB' as unknown as SUniversalPColumnId, value: 'abc' };
|
|
79
|
+
const result = convertFilterUiToExpressions(uiFilter);
|
|
80
|
+
expect(result as any).toEqual({
|
|
81
|
+
type: 'neq',
|
|
82
|
+
lhs: { type: 'col', name: 'colB' },
|
|
83
|
+
rhs: { type: 'const', value: 'abc' },
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('should compile "patternContainSubsequence" filter to ptabler expression', () => {
|
|
88
|
+
const uiFilter: FilterSpec = { type: 'patternContainSubsequence', column: 'colC' as unknown as SUniversalPColumnId, value: 'sub' };
|
|
89
|
+
const result = convertFilterUiToExpressions(uiFilter);
|
|
90
|
+
expect(result.type).toBe('str_contains');
|
|
91
|
+
expect((result as any).value).toEqual({ type: 'col', name: 'colC' });
|
|
92
|
+
expect((result as any).pattern).toEqual({ type: 'const', value: 'sub' });
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('should compile "patternNotContainSubsequence" filter to ptabler expression', () => {
|
|
96
|
+
const uiFilter: FilterSpec = { type: 'patternNotContainSubsequence', column: 'colC' as unknown as SUniversalPColumnId, value: 'sub' };
|
|
97
|
+
const result = convertFilterUiToExpressions(uiFilter);
|
|
98
|
+
expect(result.type).toBe('not');
|
|
99
|
+
expect((result as any).value.type).toBe('str_contains');
|
|
100
|
+
expect((result as any).value.value).toEqual({ type: 'col', name: 'colC' });
|
|
101
|
+
expect((result as any).value.pattern).toEqual({ type: 'const', value: 'sub' });
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should compile numerical comparison filters to ptabler expressions', () => {
|
|
105
|
+
const testCases = [
|
|
106
|
+
{ type: 'equal' as const, expected: 'eq' },
|
|
107
|
+
{ type: 'lessThan' as const, expected: 'lt' },
|
|
108
|
+
{ type: 'greaterThan' as const, expected: 'gt' },
|
|
109
|
+
{ type: 'lessThanOrEqual' as const, expected: 'le' },
|
|
110
|
+
{ type: 'greaterThanOrEqual' as const, expected: 'ge' },
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
testCases.forEach(({ type, expected }) => {
|
|
114
|
+
const uiFilter: FilterSpec = { type, column: 'colNum' as unknown as SUniversalPColumnId, x: 10 };
|
|
115
|
+
const result = convertFilterUiToExpressions(uiFilter);
|
|
116
|
+
expect(result as any).toEqual({
|
|
117
|
+
type: expected,
|
|
118
|
+
lhs: { type: 'col', name: 'colNum' },
|
|
119
|
+
rhs: { type: 'const', value: 10 },
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('should compile "lessThanColumn" filter to ptabler expression', () => {
|
|
125
|
+
const uiFilter: FilterSpec = {
|
|
126
|
+
type: 'lessThanColumn',
|
|
127
|
+
column: 'colNum1' as unknown as SUniversalPColumnId,
|
|
128
|
+
rhs: 'colNum2' as unknown as SUniversalPColumnId,
|
|
129
|
+
};
|
|
130
|
+
const result = convertFilterUiToExpressions(uiFilter);
|
|
131
|
+
expect(result as any).toEqual({
|
|
132
|
+
type: 'lt',
|
|
133
|
+
lhs: { type: 'col', name: 'colNum1' },
|
|
134
|
+
rhs: { type: 'col', name: 'colNum2' },
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should compile "lessThanColumn" filter with minDiff to ptabler expression', () => {
|
|
139
|
+
const uiFilter: FilterSpec = {
|
|
140
|
+
type: 'lessThanColumn',
|
|
141
|
+
column: 'colNum1' as unknown as SUniversalPColumnId,
|
|
142
|
+
rhs: 'colNum2' as unknown as SUniversalPColumnId,
|
|
143
|
+
minDiff: 5,
|
|
144
|
+
};
|
|
145
|
+
const result = convertFilterUiToExpressions(uiFilter);
|
|
146
|
+
expect(result as any).toEqual({
|
|
147
|
+
type: 'lt',
|
|
148
|
+
lhs: {
|
|
149
|
+
type: 'plus',
|
|
150
|
+
lhs: { type: 'col', name: 'colNum1' },
|
|
151
|
+
rhs: { type: 'const', value: 5 },
|
|
152
|
+
},
|
|
153
|
+
rhs: { type: 'col', name: 'colNum2' },
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('should compile "greaterThanColumn" filter to ptabler expression', () => {
|
|
158
|
+
const uiFilter: FilterSpec = {
|
|
159
|
+
type: 'greaterThanColumn',
|
|
160
|
+
column: 'colNum1' as unknown as SUniversalPColumnId,
|
|
161
|
+
rhs: 'colNum2' as unknown as SUniversalPColumnId,
|
|
162
|
+
};
|
|
163
|
+
const result = convertFilterUiToExpressions(uiFilter);
|
|
164
|
+
expect(result as any).toEqual({
|
|
165
|
+
type: 'gt',
|
|
166
|
+
lhs: { type: 'col', name: 'colNum1' },
|
|
167
|
+
rhs: { type: 'col', name: 'colNum2' },
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('should compile "greaterThanColumn" filter with minDiff to ptabler expression', () => {
|
|
172
|
+
const uiFilter: FilterSpec = {
|
|
173
|
+
type: 'greaterThanColumn',
|
|
174
|
+
column: 'colNum1' as unknown as SUniversalPColumnId,
|
|
175
|
+
rhs: 'colNum2' as unknown as SUniversalPColumnId,
|
|
176
|
+
minDiff: 7,
|
|
177
|
+
};
|
|
178
|
+
const result = convertFilterUiToExpressions(uiFilter);
|
|
179
|
+
expect(result as any).toEqual({
|
|
180
|
+
type: 'gt',
|
|
181
|
+
lhs: {
|
|
182
|
+
type: 'plus',
|
|
183
|
+
lhs: { type: 'col', name: 'colNum1' },
|
|
184
|
+
rhs: { type: 'const', value: 7 },
|
|
185
|
+
},
|
|
186
|
+
rhs: { type: 'col', name: 'colNum2' },
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('should compile "equalToColumn" filter to ptabler expression', () => {
|
|
191
|
+
const uiFilter: FilterSpec = {
|
|
192
|
+
type: 'equalToColumn',
|
|
193
|
+
column: 'colNum1' as unknown as SUniversalPColumnId,
|
|
194
|
+
rhs: 'colNum2' as unknown as SUniversalPColumnId,
|
|
195
|
+
};
|
|
196
|
+
const result = convertFilterUiToExpressions(uiFilter);
|
|
197
|
+
expect(result as any).toEqual({
|
|
198
|
+
type: 'eq',
|
|
199
|
+
lhs: { type: 'col', name: 'colNum1' },
|
|
200
|
+
rhs: { type: 'col', name: 'colNum2' },
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('should compile "greaterThanColumnOrEqual" filter to ptabler expression', () => {
|
|
205
|
+
const uiFilter: FilterSpec = {
|
|
206
|
+
type: 'greaterThanColumnOrEqual',
|
|
207
|
+
column: 'colNum1' as unknown as SUniversalPColumnId,
|
|
208
|
+
rhs: 'colNum2' as unknown as SUniversalPColumnId,
|
|
209
|
+
};
|
|
210
|
+
const result = convertFilterUiToExpressions(uiFilter);
|
|
211
|
+
expect(result as any).toEqual({
|
|
212
|
+
type: 'ge',
|
|
213
|
+
lhs: { type: 'col', name: 'colNum1' },
|
|
214
|
+
rhs: { type: 'col', name: 'colNum2' },
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('should compile "greaterThanColumnOrEqual" filter with minDiff to ptabler expression', () => {
|
|
219
|
+
const uiFilter: FilterSpec = {
|
|
220
|
+
type: 'greaterThanColumnOrEqual',
|
|
221
|
+
column: 'colNum1' as unknown as SUniversalPColumnId,
|
|
222
|
+
rhs: 'colNum2' as unknown as SUniversalPColumnId,
|
|
223
|
+
minDiff: 2,
|
|
224
|
+
};
|
|
225
|
+
const result = convertFilterUiToExpressions(uiFilter);
|
|
226
|
+
expect(result as any).toEqual({
|
|
227
|
+
type: 'ge',
|
|
228
|
+
lhs: {
|
|
229
|
+
type: 'plus',
|
|
230
|
+
lhs: { type: 'col', name: 'colNum1' },
|
|
231
|
+
rhs: { type: 'const', value: 2 },
|
|
232
|
+
},
|
|
233
|
+
rhs: { type: 'col', name: 'colNum2' },
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('should compile "lessThanColumnOrEqual" filter to ptabler expression', () => {
|
|
238
|
+
const uiFilter: FilterSpec = {
|
|
239
|
+
type: 'lessThanColumnOrEqual',
|
|
240
|
+
column: 'colNum1' as unknown as SUniversalPColumnId,
|
|
241
|
+
rhs: 'colNum2' as unknown as SUniversalPColumnId,
|
|
242
|
+
};
|
|
243
|
+
const result = convertFilterUiToExpressions(uiFilter);
|
|
244
|
+
expect(result as any).toEqual({
|
|
245
|
+
type: 'le',
|
|
246
|
+
lhs: { type: 'col', name: 'colNum1' },
|
|
247
|
+
rhs: { type: 'col', name: 'colNum2' },
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('should compile "lessThanColumnOrEqual" filter with minDiff to ptabler expression', () => {
|
|
252
|
+
const uiFilter: FilterSpec = {
|
|
253
|
+
type: 'lessThanColumnOrEqual',
|
|
254
|
+
column: 'colNum1' as unknown as SUniversalPColumnId,
|
|
255
|
+
rhs: 'colNum2' as unknown as SUniversalPColumnId,
|
|
256
|
+
minDiff: 3,
|
|
257
|
+
};
|
|
258
|
+
const result = convertFilterUiToExpressions(uiFilter);
|
|
259
|
+
expect(result as any).toEqual({
|
|
260
|
+
type: 'le',
|
|
261
|
+
lhs: {
|
|
262
|
+
type: 'plus',
|
|
263
|
+
lhs: { type: 'col', name: 'colNum1' },
|
|
264
|
+
rhs: { type: 'const', value: 3 },
|
|
265
|
+
},
|
|
266
|
+
rhs: { type: 'col', name: 'colNum2' },
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('should compile "topN" filter to ptabler expression', () => {
|
|
271
|
+
const uiFilter: FilterSpec = { type: 'topN', column: 'colNum' as unknown as SUniversalPColumnId, n: 5 };
|
|
272
|
+
const result = convertFilterUiToExpressions(uiFilter);
|
|
273
|
+
expect(result as any).toEqual({
|
|
274
|
+
type: 'le',
|
|
275
|
+
lhs: {
|
|
276
|
+
type: 'rank',
|
|
277
|
+
orderBy: [{ type: 'col', name: 'colNum' }],
|
|
278
|
+
partitionBy: [],
|
|
279
|
+
descending: true,
|
|
280
|
+
},
|
|
281
|
+
rhs: { type: 'const', value: 5 },
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('should compile "bottomN" filter to ptabler expression', () => {
|
|
286
|
+
const uiFilter: FilterSpec = { type: 'bottomN', column: 'colNum' as unknown as SUniversalPColumnId, n: 3 };
|
|
287
|
+
const result = convertFilterUiToExpressions(uiFilter);
|
|
288
|
+
expect(result as any).toEqual({
|
|
289
|
+
type: 'le',
|
|
290
|
+
lhs: {
|
|
291
|
+
type: 'rank',
|
|
292
|
+
orderBy: [{ type: 'col', name: 'colNum' }],
|
|
293
|
+
partitionBy: [],
|
|
294
|
+
descending: undefined, // ptabler-js sets descending to undefined when false
|
|
295
|
+
},
|
|
296
|
+
rhs: { type: 'const', value: 3 },
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
it('should compile nested filters to ptabler expressions', () => {
|
|
301
|
+
const uiFilter: FilterSpec = {
|
|
302
|
+
type: 'and',
|
|
303
|
+
filters: [
|
|
304
|
+
{
|
|
305
|
+
type: 'or',
|
|
306
|
+
filters: [
|
|
307
|
+
{ type: 'isNA', column: 'colA' as unknown as SUniversalPColumnId },
|
|
308
|
+
{ type: 'patternEquals', column: 'colB' as unknown as SUniversalPColumnId, value: 'test' },
|
|
309
|
+
],
|
|
310
|
+
},
|
|
311
|
+
{ type: 'greaterThan', column: 'colNum' as unknown as SUniversalPColumnId, x: 10 },
|
|
312
|
+
],
|
|
313
|
+
};
|
|
314
|
+
const result = convertFilterUiToExpressions(uiFilter);
|
|
315
|
+
expect(result.type).toBe('and');
|
|
316
|
+
expect((result as any).operands).toHaveLength(2);
|
|
317
|
+
expect((result as any).operands[0].type).toBe('or');
|
|
318
|
+
expect((result as any).operands[0].operands).toHaveLength(2);
|
|
319
|
+
expect((result as any).operands[1].type).toBe('gt');
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('should throw error for OR filter with no operands', () => {
|
|
323
|
+
const uiFilter: FilterSpec = { type: 'or', filters: [] };
|
|
324
|
+
expect(() => convertFilterUiToExpressions(uiFilter)).toThrow('OR filter requires at least one operand');
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('should throw error for AND filter with no operands', () => {
|
|
328
|
+
const uiFilter: FilterSpec = { type: 'and', filters: [] };
|
|
329
|
+
expect(() => convertFilterUiToExpressions(uiFilter)).toThrow('AND filter requires at least one operand');
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
it('should throw error for undefined filter type', () => {
|
|
333
|
+
const uiFilter: FilterSpec = { type: undefined };
|
|
334
|
+
expect(() => convertFilterUiToExpressions(uiFilter)).toThrow('Filter type is undefined, this should not happen');
|
|
335
|
+
});
|
|
336
|
+
});
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { assertNever } from '@milaboratories/pl-model-common';
|
|
2
|
+
import { and, col, lit, or, rank, type Expression, type ExpressionImpl } from '@milaboratories/ptabler-expression-js';
|
|
3
|
+
import type { FilterSpec } from '../filters';
|
|
4
|
+
|
|
5
|
+
export function convertFilterUiToExpressionImpl(value: FilterSpec): ExpressionImpl {
|
|
6
|
+
if (value.type === 'or') {
|
|
7
|
+
const expressions = value.filters.filter((f) => f.type !== undefined).map(convertFilterUiToExpressionImpl);
|
|
8
|
+
if (expressions.length === 0) {
|
|
9
|
+
throw new Error('OR filter requires at least one operand');
|
|
10
|
+
}
|
|
11
|
+
return or(...expressions);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (value.type === 'and') {
|
|
15
|
+
const expressions = value.filters.filter((f) => f.type !== undefined).map(convertFilterUiToExpressionImpl);
|
|
16
|
+
if (expressions.length === 0) {
|
|
17
|
+
throw new Error('AND filter requires at least one operand');
|
|
18
|
+
}
|
|
19
|
+
return and(...expressions);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (value.type === 'not') {
|
|
23
|
+
return convertFilterUiToExpressionImpl(value.filter).not();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (value.type === 'isNA') {
|
|
27
|
+
return col(value.column).isNull();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (value.type === 'isNotNA') {
|
|
31
|
+
return col(value.column).isNotNull();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (value.type === 'patternEquals') {
|
|
35
|
+
return col(value.column).eq(lit(value.value));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (value.type === 'patternNotEquals') {
|
|
39
|
+
return col(value.column).neq(lit(value.value));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (value.type === 'patternContainSubsequence') {
|
|
43
|
+
return col(value.column).strContains(value.value, false, true);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (value.type === 'patternNotContainSubsequence') {
|
|
47
|
+
return col(value.column).strContains(value.value, false, true).not();
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (value.type === 'equal') {
|
|
51
|
+
return col(value.column).eq(lit(value.x));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (value.type === 'lessThan') {
|
|
55
|
+
return col(value.column).lt(lit(value.x));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (value.type === 'greaterThan') {
|
|
59
|
+
return col(value.column).gt(lit(value.x));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (value.type === 'lessThanOrEqual') {
|
|
63
|
+
return col(value.column).le(lit(value.x));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (value.type === 'greaterThanOrEqual') {
|
|
67
|
+
return col(value.column).ge(lit(value.x));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (value.type === 'equalToColumn') {
|
|
71
|
+
return col(value.column).eq(col(value.rhs));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (value.type === 'greaterThanColumn') {
|
|
75
|
+
if (value.minDiff !== undefined && value.minDiff !== 0) {
|
|
76
|
+
return col(value.column).plus(lit(value.minDiff)).gt(col(value.rhs));
|
|
77
|
+
}
|
|
78
|
+
return col(value.column).gt(col(value.rhs));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (value.type === 'lessThanColumn') {
|
|
82
|
+
if (value.minDiff !== undefined && value.minDiff !== 0) {
|
|
83
|
+
return col(value.column).plus(lit(value.minDiff)).lt(col(value.rhs));
|
|
84
|
+
}
|
|
85
|
+
return col(value.column).lt(col(value.rhs));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (value.type === 'greaterThanColumnOrEqual') {
|
|
89
|
+
if (value.minDiff !== undefined && value.minDiff !== 0) {
|
|
90
|
+
return col(value.column).plus(lit(value.minDiff)).ge(col(value.rhs));
|
|
91
|
+
}
|
|
92
|
+
return col(value.column).ge(col(value.rhs));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (value.type === 'lessThanColumnOrEqual') {
|
|
96
|
+
if (value.minDiff !== undefined && value.minDiff !== 0) {
|
|
97
|
+
return col(value.column).plus(lit(value.minDiff)).le(col(value.rhs));
|
|
98
|
+
}
|
|
99
|
+
return col(value.column).le(col(value.rhs));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (value.type === 'topN') {
|
|
103
|
+
return rank(col(value.column), true).over([]).le(lit(value.n));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (value.type === 'bottomN') {
|
|
107
|
+
return rank(col(value.column), false).over([]).le(lit(value.n));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (value.type === undefined) {
|
|
111
|
+
throw new Error('Filter type is undefined, this should not happen');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
assertNever(value);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export function convertFilterUiToExpressions(value: FilterSpec): Expression {
|
|
118
|
+
return convertFilterUiToExpressionImpl(value).toJSON();
|
|
119
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { PColumnSpec, SUniversalPColumnId } from '@milaboratories/pl-model-common';
|
|
2
|
+
|
|
3
|
+
export type SimplifiedPColumnSpec = Pick<PColumnSpec, 'valueType' | 'annotations'>;
|
|
4
|
+
|
|
5
|
+
export type SimplifiedUniversalPColumnEntry = {
|
|
6
|
+
id: SUniversalPColumnId;
|
|
7
|
+
label: string;
|
|
8
|
+
obj: SimplifiedPColumnSpec;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type FilterSpecNode<Leaf, Common = {}> =
|
|
12
|
+
| Common & Leaf
|
|
13
|
+
| Common & { type: 'not'; filter: Common & Leaf }
|
|
14
|
+
| Common & { type: 'or'; filters: FilterSpecNode<Leaf, Common>[] }
|
|
15
|
+
| Common & { type: 'and'; filters: FilterSpecNode<Leaf, Common>[] };
|
|
16
|
+
|
|
17
|
+
export type FilterSpecLeaf =
|
|
18
|
+
| { type: undefined }
|
|
19
|
+
| { type: 'isNA'; column: SUniversalPColumnId }
|
|
20
|
+
| { type: 'isNotNA'; column: SUniversalPColumnId }
|
|
21
|
+
|
|
22
|
+
| { type: 'patternEquals'; column: SUniversalPColumnId; value: string }
|
|
23
|
+
| { type: 'patternNotEquals'; column: SUniversalPColumnId; value: string }
|
|
24
|
+
| { type: 'patternContainSubsequence'; column: SUniversalPColumnId; value: string }
|
|
25
|
+
| { type: 'patternNotContainSubsequence'; column: SUniversalPColumnId; value: string }
|
|
26
|
+
|
|
27
|
+
| { type: 'topN'; column: SUniversalPColumnId; n: number }
|
|
28
|
+
| { type: 'bottomN'; column: SUniversalPColumnId; n: number }
|
|
29
|
+
|
|
30
|
+
| { type: 'equal'; column: SUniversalPColumnId; x: number }
|
|
31
|
+
| { type: 'lessThan'; column: SUniversalPColumnId; x: number }
|
|
32
|
+
| { type: 'greaterThan'; column: SUniversalPColumnId; x: number }
|
|
33
|
+
| { type: 'lessThanOrEqual'; column: SUniversalPColumnId; x: number }
|
|
34
|
+
| { type: 'greaterThanOrEqual'; column: SUniversalPColumnId; x: number }
|
|
35
|
+
|
|
36
|
+
| { type: 'equalToColumn'; column: SUniversalPColumnId; rhs: SUniversalPColumnId }
|
|
37
|
+
| { type: 'lessThanColumn'; column: SUniversalPColumnId; rhs: SUniversalPColumnId; minDiff?: number }
|
|
38
|
+
| { type: 'greaterThanColumn'; column: SUniversalPColumnId; rhs: SUniversalPColumnId; minDiff?: number }
|
|
39
|
+
| { type: 'lessThanColumnOrEqual'; column: SUniversalPColumnId; rhs: SUniversalPColumnId; minDiff?: number }
|
|
40
|
+
| { type: 'greaterThanColumnOrEqual'; column: SUniversalPColumnId; rhs: SUniversalPColumnId; minDiff?: number };
|
|
41
|
+
|
|
42
|
+
export type FilterSpec<Leaf extends FilterSpecLeaf = FilterSpecLeaf, Common = {}> =
|
|
43
|
+
FilterSpecNode<Leaf, Common>;
|
|
44
|
+
|
|
45
|
+
export type FilterSpecType = Exclude<FilterSpec, { type: undefined }>['type'];
|
|
46
|
+
|
|
47
|
+
export type FilterSpecOfType<T extends FilterSpecType> = Extract<FilterSpec, { type: T }>;
|
package/src/index.ts
CHANGED
|
@@ -12,6 +12,8 @@ export * from './sdk_info';
|
|
|
12
12
|
export * from './raw_globals';
|
|
13
13
|
export * from './block_api_v1';
|
|
14
14
|
export * from './block_api_v2';
|
|
15
|
+
export * from './filters';
|
|
16
|
+
export * from './annotations';
|
|
15
17
|
|
|
16
18
|
// reexporting everything from SDK model
|
|
17
19
|
export * from '@milaboratories/pl-model-common';
|
package/src/render/api.ts
CHANGED
|
@@ -53,6 +53,7 @@ import type { LabelDerivationOps } from './util/label';
|
|
|
53
53
|
import { deriveLabels } from './util/label';
|
|
54
54
|
import type { APColumnSelectorWithSplit } from './util/split_selectors';
|
|
55
55
|
import { patchInSetFilters } from './util/pframe_upgraders';
|
|
56
|
+
import { allPColumnsReady } from './util/pcolumn_data';
|
|
56
57
|
|
|
57
58
|
export type PColumnDataUniversal = TreeNodeAccessor | DataInfo<TreeNodeAccessor> | PColumnValues;
|
|
58
59
|
|
|
@@ -618,21 +619,22 @@ export class RenderCtx<Args, UiState> {
|
|
|
618
619
|
}
|
|
619
620
|
|
|
620
621
|
// TODO remove all non-PColumn fields
|
|
621
|
-
public createPFrame(def: PFrameDef<PColumnDataUniversal>): PFrameHandle {
|
|
622
|
+
public createPFrame(def: PFrameDef<PColumnDataUniversal>): PFrameHandle | undefined {
|
|
622
623
|
this.verifyInlineAndExplicitColumnsSupport(def);
|
|
624
|
+
if (!allPColumnsReady(def)) return undefined;
|
|
623
625
|
return this.ctx.createPFrame(
|
|
624
626
|
def.map((c) => transformPColumnData(c)),
|
|
625
627
|
);
|
|
626
628
|
}
|
|
627
629
|
|
|
628
630
|
// TODO remove all non-PColumn fields
|
|
629
|
-
public createPTable(def: PTableDef<PColumn<PColumnDataUniversal>>): PTableHandle;
|
|
631
|
+
public createPTable(def: PTableDef<PColumn<PColumnDataUniversal>>): PTableHandle | undefined;
|
|
630
632
|
public createPTable(def: {
|
|
631
633
|
columns: PColumn<PColumnDataUniversal>[];
|
|
632
634
|
filters?: PTableRecordFilter[];
|
|
633
635
|
/** Table sorting */
|
|
634
636
|
sorting?: PTableSorting[];
|
|
635
|
-
}): PTableHandle;
|
|
637
|
+
}): PTableHandle | undefined;
|
|
636
638
|
public createPTable(
|
|
637
639
|
def:
|
|
638
640
|
| PTableDef<PColumn<PColumnDataUniversal>>
|
|
@@ -642,7 +644,7 @@ export class RenderCtx<Args, UiState> {
|
|
|
642
644
|
/** Table sorting */
|
|
643
645
|
sorting?: PTableSorting[];
|
|
644
646
|
},
|
|
645
|
-
): PTableHandle {
|
|
647
|
+
): PTableHandle | undefined {
|
|
646
648
|
let rawDef: PTableDef<PColumn<PColumnDataUniversal>>;
|
|
647
649
|
if ('columns' in def) {
|
|
648
650
|
rawDef = this.patchPTableDef({
|
|
@@ -657,7 +659,9 @@ export class RenderCtx<Args, UiState> {
|
|
|
657
659
|
} else {
|
|
658
660
|
rawDef = this.patchPTableDef(def);
|
|
659
661
|
}
|
|
660
|
-
|
|
662
|
+
const columns = extractAllColumns(rawDef.src);
|
|
663
|
+
this.verifyInlineAndExplicitColumnsSupport(columns);
|
|
664
|
+
if (!allPColumnsReady(columns)) return undefined;
|
|
661
665
|
return this.ctx.createPTable(
|
|
662
666
|
mapPTableDef(rawDef, (po) => transformPColumnData(po)),
|
|
663
667
|
);
|
|
@@ -1,39 +1,39 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
AnchoredIdDeriver,
|
|
3
|
+
AnchoredPColumnSelector,
|
|
4
|
+
AxisFilterByIdx,
|
|
5
|
+
AxisFilterValue,
|
|
3
6
|
AxisId,
|
|
7
|
+
NativePObjectId,
|
|
8
|
+
PartitionedDataInfoEntries,
|
|
4
9
|
PColumn,
|
|
5
10
|
PColumnSelector,
|
|
6
11
|
PColumnSpec,
|
|
12
|
+
PColumnValues,
|
|
7
13
|
PObjectId,
|
|
8
|
-
SUniversalPColumnId,
|
|
9
|
-
AxisFilterValue,
|
|
10
|
-
AxisFilterByIdx,
|
|
11
|
-
AnchoredPColumnSelector,
|
|
12
|
-
PartitionedDataInfoEntries,
|
|
13
14
|
ResolveAnchorsOptions,
|
|
14
|
-
|
|
15
|
-
PColumnValues,
|
|
15
|
+
SUniversalPColumnId,
|
|
16
16
|
} from '@milaboratories/pl-model-common';
|
|
17
17
|
import {
|
|
18
|
-
|
|
19
|
-
resolveAnchors,
|
|
20
|
-
getAxisId,
|
|
21
|
-
isPColumnSpec,
|
|
18
|
+
Annotation,
|
|
22
19
|
canonicalizeAxisId,
|
|
23
|
-
isPartitionedDataInfoEntries,
|
|
24
|
-
entriesToDataInfo,
|
|
25
20
|
deriveNativeId,
|
|
26
|
-
|
|
21
|
+
entriesToDataInfo,
|
|
22
|
+
getAxisId,
|
|
23
|
+
isPartitionedDataInfoEntries,
|
|
24
|
+
isPColumnSpec,
|
|
25
|
+
resolveAnchors,
|
|
26
|
+
selectorsToPredicate,
|
|
27
27
|
} from '@milaboratories/pl-model-common';
|
|
28
|
+
import canonicalize from 'canonicalize';
|
|
29
|
+
import type { Optional } from 'utility-types';
|
|
28
30
|
import type { TreeNodeAccessor } from '../accessor';
|
|
31
|
+
import type { PColumnDataUniversal } from '../api';
|
|
32
|
+
import { filterDataInfoEntries } from './axis_filtering';
|
|
29
33
|
import type { LabelDerivationOps, TraceEntry } from './label';
|
|
30
34
|
import { deriveLabels } from './label';
|
|
31
|
-
import
|
|
35
|
+
import { convertOrParsePColumnData, getUniquePartitionKeys } from './pcolumn_data';
|
|
32
36
|
import type { APColumnSelectorWithSplit, PColumnSelectorWithSplit } from './split_selectors';
|
|
33
|
-
import canonicalize from 'canonicalize';
|
|
34
|
-
import { getUniquePartitionKeys, convertOrParsePColumnData } from './pcolumn_data';
|
|
35
|
-
import { filterDataInfoEntries } from './axis_filtering';
|
|
36
|
-
import type { PColumnDataUniversal } from '../api';
|
|
37
37
|
|
|
38
38
|
function isPColumnValues(value: unknown): value is PColumnValues {
|
|
39
39
|
if (!Array.isArray(value)) return false;
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
DataInfo,
|
|
3
3
|
PartitionedDataInfoEntries,
|
|
4
|
+
PColumn,
|
|
5
|
+
PColumnValues,
|
|
4
6
|
} from '@milaboratories/pl-model-common';
|
|
5
7
|
import {
|
|
6
8
|
dataInfoToEntries,
|
|
7
9
|
isDataInfo,
|
|
8
10
|
isDataInfoEntries,
|
|
11
|
+
visitDataInfo,
|
|
9
12
|
type BinaryChunk,
|
|
10
13
|
type DataInfoEntries,
|
|
11
14
|
type PColumnDataEntry,
|
|
12
15
|
type PColumnKey,
|
|
13
16
|
} from '@milaboratories/pl-model-common';
|
|
14
17
|
import { TreeNodeAccessor } from '../accessor';
|
|
18
|
+
import type { PColumnDataUniversal } from '../api';
|
|
15
19
|
|
|
16
20
|
const PCD_PREFIX = 'PColumnData/';
|
|
17
21
|
|
|
@@ -507,3 +511,23 @@ DataInfoEntries<TreeNodeAccessor> | undefined {
|
|
|
507
511
|
|
|
508
512
|
throw new Error(`Unexpected input type: ${typeof acc}`);
|
|
509
513
|
}
|
|
514
|
+
|
|
515
|
+
export function isPColumnReady(c: PColumn<PColumnDataUniversal>): boolean {
|
|
516
|
+
const isValues = (d: PColumnDataUniversal): d is PColumnValues => Array.isArray(d);
|
|
517
|
+
const isAccessor = (d: PColumnDataUniversal): d is TreeNodeAccessor => d instanceof TreeNodeAccessor;
|
|
518
|
+
|
|
519
|
+
let ready = true;
|
|
520
|
+
if (isAccessor(c.data)) {
|
|
521
|
+
ready &&= c.data.getIsReadyOrError();
|
|
522
|
+
} else if (isDataInfo(c.data)) {
|
|
523
|
+
visitDataInfo(c.data, (v) => ready &&= v.getIsReadyOrError());
|
|
524
|
+
} else if (!isValues(c.data)) {
|
|
525
|
+
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
|
|
526
|
+
throw Error(`unsupported column data type: ${c.data satisfies never}`);
|
|
527
|
+
}
|
|
528
|
+
return ready;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
export function allPColumnsReady(columns: PColumn<PColumnDataUniversal>[]): boolean {
|
|
532
|
+
return columns.every(isPColumnReady);
|
|
533
|
+
}
|