Skip to content

Commit 52d604f

Browse files
feat: added support for in/not in operator (#81)
* added support for in operator * added support for not in operator * test fixes * fixed group by test * bumped meerkat version * transforms tests * reverted package lockk * updated node duck db version * added BigInt in aggregate check * misc changes * bumped meekat node duck db
1 parent 4a17236 commit 52d604f

File tree

14 files changed

+829
-64
lines changed

14 files changed

+829
-64
lines changed

meerkat-browser/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@devrev/meerkat-browser",
3-
"version": "0.0.73",
3+
"version": "0.0.74",
44
"dependencies": {
55
"@swc/helpers": "~0.5.0",
66
"@devrev/meerkat-core": "*",

meerkat-core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@devrev/meerkat-core",
3-
"version": "0.0.75",
3+
"version": "0.0.76",
44
"dependencies": {
55
"@swc/helpers": "~0.5.0"
66
},

meerkat-core/src/cube-filter-transformer/factory.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@ import { equalsTransform } from './equals/equals';
2020
import { gtTransform } from './gt/gt';
2121
import { gteTransform } from './gte/gte';
2222
import { inDataRangeTransform } from './in-date-range/in-date-range';
23+
import { inTransform } from './in/in';
2324
import { ltTransform } from './lt/lt';
2425
import { lteTransform } from './lte/lte';
2526
import { notInDataRangeTransform } from './not-In-date-range/not-In-date-range';
2627
import { notContainsTransform } from './not-contains/not-contains';
2728
import { notEqualsTransform } from './not-equals/not-equals';
29+
import { notInTransform } from './not-in/not-in';
2830
import { notSetTransform } from './not-set/not-set';
2931
import { orDuckdbCondition } from './or/or';
3032
import { setTransform } from './set/set';
@@ -40,6 +42,10 @@ const cubeFilterOperatorsToDuckdb = (cubeFilter: QueryOperatorsWithInfo) => {
4042
return equalsTransform(cubeFilter);
4143
case 'notEquals':
4244
return notEqualsTransform(cubeFilter);
45+
case 'in':
46+
return inTransform(cubeFilter);
47+
case 'notIn':
48+
return notInTransform(cubeFilter);
4349
case 'contains':
4450
return containsTransform(cubeFilter);
4551
case 'notContains':
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import { ConjunctionExpression } from '../../types/duckdb-serialization-types/serialization/ParsedExpression';
2+
import { inTransform } from './in';
3+
4+
describe('In transforms Tests', () => {
5+
it('Should throw error if values are undefined', () => {
6+
expect(() =>
7+
inTransform({
8+
member: 'country',
9+
operator: 'contains',
10+
memberInfo: {
11+
name: 'country',
12+
sql: 'table.country',
13+
type: 'string',
14+
},
15+
})
16+
).toThrow();
17+
});
18+
19+
it('Should return the correct value for string member', () => {
20+
const expectedOutput = {
21+
"alias": "",
22+
"children": [
23+
{
24+
"alias": "",
25+
"class": "COLUMN_REF",
26+
"column_names": [
27+
"country",
28+
],
29+
"type": "COLUMN_REF",
30+
},
31+
{
32+
"alias": "",
33+
"class": "CONSTANT",
34+
"type": "VALUE_CONSTANT",
35+
"value": {
36+
"is_null": false,
37+
"type": {
38+
"id": "VARCHAR",
39+
"type_info": null,
40+
},
41+
"value": "US",
42+
},
43+
},
44+
],
45+
"class": "OPERATOR",
46+
"type": "COMPARE_IN",
47+
};
48+
expect(
49+
inTransform({
50+
member: 'country',
51+
operator: 'contains',
52+
values: ['US'],
53+
memberInfo: {
54+
name: 'country',
55+
sql: 'table.country',
56+
type: 'string',
57+
},
58+
})
59+
).toEqual(expectedOutput);
60+
});
61+
62+
it('Should return the correct value for string_array member', () => {
63+
const output = inTransform({
64+
member: 'country',
65+
operator: 'contains',
66+
values: ['US', 'Germany', 'Israel'],
67+
memberInfo: {
68+
name: 'country',
69+
sql: 'table.country',
70+
type: 'string_array',
71+
},
72+
}) as ConjunctionExpression;
73+
expect(output).toEqual( {
74+
"alias": "",
75+
"catalog": "",
76+
"children": [
77+
{
78+
"alias": "",
79+
"class": "COLUMN_REF",
80+
"column_names": [
81+
"country",
82+
],
83+
"type": "COLUMN_REF",
84+
},
85+
{
86+
"alias": "",
87+
"children": [
88+
{
89+
"alias": "",
90+
"class": "CONSTANT",
91+
"type": "VALUE_CONSTANT",
92+
"value": {
93+
"is_null": false,
94+
"type": {
95+
"id": "VARCHAR",
96+
"type_info": null,
97+
},
98+
"value": "US",
99+
},
100+
},
101+
{
102+
"alias": "",
103+
"class": "CONSTANT",
104+
"type": "VALUE_CONSTANT",
105+
"value": {
106+
"is_null": false,
107+
"type": {
108+
"id": "VARCHAR",
109+
"type_info": null,
110+
},
111+
"value": "Germany",
112+
},
113+
},
114+
{
115+
"alias": "",
116+
"class": "CONSTANT",
117+
"type": "VALUE_CONSTANT",
118+
"value": {
119+
"is_null": false,
120+
"type": {
121+
"id": "VARCHAR",
122+
"type_info": null,
123+
},
124+
"value": "Israel",
125+
},
126+
}],
127+
"class": "OPERATOR",
128+
"type": "ARRAY_CONSTRUCTOR",
129+
},
130+
],
131+
"class": "FUNCTION",
132+
"distinct": false,
133+
"export_state": false,
134+
"filter": null,
135+
"function_name": "&&",
136+
"is_operator": true,
137+
"order_bys": {
138+
"orders": [],
139+
"type": "ORDER_MODIFIER",
140+
},
141+
"schema": "",
142+
"type": "FUNCTION",
143+
});
144+
});
145+
});
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { Dimension, Measure } from '../../types/cube-types/table';
2+
import {
3+
ExpressionClass,
4+
ExpressionType
5+
} from '../../types/duckdb-serialization-types/serialization/Expression';
6+
import { valueBuilder } from "../base-condition-builder/base-condition-builder";
7+
import { CubeToParseExpressionTransform } from "../factory";
8+
9+
10+
const inDuckDbCondition = (
11+
columnName: string,
12+
values: string[],
13+
memberInfo: Measure | Dimension
14+
) => {
15+
const sqlTreeValues = values.map((value) => {
16+
return {
17+
class: ExpressionClass.CONSTANT,
18+
type: ExpressionType.VALUE_CONSTANT,
19+
alias: "",
20+
value: valueBuilder(value, memberInfo)
21+
}
22+
})
23+
const columnRef = {
24+
class: "COLUMN_REF",
25+
type: "COLUMN_REF",
26+
alias: "",
27+
column_names: columnName.split('.')
28+
}
29+
switch (memberInfo.type) {
30+
case 'number_array':
31+
case 'string_array': {
32+
return {
33+
"class": ExpressionClass.FUNCTION,
34+
"type": ExpressionType.FUNCTION,
35+
"alias": "",
36+
"function_name": "&&",
37+
"schema": "",
38+
"children": [
39+
columnRef,
40+
{
41+
class: ExpressionClass.OPERATOR,
42+
type: ExpressionType.ARRAY_CONSTRUCTOR,
43+
alias: "",
44+
children: sqlTreeValues,
45+
}
46+
],
47+
"filter": null,
48+
"order_bys": {
49+
"type": "ORDER_MODIFIER",
50+
"orders": []
51+
},
52+
"distinct": false,
53+
"is_operator": true,
54+
"export_state": false,
55+
"catalog": ""
56+
}
57+
}
58+
default: {
59+
return {
60+
class: ExpressionClass.OPERATOR,
61+
type: ExpressionType.COMPARE_IN,
62+
alias: "",
63+
children: [
64+
columnRef,
65+
...sqlTreeValues
66+
]
67+
}
68+
}
69+
}
70+
71+
}
72+
73+
74+
export const inTransform: CubeToParseExpressionTransform = (query) => {
75+
const { member, values, memberInfo } = query;
76+
if (!values) {
77+
throw new Error('In filter must have at least one value');
78+
}
79+
return inDuckDbCondition(member, values, memberInfo);
80+
}

0 commit comments

Comments
 (0)