feat(index): $nin and $not operators (#13289)
* feat(index): add $not and $nin operators * logical operator * test * test * types * logical * schema ID * types and $ilike fix * index type
This commit is contained in:
1 parent
b3fb0f7634
commit
f764b3a364
8 files changed
+264
-20
No files matched your search
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/index": patch
|
||||||
|
"@medusajs/types": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat(index): $nin and $not operators
|
||||||
@@ -160,14 +160,14 @@ medusaIntegrationTestRunner({
|
|||||||
...(indexEngine as any).moduleOptions_,
|
...(indexEngine as any).moduleOptions_,
|
||||||
schema: `
|
schema: `
|
||||||
type Product @Listeners(values: ["product.created", "product.updated", "product.deleted"]) {
|
type Product @Listeners(values: ["product.created", "product.updated", "product.deleted"]) {
|
||||||
id: String
|
id: ID
|
||||||
title: String
|
title: String
|
||||||
handle: String
|
handle: String
|
||||||
variants: [ProductVariant]
|
variants: [ProductVariant]
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProductVariant @Listeners(values: ["variant.created", "variant.updated", "variant.deleted"]) {
|
type ProductVariant @Listeners(values: ["variant.created", "variant.updated", "variant.deleted"]) {
|
||||||
id: String
|
id: ID
|
||||||
product_id: String
|
product_id: String
|
||||||
sku: String
|
sku: String
|
||||||
description: String
|
description: String
|
||||||
|
|||||||
@@ -5,8 +5,12 @@ export type IndexOperatorMap<T> = {
|
|||||||
$gt?: T
|
$gt?: T
|
||||||
$gte?: T
|
$gte?: T
|
||||||
$ne?: T
|
$ne?: T
|
||||||
$in?: T
|
$in?: T[]
|
||||||
|
$nin?: T[]
|
||||||
$is?: T
|
$is?: T
|
||||||
$like?: T
|
$like?: T
|
||||||
$ilike?: T
|
$ilike?: T
|
||||||
|
$and?: T[]
|
||||||
|
$or?: T[]
|
||||||
|
$not?: T | T[]
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
export const schema = `
|
export const schema = `
|
||||||
type Product @Listeners(values: ["product.created", "product.updated", "product.deleted"]) {
|
type Product @Listeners(values: ["product.created", "product.updated", "product.deleted"]) {
|
||||||
id: String
|
id: ID
|
||||||
title: String
|
title: String
|
||||||
created_at: DateTime
|
created_at: DateTime
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ export const schema = `
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ProductVariant @Listeners(values: ["variant.created", "variant.updated", "variant.deleted"]) {
|
type ProductVariant @Listeners(values: ["variant.created", "variant.updated", "variant.deleted"]) {
|
||||||
id: String
|
id: ID
|
||||||
product_id: String
|
product_id: String
|
||||||
sku: String
|
sku: String
|
||||||
prices: [Price]
|
prices: [Price]
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
export const updateRemovedSchema = `
|
export const updateRemovedSchema = `
|
||||||
type Product @Listeners(values: ["product.created", "product.updated", "product.deleted"]) {
|
type Product @Listeners(values: ["product.created", "product.updated", "product.deleted"]) {
|
||||||
id: String
|
id: ID
|
||||||
title: String
|
title: String
|
||||||
handle: String
|
handle: String
|
||||||
variants: [ProductVariant]
|
variants: [ProductVariant]
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProductVariant @Listeners(values: ["variant.created", "variant.updated", "variant.deleted"]) {
|
type ProductVariant @Listeners(values: ["variant.created", "variant.updated", "variant.deleted"]) {
|
||||||
id: String
|
id: ID
|
||||||
product_id: String
|
product_id: String
|
||||||
sku: String
|
sku: String
|
||||||
description: String
|
description: String
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export const updatedSchema = `
|
export const updatedSchema = `
|
||||||
type Product @Listeners(values: ["product.created", "product.updated", "product.deleted"]) {
|
type Product @Listeners(values: ["product.created", "product.updated", "product.deleted"]) {
|
||||||
id: String
|
id: ID
|
||||||
title: String
|
title: String
|
||||||
handle: String
|
handle: String
|
||||||
deep: InternalNested
|
deep: InternalNested
|
||||||
@@ -17,7 +17,7 @@ export const updatedSchema = `
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ProductVariant @Listeners(values: ["variant.created", "variant.updated", "variant.deleted"]) {
|
type ProductVariant @Listeners(values: ["variant.created", "variant.updated", "variant.deleted"]) {
|
||||||
id: String
|
id: ID
|
||||||
product_id: String
|
product_id: String
|
||||||
sku: String
|
sku: String
|
||||||
prices: [Price]
|
prices: [Price]
|
||||||
|
|||||||
@@ -135,6 +135,7 @@ describe("IndexModuleService query", function () {
|
|||||||
name: "Product",
|
name: "Product",
|
||||||
data: {
|
data: {
|
||||||
id: "prod_1",
|
id: "prod_1",
|
||||||
|
title: "Product 1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -296,7 +297,24 @@ describe("IndexModuleService query", function () {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const { data: dataNot } = await module.query({
|
||||||
|
fields: ["product.*", "product.variants.*", "product.variants.prices.*"],
|
||||||
|
filters: {
|
||||||
|
product: {
|
||||||
|
variants: {
|
||||||
|
sku: {
|
||||||
|
$not: {
|
||||||
|
$eq: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
expect(data.length).toEqual(1)
|
expect(data.length).toEqual(1)
|
||||||
|
expect(dataNot.length).toEqual(1)
|
||||||
|
expect(dataNot).toEqual(data)
|
||||||
|
|
||||||
const { data: data2 } = await module.query({
|
const { data: data2 } = await module.query({
|
||||||
fields: ["product.*", "product.variants.*", "product.variants.prices.*"],
|
fields: ["product.*", "product.variants.*", "product.variants.prices.*"],
|
||||||
@@ -340,6 +358,7 @@ describe("IndexModuleService query", function () {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "prod_1",
|
id: "prod_1",
|
||||||
|
title: "Product 1",
|
||||||
variants: [
|
variants: [
|
||||||
{
|
{
|
||||||
id: "var_2",
|
id: "var_2",
|
||||||
@@ -399,6 +418,7 @@ describe("IndexModuleService query", function () {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "prod_1",
|
id: "prod_1",
|
||||||
|
title: "Product 1",
|
||||||
variants: [
|
variants: [
|
||||||
{
|
{
|
||||||
id: "var_2",
|
id: "var_2",
|
||||||
@@ -456,6 +476,7 @@ describe("IndexModuleService query", function () {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "prod_1",
|
id: "prod_1",
|
||||||
|
title: "Product 1",
|
||||||
variants: [
|
variants: [
|
||||||
{
|
{
|
||||||
id: "var_1",
|
id: "var_1",
|
||||||
@@ -499,6 +520,7 @@ describe("IndexModuleService query", function () {
|
|||||||
expect(dataAsc).toEqual([
|
expect(dataAsc).toEqual([
|
||||||
{
|
{
|
||||||
id: "prod_1",
|
id: "prod_1",
|
||||||
|
title: "Product 1",
|
||||||
variants: [
|
variants: [
|
||||||
{
|
{
|
||||||
id: "var_2",
|
id: "var_2",
|
||||||
@@ -597,6 +619,7 @@ describe("IndexModuleService query", function () {
|
|||||||
expect(data).toEqual([
|
expect(data).toEqual([
|
||||||
{
|
{
|
||||||
id: "prod_1",
|
id: "prod_1",
|
||||||
|
title: "Product 1",
|
||||||
variants: [
|
variants: [
|
||||||
{
|
{
|
||||||
id: "var_1",
|
id: "var_1",
|
||||||
@@ -646,6 +669,7 @@ describe("IndexModuleService query", function () {
|
|||||||
expect(data).toEqual([
|
expect(data).toEqual([
|
||||||
{
|
{
|
||||||
id: "prod_1",
|
id: "prod_1",
|
||||||
|
title: "Product 1",
|
||||||
variants: [
|
variants: [
|
||||||
{
|
{
|
||||||
id: "var_1",
|
id: "var_1",
|
||||||
@@ -782,6 +806,7 @@ describe("IndexModuleService query", function () {
|
|||||||
expect(data).toEqual([
|
expect(data).toEqual([
|
||||||
{
|
{
|
||||||
id: "prod_1",
|
id: "prod_1",
|
||||||
|
title: "Product 1",
|
||||||
variants: [
|
variants: [
|
||||||
{
|
{
|
||||||
id: "var_1",
|
id: "var_1",
|
||||||
@@ -922,6 +947,7 @@ describe("IndexModuleService query", function () {
|
|||||||
expect(data).toEqual([
|
expect(data).toEqual([
|
||||||
{
|
{
|
||||||
id: "prod_1",
|
id: "prod_1",
|
||||||
|
title: "Product 1",
|
||||||
variants: [
|
variants: [
|
||||||
{
|
{
|
||||||
id: "var_1",
|
id: "var_1",
|
||||||
@@ -937,4 +963,165 @@ describe("IndexModuleService query", function () {
|
|||||||
},
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should query products filtering product not in [X]", async () => {
|
||||||
|
const expected = [
|
||||||
|
{
|
||||||
|
id: "prod_2",
|
||||||
|
title: "Product 2 title",
|
||||||
|
deep: {
|
||||||
|
a: 1,
|
||||||
|
obj: {
|
||||||
|
b: 15,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const { data } = await module.query({
|
||||||
|
fields: ["product.*"],
|
||||||
|
filters: {
|
||||||
|
product: {
|
||||||
|
$not: [
|
||||||
|
{
|
||||||
|
id: {
|
||||||
|
$in: ["prod_1"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(data).toEqual(expected)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should query products filtering product not in [X] using $nin", async () => {
|
||||||
|
const expected = [
|
||||||
|
{
|
||||||
|
id: "prod_2",
|
||||||
|
title: "Product 2 title",
|
||||||
|
deep: {
|
||||||
|
a: 1,
|
||||||
|
obj: {
|
||||||
|
b: 15,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const { data } = await module.query({
|
||||||
|
fields: ["product.*"],
|
||||||
|
filters: {
|
||||||
|
product: {
|
||||||
|
id: {
|
||||||
|
$nin: ["prod_1"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(data).toEqual(expected)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should query products with variants.sku not in [X] and title eq", async () => {
|
||||||
|
const expected = [
|
||||||
|
{
|
||||||
|
id: "prod_2",
|
||||||
|
title: "Product 2 title",
|
||||||
|
deep: {
|
||||||
|
a: 1,
|
||||||
|
obj: {
|
||||||
|
b: 15,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const { data } = await module.query({
|
||||||
|
fields: ["product.*", "variants.*"],
|
||||||
|
filters: {
|
||||||
|
product: {
|
||||||
|
variants: {
|
||||||
|
sku: {
|
||||||
|
$nin: ["sku 123"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
$eq: "Product 2 title",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(data).toEqual(expected)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should query products filtering title like and not equal specific value", async () => {
|
||||||
|
const expected = [
|
||||||
|
{
|
||||||
|
id: "prod_2",
|
||||||
|
title: "Product 2 title",
|
||||||
|
deep: {
|
||||||
|
a: 1,
|
||||||
|
obj: {
|
||||||
|
b: 15,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const { data } = await module.query({
|
||||||
|
fields: ["product.*"],
|
||||||
|
filters: {
|
||||||
|
product: {
|
||||||
|
$and: [
|
||||||
|
{
|
||||||
|
title: {
|
||||||
|
$like: "Product%",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$not: {
|
||||||
|
title: {
|
||||||
|
$eq: "Product 1",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(data).toEqual(expected)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should query products filtering title using $ilike", async () => {
|
||||||
|
const expected = [
|
||||||
|
{
|
||||||
|
id: "prod_2",
|
||||||
|
title: "Product 2 title",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const { data } = await module.query({
|
||||||
|
fields: ["product.id", "product.title"],
|
||||||
|
filters: {
|
||||||
|
product: {
|
||||||
|
title: {
|
||||||
|
$ilike: "PROdUCt 2%",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(data).toEqual(expected)
|
||||||
|
|
||||||
|
const { data: sensitive } = await module.query({
|
||||||
|
fields: ["product.id", "product.title"],
|
||||||
|
filters: {
|
||||||
|
product: {
|
||||||
|
title: {
|
||||||
|
$like: "PROdUCt 2%",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(sensitive).toEqual([])
|
||||||
|
})
|
||||||
})
|
})
|
||||||
@@ -11,6 +11,7 @@ import { getPivotTableName, normalizeTableName } from "./normalze-table-name"
|
|||||||
|
|
||||||
const AND_OPERATOR = "$and"
|
const AND_OPERATOR = "$and"
|
||||||
const OR_OPERATOR = "$or"
|
const OR_OPERATOR = "$or"
|
||||||
|
const NOT_OPERATOR = "$not"
|
||||||
|
|
||||||
function escapeJsonPathString(val: string): string {
|
function escapeJsonPathString(val: string): string {
|
||||||
// Escape for JSONPath string
|
// Escape for JSONPath string
|
||||||
@@ -23,10 +24,15 @@ function buildSafeJsonPathQuery(
|
|||||||
value: any
|
value: any
|
||||||
): string {
|
): string {
|
||||||
let jsonPathOperator = operator
|
let jsonPathOperator = operator
|
||||||
|
let caseInsensitiveFlag = ""
|
||||||
if (operator === "=") {
|
if (operator === "=") {
|
||||||
jsonPathOperator = "=="
|
jsonPathOperator = "=="
|
||||||
} else if (operator.toUpperCase().includes("LIKE")) {
|
} else if (operator.toUpperCase().includes("LIKE")) {
|
||||||
jsonPathOperator = "like_regex"
|
jsonPathOperator = "like_regex"
|
||||||
|
|
||||||
|
if (operator.toUpperCase() === "ILIKE") {
|
||||||
|
caseInsensitiveFlag = ' flag "i"'
|
||||||
|
}
|
||||||
} else if (operator === "IS") {
|
} else if (operator === "IS") {
|
||||||
jsonPathOperator = "=="
|
jsonPathOperator = "=="
|
||||||
} else if (operator === "IS NOT") {
|
} else if (operator === "IS NOT") {
|
||||||
@@ -46,7 +52,7 @@ function buildSafeJsonPathQuery(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return `$.${field} ${jsonPathOperator} ${value}`
|
return `$.${field} ${jsonPathOperator} ${value}${caseInsensitiveFlag}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export const OPERATOR_MAP = {
|
export const OPERATOR_MAP = {
|
||||||
@@ -57,6 +63,7 @@ export const OPERATOR_MAP = {
|
|||||||
$gte: ">=",
|
$gte: ">=",
|
||||||
$ne: "!=",
|
$ne: "!=",
|
||||||
$in: "IN",
|
$in: "IN",
|
||||||
|
$nin: "NOT IN",
|
||||||
$is: "IS",
|
$is: "IS",
|
||||||
$like: "LIKE",
|
$like: "LIKE",
|
||||||
$ilike: "ILIKE",
|
$ilike: "ILIKE",
|
||||||
@@ -104,6 +111,10 @@ export class QueryBuilder {
|
|||||||
this.idsOnly = args.idsOnly ?? false
|
this.idsOnly = args.idsOnly ?? false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private isLogicalOperator(key: string) {
|
||||||
|
return key === AND_OPERATOR || key === OR_OPERATOR || key === NOT_OPERATOR
|
||||||
|
}
|
||||||
|
|
||||||
private getStructureKeys(structure) {
|
private getStructureKeys(structure) {
|
||||||
const collectKeys = (obj: any, keys = new Set<string>()) => {
|
const collectKeys = (obj: any, keys = new Set<string>()) => {
|
||||||
if (!isObject(obj)) {
|
if (!isObject(obj)) {
|
||||||
@@ -111,7 +122,7 @@ export class QueryBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Object.keys(obj).forEach((key) => {
|
Object.keys(obj).forEach((key) => {
|
||||||
if (key === AND_OPERATOR || key === OR_OPERATOR) {
|
if (this.isLogicalOperator(key)) {
|
||||||
if (Array.isArray(obj[key])) {
|
if (Array.isArray(obj[key])) {
|
||||||
obj[key].forEach((item) => collectKeys(item, keys))
|
obj[key].forEach((item) => collectKeys(item, keys))
|
||||||
}
|
}
|
||||||
@@ -144,7 +155,7 @@ export class QueryBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getGraphQLType(path, field) {
|
private getGraphQLType(path, field) {
|
||||||
if (field === AND_OPERATOR || field === OR_OPERATOR) {
|
if (this.isLogicalOperator(field)) {
|
||||||
return "JSON"
|
return "JSON"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,12 +280,11 @@ export class QueryBuilder {
|
|||||||
|
|
||||||
keys.forEach((key) => {
|
keys.forEach((key) => {
|
||||||
const pathAsArray = (parentPath ? `${parentPath}.${key}` : key).split(".")
|
const pathAsArray = (parentPath ? `${parentPath}.${key}` : key).split(".")
|
||||||
const fieldOrLogicalOperator = pathAsArray.pop()
|
const fieldOrLogicalOperator = pathAsArray.pop()!
|
||||||
let value = obj[key]
|
let value = obj[key]
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(fieldOrLogicalOperator === AND_OPERATOR ||
|
this.isLogicalOperator(fieldOrLogicalOperator) &&
|
||||||
fieldOrLogicalOperator === OR_OPERATOR) &&
|
|
||||||
!Array.isArray(value)
|
!Array.isArray(value)
|
||||||
) {
|
) {
|
||||||
value = [value]
|
value = [value]
|
||||||
@@ -309,11 +319,35 @@ export class QueryBuilder {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
} else if (
|
||||||
|
fieldOrLogicalOperator === NOT_OPERATOR &&
|
||||||
|
(Array.isArray(value) || isObject(value))
|
||||||
|
) {
|
||||||
|
builder.whereNot((qb) => {
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
value.forEach((cond) => {
|
||||||
|
qb.andWhere((subBuilder) =>
|
||||||
|
this.parseWhere(
|
||||||
|
aliasMapping,
|
||||||
|
cond,
|
||||||
|
subBuilder,
|
||||||
|
pathAsArray.join(".")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.parseWhere(
|
||||||
|
aliasMapping,
|
||||||
|
value as any,
|
||||||
|
qb,
|
||||||
|
pathAsArray.join(".")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
} else if (
|
} else if (
|
||||||
isObject(value) &&
|
isObject(value) &&
|
||||||
!Array.isArray(value) &&
|
!Array.isArray(value) &&
|
||||||
fieldOrLogicalOperator !== AND_OPERATOR &&
|
!this.isLogicalOperator(fieldOrLogicalOperator)
|
||||||
fieldOrLogicalOperator !== OR_OPERATOR
|
|
||||||
) {
|
) {
|
||||||
const currentPath = parentPath ? `${parentPath}.${key}` : key
|
const currentPath = parentPath ? `${parentPath}.${key}` : key
|
||||||
|
|
||||||
@@ -335,7 +369,10 @@ export class QueryBuilder {
|
|||||||
value[subKey]
|
value[subKey]
|
||||||
)
|
)
|
||||||
|
|
||||||
let val = operator === "IN" ? subValue : [subValue]
|
let val =
|
||||||
|
operator === "IN" || operator === "NOT IN"
|
||||||
|
? subValue
|
||||||
|
: [subValue]
|
||||||
if (operator === "=" && subValue === null) {
|
if (operator === "=" && subValue === null) {
|
||||||
operator = "IS"
|
operator = "IS"
|
||||||
} else if (operator === "!=" && subValue === null) {
|
} else if (operator === "!=" && subValue === null) {
|
||||||
@@ -355,7 +392,7 @@ export class QueryBuilder {
|
|||||||
)}'::jsonb`
|
)}'::jsonb`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else if (operator === "IN") {
|
} else if (operator === "IN" || operator === "NOT IN") {
|
||||||
if (val && !Array.isArray(val)) {
|
if (val && !Array.isArray(val)) {
|
||||||
val = [val]
|
val = [val]
|
||||||
}
|
}
|
||||||
@@ -365,9 +402,12 @@ export class QueryBuilder {
|
|||||||
|
|
||||||
const inPlaceholders = val.map(() => "?").join(",")
|
const inPlaceholders = val.map(() => "?").join(",")
|
||||||
const hasId = field[field.length - 1] === "id"
|
const hasId = field[field.length - 1] === "id"
|
||||||
|
const isNegated = operator === "NOT IN"
|
||||||
if (hasId) {
|
if (hasId) {
|
||||||
builder.whereRaw(
|
builder.whereRaw(
|
||||||
`${aliasMapping[attr]}.id IN (${inPlaceholders})`,
|
`${aliasMapping[attr]}.id ${
|
||||||
|
isNegated ? "NOT IN" : "IN"
|
||||||
|
} (${inPlaceholders})`,
|
||||||
val
|
val
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@@ -379,11 +419,18 @@ export class QueryBuilder {
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (isNegated) {
|
||||||
|
builder.whereRaw(
|
||||||
|
`NOT EXISTS (SELECT 1 FROM unnest(ARRAY[${inPlaceholders}]::JSONB[]) AS v(val) WHERE ${aliasMapping[attr]}.data${nested} @> v.val)`,
|
||||||
|
jsonbValues
|
||||||
|
)
|
||||||
|
} else {
|
||||||
builder.whereRaw(
|
builder.whereRaw(
|
||||||
`${aliasMapping[attr]}.data${nested} @> ANY(ARRAY[${inPlaceholders}]::JSONB[])`,
|
`${aliasMapping[attr]}.data${nested} @> ANY(ARRAY[${inPlaceholders}]::JSONB[])`,
|
||||||
jsonbValues
|
jsonbValues
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const potentialIdFields = field[field.length - 1]
|
const potentialIdFields = field[field.length - 1]
|
||||||
const hasId = potentialIdFields === "id"
|
const hasId = potentialIdFields === "id"
|
||||||
|
|||||||
Reference in new issue
Block a user