fix(index): logical operators (#13137)
This commit is contained in:
committed by
GitHub
parent
a52708769d
commit
9725bff25d
6
.changeset/eleven-apricots-look.md
Normal file
6
.changeset/eleven-apricots-look.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/index": patch
|
||||||
|
"@medusajs/link-modules": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix(index): handle $and and $or operators
|
||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
ContainerRegistrationKeys,
|
ContainerRegistrationKeys,
|
||||||
defaultCurrencies,
|
defaultCurrencies,
|
||||||
defineLink,
|
defineLink,
|
||||||
|
Modules,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import { setTimeout } from "timers/promises"
|
import { setTimeout } from "timers/promises"
|
||||||
import {
|
import {
|
||||||
@@ -35,6 +36,7 @@ async function populateData(api: any) {
|
|||||||
origin_country: "USA",
|
origin_country: "USA",
|
||||||
shipping_profile_id: shippingProfile.id,
|
shipping_profile_id: shippingProfile.id,
|
||||||
options: [{ title: "Denominations", values: ["100"] }],
|
options: [{ title: "Denominations", values: ["100"] }],
|
||||||
|
material: "test-material",
|
||||||
variants: [
|
variants: [
|
||||||
{
|
{
|
||||||
title: `Test variant 1`,
|
title: `Test variant 1`,
|
||||||
@@ -61,6 +63,7 @@ async function populateData(api: any) {
|
|||||||
status: "published",
|
status: "published",
|
||||||
shipping_profile_id: shippingProfile.id,
|
shipping_profile_id: shippingProfile.id,
|
||||||
options: [{ title: "Colors", values: ["Red"] }],
|
options: [{ title: "Colors", values: ["Red"] }],
|
||||||
|
material: "extra-material",
|
||||||
variants: new Array(2).fill(0).map((_, i) => ({
|
variants: new Array(2).fill(0).map((_, i) => ({
|
||||||
title: `extra variant ${i}`,
|
title: `extra variant ${i}`,
|
||||||
sku: `extra-variant-${i}`,
|
sku: `extra-variant-${i}`,
|
||||||
@@ -81,9 +84,16 @@ async function populateData(api: any) {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
await api.post("/admin/products/batch", { create: payload }, adminHeaders)
|
const response = await api.post(
|
||||||
|
"/admin/products/batch",
|
||||||
|
{ create: payload },
|
||||||
|
adminHeaders
|
||||||
|
)
|
||||||
|
const products = response.data.created
|
||||||
|
|
||||||
await setTimeout(4000)
|
await setTimeout(4000)
|
||||||
|
|
||||||
|
return products
|
||||||
}
|
}
|
||||||
|
|
||||||
process.env.ENABLE_INDEX_MODULE = "true"
|
process.env.ENABLE_INDEX_MODULE = "true"
|
||||||
@@ -117,7 +127,22 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("should use query.index to query the index module and hydrate the data", async () => {
|
it("should use query.index to query the index module and hydrate the data", async () => {
|
||||||
await populateData(api)
|
const products = await populateData(api)
|
||||||
|
|
||||||
|
const brandModule = appContainer.resolve("brand")
|
||||||
|
const link = appContainer.resolve(ContainerRegistrationKeys.LINK)
|
||||||
|
const brand = await brandModule.createBrands({
|
||||||
|
name: "Medusa Brand",
|
||||||
|
})
|
||||||
|
|
||||||
|
await link.create({
|
||||||
|
[Modules.PRODUCT]: {
|
||||||
|
product_id: products.find((p) => p.title === "Extra product").id,
|
||||||
|
},
|
||||||
|
brand: {
|
||||||
|
brand_id: brand.id,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
const query = appContainer.resolve(
|
const query = appContainer.resolve(
|
||||||
ContainerRegistrationKeys.QUERY
|
ContainerRegistrationKeys.QUERY
|
||||||
@@ -132,6 +157,8 @@ medusaIntegrationTestRunner({
|
|||||||
"description",
|
"description",
|
||||||
"status",
|
"status",
|
||||||
"title",
|
"title",
|
||||||
|
"brand.name",
|
||||||
|
"brand.id",
|
||||||
"variants.sku",
|
"variants.sku",
|
||||||
"variants.barcode",
|
"variants.barcode",
|
||||||
"variants.material",
|
"variants.material",
|
||||||
@@ -142,8 +169,28 @@ medusaIntegrationTestRunner({
|
|||||||
"variants.inventory_items.inventory.description",
|
"variants.inventory_items.inventory.description",
|
||||||
],
|
],
|
||||||
filters: {
|
filters: {
|
||||||
"variants.sku": { $like: "%-1" },
|
$and: [
|
||||||
"variants.prices.amount": { $gt: 30 },
|
{ status: "published" },
|
||||||
|
{ material: { $ilike: "%material%" } },
|
||||||
|
{
|
||||||
|
$or: [
|
||||||
|
{
|
||||||
|
brand: {
|
||||||
|
name: { $ilike: "%brand" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ title: { $ilike: "%duct%" } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
$and: [
|
||||||
|
{ sku: { $like: "%-1" } },
|
||||||
|
{ "prices.amount": { $gt: 30 } },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
pagination: {
|
pagination: {
|
||||||
take: 10,
|
take: 10,
|
||||||
@@ -171,6 +218,10 @@ medusaIntegrationTestRunner({
|
|||||||
description: "extra description",
|
description: "extra description",
|
||||||
title: "Extra product",
|
title: "Extra product",
|
||||||
status: "published",
|
status: "published",
|
||||||
|
brand: {
|
||||||
|
id: expect.any(String),
|
||||||
|
name: "Medusa Brand",
|
||||||
|
},
|
||||||
variants: [
|
variants: [
|
||||||
{
|
{
|
||||||
sku: "extra-variant-0",
|
sku: "extra-variant-0",
|
||||||
@@ -247,6 +298,7 @@ medusaIntegrationTestRunner({
|
|||||||
description: "test-product-description",
|
description: "test-product-description",
|
||||||
title: "Test Product",
|
title: "Test Product",
|
||||||
status: "published",
|
status: "published",
|
||||||
|
brand: undefined,
|
||||||
variants: [
|
variants: [
|
||||||
{
|
{
|
||||||
sku: "test-variant-1",
|
sku: "test-variant-1",
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { defineConfig } from "@medusajs/utils"
|
||||||
|
|
||||||
const { Modules } = require("@medusajs/utils")
|
const { Modules } = require("@medusajs/utils")
|
||||||
|
|
||||||
const DB_HOST = process.env.DB_HOST
|
const DB_HOST = process.env.DB_HOST
|
||||||
@@ -35,7 +37,7 @@ const customFulfillmentProviderCalculated = {
|
|||||||
id: "test-provider-calculated",
|
id: "test-provider-calculated",
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = defineConfig({
|
||||||
admin: {
|
admin: {
|
||||||
disable: true,
|
disable: true,
|
||||||
},
|
},
|
||||||
@@ -51,11 +53,13 @@ module.exports = {
|
|||||||
featureFlags: {
|
featureFlags: {
|
||||||
medusa_v2: enableMedusaV2,
|
medusa_v2: enableMedusaV2,
|
||||||
},
|
},
|
||||||
modules: {
|
modules: [
|
||||||
testingModule: {
|
{
|
||||||
|
key: "testingModule",
|
||||||
resolve: "__tests__/__fixtures__/testing-module",
|
resolve: "__tests__/__fixtures__/testing-module",
|
||||||
},
|
},
|
||||||
[Modules.AUTH]: {
|
{
|
||||||
|
key: "auth",
|
||||||
resolve: "@medusajs/auth",
|
resolve: "@medusajs/auth",
|
||||||
options: {
|
options: {
|
||||||
providers: [
|
providers: [
|
||||||
@@ -66,53 +70,98 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[Modules.USER]: {
|
{
|
||||||
|
key: Modules.USER,
|
||||||
scope: "internal",
|
scope: "internal",
|
||||||
resolve: "@medusajs/user",
|
resolve: "@medusajs/user",
|
||||||
options: {
|
options: {
|
||||||
jwt_secret: "test",
|
jwt_secret: "test",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[Modules.CACHE]: {
|
{
|
||||||
|
key: Modules.CACHE,
|
||||||
resolve: "@medusajs/cache-inmemory",
|
resolve: "@medusajs/cache-inmemory",
|
||||||
options: { ttl: 0 }, // Cache disabled
|
options: { ttl: 0 }, // Cache disabled
|
||||||
},
|
},
|
||||||
[Modules.LOCKING]: true,
|
{
|
||||||
[Modules.STOCK_LOCATION]: {
|
key: Modules.LOCKING,
|
||||||
|
resolve: "@medusajs/locking",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: Modules.STOCK_LOCATION,
|
||||||
resolve: "@medusajs/stock-location",
|
resolve: "@medusajs/stock-location",
|
||||||
options: {},
|
options: {},
|
||||||
},
|
},
|
||||||
[Modules.INVENTORY]: {
|
{
|
||||||
|
key: Modules.INVENTORY,
|
||||||
resolve: "@medusajs/inventory",
|
resolve: "@medusajs/inventory",
|
||||||
options: {},
|
options: {},
|
||||||
},
|
},
|
||||||
[Modules.PRODUCT]: true,
|
{
|
||||||
[Modules.PRICING]: true,
|
key: Modules.PRODUCT,
|
||||||
[Modules.PROMOTION]: true,
|
resolve: "@medusajs/product",
|
||||||
[Modules.REGION]: true,
|
},
|
||||||
[Modules.CUSTOMER]: true,
|
{
|
||||||
[Modules.SALES_CHANNEL]: true,
|
key: Modules.PRICING,
|
||||||
[Modules.CART]: true,
|
resolve: "@medusajs/pricing",
|
||||||
[Modules.WORKFLOW_ENGINE]: true,
|
},
|
||||||
[Modules.API_KEY]: true,
|
{
|
||||||
[Modules.STORE]: true,
|
key: Modules.PROMOTION,
|
||||||
[Modules.TAX]: {
|
resolve: "@medusajs/promotion",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: Modules.REGION,
|
||||||
|
resolve: "@medusajs/region",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: Modules.CUSTOMER,
|
||||||
|
resolve: "@medusajs/customer",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: Modules.SALES_CHANNEL,
|
||||||
|
resolve: "@medusajs/sales-channel",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: Modules.CART,
|
||||||
|
resolve: "@medusajs/cart",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: Modules.WORKFLOW_ENGINE,
|
||||||
|
resolve: "@medusajs/workflow-engine-inmemory",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: Modules.API_KEY,
|
||||||
|
resolve: "@medusajs/api-key",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: Modules.STORE,
|
||||||
|
resolve: "@medusajs/store",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: Modules.TAX,
|
||||||
resolve: "@medusajs/tax",
|
resolve: "@medusajs/tax",
|
||||||
options: {
|
options: {
|
||||||
providers: [customTaxProviderRegistration],
|
providers: [customTaxProviderRegistration],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[Modules.CURRENCY]: true,
|
{
|
||||||
[Modules.ORDER]: true,
|
key: Modules.CURRENCY,
|
||||||
[Modules.PAYMENT]: {
|
resolve: "@medusajs/currency",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: Modules.ORDER,
|
||||||
|
resolve: "@medusajs/order",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: Modules.PAYMENT,
|
||||||
resolve: "@medusajs/payment",
|
resolve: "@medusajs/payment",
|
||||||
/** @type {import('@medusajs/payment').PaymentModuleOptions}*/
|
|
||||||
options: {
|
options: {
|
||||||
providers: [customPaymentProvider],
|
providers: [customPaymentProvider],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[Modules.FULFILLMENT]: {
|
{
|
||||||
/** @type {import('@medusajs/fulfillment').FulfillmentModuleOptions} */
|
key: Modules.FULFILLMENT,
|
||||||
|
resolve: "@medusajs/fulfillment",
|
||||||
options: {
|
options: {
|
||||||
providers: [
|
providers: [
|
||||||
customFulfillmentProvider,
|
customFulfillmentProvider,
|
||||||
@@ -120,8 +169,8 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[Modules.NOTIFICATION]: {
|
{
|
||||||
/** @type {import('@medusajs/types').LocalNotificationServiceOptions} */
|
key: Modules.NOTIFICATION,
|
||||||
options: {
|
options: {
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
@@ -135,10 +184,15 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[Modules.INDEX]: process.env.ENABLE_INDEX_MODULE
|
{
|
||||||
? {
|
key: Modules.INDEX,
|
||||||
resolve: "@medusajs/index",
|
resolve: "@medusajs/index",
|
||||||
}
|
disable: process.env.ENABLE_INDEX_MODULE !== "true",
|
||||||
: false,
|
|
||||||
},
|
},
|
||||||
}
|
{
|
||||||
|
key: "brand",
|
||||||
|
resolve: "src/modules/brand",
|
||||||
|
disable: process.env.ENABLE_INDEX_MODULE !== "true",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
21
integration-tests/modules/src/links/product-brand.ts
Normal file
21
integration-tests/modules/src/links/product-brand.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import ProductModule from "@medusajs/medusa/product"
|
||||||
|
import { defineLink } from "@medusajs/utils"
|
||||||
|
import BrandModule from "../modules/brand"
|
||||||
|
|
||||||
|
const link =
|
||||||
|
process.env.ENABLE_INDEX_MODULE === "true"
|
||||||
|
? defineLink(
|
||||||
|
{
|
||||||
|
linkable: ProductModule.linkable.product.id,
|
||||||
|
filterable: ["description", "material"],
|
||||||
|
isList: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
linkable: BrandModule.linkable.brand.id,
|
||||||
|
filterable: ["id", "name"],
|
||||||
|
isList: false,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
: {}
|
||||||
|
|
||||||
|
export default link
|
||||||
8
integration-tests/modules/src/modules/brand/index.ts
Normal file
8
integration-tests/modules/src/modules/brand/index.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { Module } from "@medusajs/utils"
|
||||||
|
import { BrandModuleService } from "./service"
|
||||||
|
|
||||||
|
export const BRAND_MODULE = "brand"
|
||||||
|
|
||||||
|
export default Module(BRAND_MODULE, {
|
||||||
|
service: BrandModuleService,
|
||||||
|
})
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { Migration } from "@mikro-orm/migrations"
|
||||||
|
|
||||||
|
export class Migration20250805184935 extends Migration {
|
||||||
|
override async up(): Promise<void> {
|
||||||
|
this.addSql(
|
||||||
|
`create table if not exists "brand" ("id" text not null, "name" text not null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "brand_pkey" primary key ("id"));`
|
||||||
|
)
|
||||||
|
this.addSql(
|
||||||
|
`CREATE INDEX IF NOT EXISTS "IDX_brand_deleted_at" ON "brand" (deleted_at) WHERE deleted_at IS NULL;`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override async down(): Promise<void> {
|
||||||
|
this.addSql(`drop table if exists "brand" cascade;`)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { model } from "@medusajs/utils"
|
||||||
|
|
||||||
|
export const Brand = model.define("brand", {
|
||||||
|
id: model.id({ prefix: "brand" }).primaryKey(),
|
||||||
|
name: model.text(),
|
||||||
|
})
|
||||||
6
integration-tests/modules/src/modules/brand/service.ts
Normal file
6
integration-tests/modules/src/modules/brand/service.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import { MedusaService } from "@medusajs/utils"
|
||||||
|
import { Brand } from "./models/brand"
|
||||||
|
|
||||||
|
export class BrandModuleService extends MedusaService({
|
||||||
|
Brand,
|
||||||
|
}) {}
|
||||||
@@ -1206,7 +1206,12 @@ function buildSchemaFromFilterableLinks(
|
|||||||
})
|
})
|
||||||
.join("\n")
|
.join("\n")
|
||||||
|
|
||||||
return `extend type ${entity} ${events} {
|
return `
|
||||||
|
type ${entity} ${events} {
|
||||||
|
id: ID!
|
||||||
|
}
|
||||||
|
|
||||||
|
extend type ${entity} {
|
||||||
${fieldDefinitions}
|
${fieldDefinitions}
|
||||||
}`
|
}`
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ import { Knex } from "@mikro-orm/knex"
|
|||||||
import { OrderBy, QueryFormat, QueryOptions, Select } from "@types"
|
import { OrderBy, QueryFormat, QueryOptions, Select } from "@types"
|
||||||
import { getPivotTableName, normalizeTableName } from "./normalze-table-name"
|
import { getPivotTableName, normalizeTableName } from "./normalze-table-name"
|
||||||
|
|
||||||
|
const AND_OPERATOR = "$and"
|
||||||
|
const OR_OPERATOR = "$or"
|
||||||
|
|
||||||
function escapeJsonPathString(val: string): string {
|
function escapeJsonPathString(val: string): string {
|
||||||
// Escape for JSONPath string
|
// Escape for JSONPath string
|
||||||
return val.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/'/g, "\\'")
|
return val.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/'/g, "\\'")
|
||||||
@@ -102,7 +105,25 @@ export class QueryBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getStructureKeys(structure) {
|
private getStructureKeys(structure) {
|
||||||
return Object.keys(structure ?? {}).filter((key) => key !== "entity")
|
const collectKeys = (obj: any, keys = new Set<string>()) => {
|
||||||
|
if (!isObject(obj)) {
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.keys(obj).forEach((key) => {
|
||||||
|
if (key === AND_OPERATOR || key === OR_OPERATOR) {
|
||||||
|
if (Array.isArray(obj[key])) {
|
||||||
|
obj[key].forEach((item) => collectKeys(item, keys))
|
||||||
|
}
|
||||||
|
} else if (key !== "entity") {
|
||||||
|
keys.add(key)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return keys
|
||||||
|
}
|
||||||
|
|
||||||
|
return [...collectKeys(structure ?? {})]
|
||||||
}
|
}
|
||||||
|
|
||||||
private getEntity(
|
private getEntity(
|
||||||
@@ -123,6 +144,10 @@ export class QueryBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private getGraphQLType(path, field) {
|
private getGraphQLType(path, field) {
|
||||||
|
if (field === AND_OPERATOR || field === OR_OPERATOR) {
|
||||||
|
return "JSON"
|
||||||
|
}
|
||||||
|
|
||||||
const entity = this.getEntity(path)?.ref?.entity!
|
const entity = this.getEntity(path)?.ref?.entity!
|
||||||
const fieldRef = this.entityMap[entity]._fields[field]
|
const fieldRef = this.entityMap[entity]._fields[field]
|
||||||
|
|
||||||
@@ -209,12 +234,14 @@ export class QueryBuilder {
|
|||||||
private parseWhere(
|
private parseWhere(
|
||||||
aliasMapping: { [path: string]: string },
|
aliasMapping: { [path: string]: string },
|
||||||
obj: object,
|
obj: object,
|
||||||
builder: Knex.QueryBuilder
|
builder: Knex.QueryBuilder,
|
||||||
|
parentPath: string = ""
|
||||||
) {
|
) {
|
||||||
const keys = Object.keys(obj)
|
const keys = Object.keys(obj)
|
||||||
|
|
||||||
const getPathAndField = (key: string) => {
|
const getPathAndField = (key: string) => {
|
||||||
const path = key.split(".")
|
const fullKey = parentPath ? `${parentPath}.${key}` : key
|
||||||
|
const path = fullKey.split(".")
|
||||||
const field = [path.pop()]
|
const field = [path.pop()]
|
||||||
|
|
||||||
while (!aliasMapping[path.join(".")] && path.length > 0) {
|
while (!aliasMapping[path.join(".")] && path.length > 0) {
|
||||||
@@ -241,34 +268,65 @@ export class QueryBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
keys.forEach((key) => {
|
keys.forEach((key) => {
|
||||||
|
const pathAsArray = (parentPath ? `${parentPath}.${key}` : key).split(".")
|
||||||
|
const fieldOrLogicalOperator = pathAsArray.pop()
|
||||||
let value = obj[key]
|
let value = obj[key]
|
||||||
|
|
||||||
if ((key === "$and" || key === "$or") && !Array.isArray(value)) {
|
if (
|
||||||
|
(fieldOrLogicalOperator === AND_OPERATOR ||
|
||||||
|
fieldOrLogicalOperator === OR_OPERATOR) &&
|
||||||
|
!Array.isArray(value)
|
||||||
|
) {
|
||||||
value = [value]
|
value = [value]
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key === "$and" && Array.isArray(value)) {
|
if (fieldOrLogicalOperator === AND_OPERATOR && Array.isArray(value)) {
|
||||||
builder.where((qb) => {
|
builder.where((qb) => {
|
||||||
value.forEach((cond) => {
|
value.forEach((cond) => {
|
||||||
qb.andWhere((subBuilder) =>
|
qb.andWhere((subBuilder) =>
|
||||||
this.parseWhere(aliasMapping, cond, subBuilder)
|
this.parseWhere(
|
||||||
|
aliasMapping,
|
||||||
|
cond,
|
||||||
|
subBuilder,
|
||||||
|
pathAsArray.join(".")
|
||||||
|
)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
} else if (key === "$or" && Array.isArray(value)) {
|
} else if (
|
||||||
|
fieldOrLogicalOperator === OR_OPERATOR &&
|
||||||
|
Array.isArray(value)
|
||||||
|
) {
|
||||||
builder.where((qb) => {
|
builder.where((qb) => {
|
||||||
value.forEach((cond) => {
|
value.forEach((cond) => {
|
||||||
qb.orWhere((subBuilder) =>
|
qb.orWhere((subBuilder) =>
|
||||||
this.parseWhere(aliasMapping, cond, subBuilder)
|
this.parseWhere(
|
||||||
|
aliasMapping,
|
||||||
|
cond,
|
||||||
|
subBuilder,
|
||||||
|
pathAsArray.join(".")
|
||||||
|
)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
} else if (isObject(value) && !Array.isArray(value)) {
|
} else if (
|
||||||
|
isObject(value) &&
|
||||||
|
!Array.isArray(value) &&
|
||||||
|
fieldOrLogicalOperator !== AND_OPERATOR &&
|
||||||
|
fieldOrLogicalOperator !== OR_OPERATOR
|
||||||
|
) {
|
||||||
|
const currentPath = parentPath ? `${parentPath}.${key}` : key
|
||||||
|
|
||||||
|
const subKeys = Object.keys(value)
|
||||||
|
const hasOperators = subKeys.some((subKey) => OPERATOR_MAP[subKey])
|
||||||
|
|
||||||
|
if (hasOperators) {
|
||||||
|
const { field, attr } = getPathAndField(key)
|
||||||
|
|
||||||
const subKeys = Object.keys(value)
|
const subKeys = Object.keys(value)
|
||||||
subKeys.forEach((subKey) => {
|
subKeys.forEach((subKey) => {
|
||||||
let operator = OPERATOR_MAP[subKey]
|
let operator = OPERATOR_MAP[subKey]
|
||||||
if (operator) {
|
if (operator) {
|
||||||
const { field, attr } = getPathAndField(key)
|
|
||||||
const nested = new Array(field.length).join("->?")
|
const nested = new Array(field.length).join("->?")
|
||||||
|
|
||||||
const subValue = this.transformValueToType(
|
const subValue = this.transformValueToType(
|
||||||
@@ -329,6 +387,7 @@ export class QueryBuilder {
|
|||||||
} else {
|
} else {
|
||||||
const potentialIdFields = field[field.length - 1]
|
const potentialIdFields = field[field.length - 1]
|
||||||
const hasId = potentialIdFields === "id"
|
const hasId = potentialIdFields === "id"
|
||||||
|
|
||||||
if (hasId) {
|
if (hasId) {
|
||||||
builder.whereRaw(`(${aliasMapping[attr]}.id) ${operator} ?`, [
|
builder.whereRaw(`(${aliasMapping[attr]}.id) ${operator} ?`, [
|
||||||
...val,
|
...val,
|
||||||
@@ -341,6 +400,7 @@ export class QueryBuilder {
|
|||||||
operator,
|
operator,
|
||||||
val[0]
|
val[0]
|
||||||
)
|
)
|
||||||
|
|
||||||
builder.whereRaw(`${aliasMapping[attr]}.data${nested} @@ ?`, [
|
builder.whereRaw(`${aliasMapping[attr]}.data${nested} @@ ?`, [
|
||||||
jsonPath,
|
jsonPath,
|
||||||
])
|
])
|
||||||
@@ -350,6 +410,9 @@ export class QueryBuilder {
|
|||||||
throw new Error(`Unsupported operator: ${subKey}`)
|
throw new Error(`Unsupported operator: ${subKey}`)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
this.parseWhere(aliasMapping, value, builder, currentPath)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
const { field, attr } = getPathAndField(key)
|
const { field, attr } = getPathAndField(key)
|
||||||
const nested = new Array(field.length).join("->?")
|
const nested = new Array(field.length).join("->?")
|
||||||
@@ -667,7 +730,6 @@ export class QueryBuilder {
|
|||||||
selectParts[currentAliasPath + ".id"] = `${alias}.id`
|
selectParts[currentAliasPath + ".id"] = `${alias}.id`
|
||||||
|
|
||||||
const children = this.getStructureKeys(structure)
|
const children = this.getStructureKeys(structure)
|
||||||
|
|
||||||
for (const child of children) {
|
for (const child of children) {
|
||||||
const childStructure = structure[child] as Select
|
const childStructure = structure[child] as Select
|
||||||
|
|
||||||
@@ -859,6 +921,7 @@ export class QueryBuilder {
|
|||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
|
|
||||||
innerQueryBuilder.whereRaw(
|
innerQueryBuilder.whereRaw(
|
||||||
`(${searchWhereParts.join(" OR ")})`,
|
`(${searchWhereParts.join(" OR ")})`,
|
||||||
Array(searchWhereParts.length).fill(textSearchQuery)
|
Array(searchWhereParts.length).fill(textSearchQuery)
|
||||||
|
|||||||
@@ -224,6 +224,7 @@ export default class LinkModuleService implements ILinkModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@InjectTransactionManager()
|
@InjectTransactionManager()
|
||||||
|
@EmitEvents()
|
||||||
async dismiss(
|
async dismiss(
|
||||||
primaryKeyOrBulkData: string | string[] | [string | string[], string][],
|
primaryKeyOrBulkData: string | string[] | [string | string[], string][],
|
||||||
foreignKeyData?: string,
|
foreignKeyData?: string,
|
||||||
@@ -245,6 +246,16 @@ export default class LinkModuleService implements ILinkModule {
|
|||||||
|
|
||||||
const links = await this.linkService_.dismiss(data, sharedContext)
|
const links = await this.linkService_.dismiss(data, sharedContext)
|
||||||
|
|
||||||
|
moduleEventBuilderFactory({
|
||||||
|
action: CommonEvents.DETACHED,
|
||||||
|
object: this.entityName_,
|
||||||
|
source: this.serviceName_,
|
||||||
|
eventName: this.entityName_ + "." + CommonEvents.DETACHED,
|
||||||
|
})({
|
||||||
|
data: links.map((link) => link.id),
|
||||||
|
sharedContext,
|
||||||
|
})
|
||||||
|
|
||||||
return (await this.baseRepository_.serialize(links)) as unknown[]
|
return (await this.baseRepository_.serialize(links)) as unknown[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user