Skip to content

Commit

Permalink
Added includes
Browse files Browse the repository at this point in the history
  • Loading branch information
grassick committed Mar 18, 2021
1 parent 79dca76 commit ee6c697
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 22 deletions.
33 changes: 28 additions & 5 deletions lib/ExprCompiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -675,17 +675,40 @@ var ExprCompiler = /** @class */ (function () {
if (compiledExprs[0] == null || compiledExprs[1] == null) {
return null;
}
// Null if no expressions in literal list
if ((compiledExprs[1].type === "literal") && (compiledExprs[1].value.length === 0)) {
// Use (select bool_or(x.value) from (select LEFT::jsonb @> jsonb_array_elements(RIGHT::jsonb) as value) as x)
return {
type: "scalar",
expr: { type: "op", op: "bool_or", exprs: [{ type: "field", tableAlias: "elements", column: "value" }] },
from: {
type: "subquery",
alias: "elements",
query: {
type: "query",
selects: [
{
type: "select",
expr: { type: "op", op: "@>", exprs: [
convertToJsonB(compiledExprs[0]),
{ type: "op", op: "jsonb_array_elements", exprs: [convertToJsonB(compiledExprs[1])] }
] },
alias: "value"
}
]
}
}
};
case "includes":
// Null if either not present
if (compiledExprs[0] == null || compiledExprs[1] == null) {
return null;
}
// Cast to jsonb and use ?| Also convert to json first to handle literal arrays
// Cast both to jsonb and use @>. Also convert both to json first to handle literal arrays
return {
type: "op",
op: "?|",
op: "@>",
exprs: [
convertToJsonB(compiledExprs[0]),
compiledExprs[1]
convertToJsonB(compiledExprs[1])
]
};
case "length":
Expand Down
5 changes: 5 additions & 0 deletions lib/PromiseExprEvaluator.js
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,11 @@ var PromiseExprEvaluator = /** @class */ (function () {
return null;
}
return lodash_1.default.intersection(values[0], values[1]).length > 0;
case "includes":
if (hasNull) {
return null;
}
return lodash_1.default.includes(values[0], values[1]);
case "length":
if (hasNull) {
return 0;
Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 32 additions & 8 deletions src/ExprCompiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -723,29 +723,53 @@ export default class ExprCompiler {
convertToJsonB(compiledExprs[0]),
convertToJsonB(compiledExprs[1])
]
};
}

case "intersects":
// Null if either not present
if (compiledExprs[0] == null || compiledExprs[1] == null) {
return null;
}

// Null if no expressions in literal list
if (((compiledExprs[1] as any).type === "literal") && ((compiledExprs[1] as any).value.length === 0)) {
// Use (select bool_or(x.value) from (select LEFT::jsonb @> jsonb_array_elements(RIGHT::jsonb) as value) as x)
return {
type: "scalar",
expr: { type: "op", op: "bool_or", exprs: [{ type: "field", tableAlias: "elements", column: "value" }] },
from: {
type: "subquery",
alias: "elements",
query: {
type: "query",
selects: [
{
type: "select",
expr: { type: "op", op: "@>", exprs: [
convertToJsonB(compiledExprs[0]),
{ type: "op", op: "jsonb_array_elements", exprs: [convertToJsonB(compiledExprs[1])] }
]},
alias: "value"
}
]
}
}
}

case "includes":
// Null if either not present
if (compiledExprs[0] == null || compiledExprs[1] == null) {
return null;
}

// Cast to jsonb and use ?| Also convert to json first to handle literal arrays
// Cast both to jsonb and use @>. Also convert both to json first to handle literal arrays
return {
type: "op",
op: "?|",
op: "@>",
exprs: [
convertToJsonB(compiledExprs[0]),
compiledExprs[1]
convertToJsonB(compiledExprs[1])
]
};

}
case "length":
// 0 if null
if ((compiledExprs[0] == null)) {
Expand Down
5 changes: 5 additions & 0 deletions src/PromiseExprEvaluator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,11 @@ export class PromiseExprEvaluator {
return null
}
return _.intersection(values[0], values[1]).length > 0
case "includes":
if (hasNull) {
return null
}
return _.includes(values[0], values[1])
case "length":
if (hasNull) {
return 0
Expand Down
36 changes: 28 additions & 8 deletions test/ExprCompilerTests.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -1493,37 +1493,57 @@ describe "ExprCompiler", ->
null
)

it "compiles intersects", ->
it "compiles includes", ->
@compile(
{
type: "op"
op: "intersects",
op: "includes",
exprs: [
{ type: "field", table: "t1", column: "enumset" }
{ type: "literal", valueType: "enumset", value: ["a", "b"] }
{ type: "literal", valueType: "enum", value: "a" }
]
}
{
type: "op"
op: "?|"
op: "@>"
exprs: [
{ type: "op", op: "to_jsonb", exprs: [{ type: "field", tableAlias: "T1", column: "enumset" }] }
{ type: "literal", value: ["a", "b"] }
{ type: "op", op: "::jsonb", exprs: [{ type: "literal", value: '"a"' }]}
]
}
)

it "compiles empty intersects", ->
it "compiles intersects", ->
@compile(
{
type: "op"
op: "intersects",
exprs: [
{ type: "field", table: "t1", column: "enumset" }
{ type: "literal", valueType: "enumset", value: [] }
{ type: "literal", valueType: "enumset", value: ["a", "b"] }
]
}
null
{
type: "scalar",
expr: { type: "op", op: "bool_or", exprs: [{ type: "field", tableAlias: "elements", column: "value" }] },
from: {
type: "subquery",
alias: "elements",
query: {
type: "query",
selects: [
{
type: "select",
expr: { type: "op", op: "@>", exprs: [
{ type: "op", op: "to_jsonb", exprs: [{ type: "field", tableAlias: "T1", column: "enumset" }] }
{ type: "op", op: "jsonb_array_elements", exprs: [{ type: "op", op: "::jsonb", exprs: [{ type: "literal", value: '["a","b"]' }]}] }
]},
alias: "value"
}
]
}
}
}
)

it "compiles length", ->
Expand Down
3 changes: 3 additions & 0 deletions test/testExprs.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ addOp(false, "intersects", literal(["a", "b", "c"], "enumset"), literal(["d"], "
addOp(true, "intersects", literal(["a", "b", "c"], "text[]"), literal(["a", "x"], "text[]"))
addOp(false, "intersects", literal(["a", "b", "c"], "text[]"), literal(["d"], "text[]"))

addOp(true, "includes", literal(["a", "b", "c"], "text[]"), literal("a", "text"))
addOp(false, "includes", literal(["a", "b", "c"], "enumset"), literal("d", "enum"))

# Length of null returns 0 as enumsets are not stored as [] when empty, but rather as null
addOp(2, "length", literal(["a", "b"], "enumset"))
addOp(0, "length", literal(null, "enumset"))
Expand Down

0 comments on commit ee6c697

Please sign in to comment.