diff --git a/packages/inventory-next/src/models/inventory-item.ts b/packages/inventory-next/src/models/inventory-item.ts index 7912089ea3..31fcd3e8f3 100644 --- a/packages/inventory-next/src/models/inventory-item.ts +++ b/packages/inventory-next/src/models/inventory-item.ts @@ -1,21 +1,20 @@ import { BeforeCreate, - Cascade, Collection, Entity, Filter, Formula, - OnInit, - OnLoad, OneToMany, + OnInit, OptionalProps, PrimaryKey, Property, } from "@mikro-orm/core" import { - DALUtils, createPsqlIndexStatementHelper, + DALUtils, generateEntityId, + Searchable, } from "@medusajs/utils" import { DAL } from "@medusajs/types" @@ -64,6 +63,7 @@ export class InventoryItem { deleted_at: Date | null = null @InventoryItemSkuIndex.MikroORMIndex() + @Searchable() @Property({ columnType: "text", nullable: true }) sku: string | null = null @@ -94,9 +94,11 @@ export class InventoryItem { @Property({ columnType: "boolean" }) requires_shipping: boolean = true + @Searchable() @Property({ columnType: "text", nullable: true }) description: string | null = null + @Searchable() @Property({ columnType: "text", nullable: true }) title: string | null = null diff --git a/packages/inventory-next/src/repositories/index.ts b/packages/inventory-next/src/repositories/index.ts index 6992c162ee..dc31fdaa30 100644 --- a/packages/inventory-next/src/repositories/index.ts +++ b/packages/inventory-next/src/repositories/index.ts @@ -1,3 +1,2 @@ export * from "./inventory-level" -export * from "./inventory-item" export { MikroOrmBaseRepository as BaseRepository } from "@medusajs/utils" diff --git a/packages/inventory-next/src/repositories/inventory-item.ts b/packages/inventory-next/src/repositories/inventory-item.ts deleted file mode 100644 index 6384a5adcc..0000000000 --- a/packages/inventory-next/src/repositories/inventory-item.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { Context, DAL } from "@medusajs/types" -import { InventoryItem, InventoryLevel } from "@models" - -import { SqlEntityManager } from "@mikro-orm/postgresql" -import { mikroOrmBaseRepositoryFactory } from "@medusajs/utils" - -export class InventoryItemRepository extends mikroOrmBaseRepositoryFactory( - InventoryItem -) { - async find( - findOptions: DAL.FindOptions = { - where: {}, - }, - context: Context = {} - ): Promise { - const findOptions_ = { ...findOptions } - findOptions_.options ??= {} - - this.applyFreeTextSearchFilters( - findOptions_, - this.getFreeTextSearchConstraints - ) - - return await super.find(findOptions_, context) - } - - async findAndCount( - findOptions: DAL.FindOptions = { - where: {}, - }, - context: Context = {} - ): Promise<[InventoryItem[], number]> { - const findOptions_ = { ...findOptions } - findOptions_.options ??= {} - - this.applyFreeTextSearchFilters( - findOptions_, - this.getFreeTextSearchConstraints - ) - - return await super.findAndCount(findOptions_, context) - } - - protected getFreeTextSearchConstraints(q: string) { - return [ - { - description: { - $ilike: `%${q}%`, - }, - }, - { - title: { - $ilike: `%${q}%`, - }, - }, - { - sku: { - $ilike: `%${q}%`, - }, - }, - ] - } -} diff --git a/packages/product/integration-tests/__tests__/services/product/index.ts b/packages/product/integration-tests/__tests__/services/product/index.ts index 6878a3b01d..a29321fe2a 100644 --- a/packages/product/integration-tests/__tests__/services/product/index.ts +++ b/packages/product/integration-tests/__tests__/services/product/index.ts @@ -234,6 +234,31 @@ moduleIntegrationTestRunner({ }) describe("list", () => { + it("should list all product that match the free text search", async () => { + const data = buildProductOnlyData({ + title: "test product", + }) + const data2 = buildProductOnlyData({ + title: "space X", + }) + + const products = await service.create([data, data2]) + + const result = await service.list({ + q: "test", + }) + + expect(result).toHaveLength(1) + expect(result[0].title).toEqual("test product") + + const result2 = await service.list({ + q: "space", + }) + + expect(result2).toHaveLength(1) + expect(result2[0].title).toEqual("space X") + }) + describe("soft deleted", function () { let product diff --git a/packages/product/src/models/product-collection.ts b/packages/product/src/models/product-collection.ts index 41f965ad2e..3f71864eeb 100644 --- a/packages/product/src/models/product-collection.ts +++ b/packages/product/src/models/product-collection.ts @@ -4,17 +4,18 @@ import { Entity, Filter, Index, - OnInit, OneToMany, + OnInit, PrimaryKey, Property, } from "@mikro-orm/core" import { - DALUtils, createPsqlIndexStatementHelper, + DALUtils, generateEntityId, kebabCase, + Searchable, } from "@medusajs/utils" import Product from "./product" @@ -34,6 +35,7 @@ class ProductCollection { @PrimaryKey({ columnType: "text" }) id!: string + @Searchable() @Property({ columnType: "text" }) title: string diff --git a/packages/product/src/models/product-variant.ts b/packages/product/src/models/product-variant.ts index a1464bafd3..906eccc5be 100644 --- a/packages/product/src/models/product-variant.ts +++ b/packages/product/src/models/product-variant.ts @@ -1,8 +1,9 @@ import { - DALUtils, createPsqlIndexStatementHelper, + DALUtils, generateEntityId, optionalNumericSerializer, + Searchable, } from "@medusajs/utils" import { BeforeCreate, @@ -12,8 +13,8 @@ import { Filter, Index, ManyToOne, - OnInit, OneToMany, + OnInit, PrimaryKey, Property, } from "@mikro-orm/core" @@ -76,9 +77,11 @@ class ProductVariant { @PrimaryKey({ columnType: "text" }) id!: string + @Searchable() @Property({ columnType: "text" }) title: string + @Searchable() @Property({ columnType: "text", nullable: true }) sku?: string | null diff --git a/packages/product/src/models/product.ts b/packages/product/src/models/product.ts index 6afe4a36e6..917ee2de92 100644 --- a/packages/product/src/models/product.ts +++ b/packages/product/src/models/product.ts @@ -19,6 +19,7 @@ import { generateEntityId, kebabCase, ProductUtils, + Searchable, } from "@medusajs/utils" import ProductCategory from "./product-category" import ProductCollection from "./product-collection" @@ -64,6 +65,7 @@ class Product { @PrimaryKey({ columnType: "text" }) id!: string + @Searchable() @Property({ columnType: "text" }) title: string @@ -73,7 +75,11 @@ class Product { @Property({ columnType: "text", nullable: true }) subtitle?: string | null - @Property({ columnType: "text", nullable: true }) + @Searchable() + @Property({ + columnType: "text", + nullable: true, + }) description?: string | null @Property({ columnType: "boolean", default: false }) @@ -91,6 +97,7 @@ class Product { }) options = new Collection(this) + @Searchable() @OneToMany(() => ProductVariant, (variant) => variant.product, { cascade: ["soft-remove"] as any, }) @@ -120,6 +127,7 @@ class Product { @Property({ columnType: "text", nullable: true }) material?: string | null + @Searchable() @ManyToOne(() => ProductCollection, { columnType: "text", nullable: true, diff --git a/packages/product/src/repositories/product.ts b/packages/product/src/repositories/product.ts index a26b777455..2d1d25f9a9 100644 --- a/packages/product/src/repositories/product.ts +++ b/packages/product/src/repositories/product.ts @@ -15,40 +15,6 @@ export class ProductRepository extends DALUtils.mikroOrmBaseRepositoryFactory = { where: {} }, - context: Context = {} - ): Promise { - const findOptions_ = { ...findOptions } - findOptions_.options ??= {} - - await this.mutateNotInCategoriesConstraints(findOptions_) - - this.applyFreeTextSearchFilters( - findOptions_, - this.getFreeTextSearchConstraints - ) - - return await super.find(findOptions_, context) - } - - async findAndCount( - findOptions: DAL.FindOptions = { where: {} }, - context: Context = {} - ): Promise<[Product[], number]> { - const findOptions_ = { ...findOptions } - findOptions_.options ??= {} - - await this.mutateNotInCategoriesConstraints(findOptions_) - - this.applyFreeTextSearchFilters( - findOptions_, - this.getFreeTextSearchConstraints - ) - - return await super.findAndCount(findOptions_, context) - } - /** * In order to be able to have a strict not in categories, and prevent a product * to be return in the case it also belongs to other categories, we need to @@ -88,42 +54,4 @@ export class ProductRepository extends DALUtils.mikroOrmBaseRepositoryFactory( - StockLocation -) { - async find( - findOptions: DAL.FindOptions = { - where: {}, - }, - context: Context - ): Promise { - const findOptions_ = { ...findOptions } - findOptions_.options ??= {} - - this.applyFreeTextSearchFilters( - findOptions_, - this.getFreeTextSearchConstraints - ) - - return await super.find(findOptions_, context) - } - - async findAndCount( - findOptions: DAL.FindOptions = { - where: {}, - }, - context: Context - ): Promise<[StockLocation[], number]> { - const findOptions_ = { ...findOptions } - findOptions_.options ??= {} - - this.applyFreeTextSearchFilters( - findOptions_, - this.getFreeTextSearchConstraints - ) - - return await super.findAndCount(findOptions_, context) - } - - protected getFreeTextSearchConstraints(q: string) { - return [ - { - name: { - $ilike: `%${q}%`, - }, - }, - ] - } -} diff --git a/packages/utils/src/dal/index.ts b/packages/utils/src/dal/index.ts index d4f1b360df..30f303d1ab 100644 --- a/packages/utils/src/dal/index.ts +++ b/packages/utils/src/dal/index.ts @@ -1,8 +1,10 @@ export * from "./mikro-orm/big-number-field" export * from "./mikro-orm/mikro-orm-create-connection" +export * from "./mikro-orm/mikro-orm-free-text-search-filter" export * from "./mikro-orm/mikro-orm-repository" export * from "./mikro-orm/mikro-orm-soft-deletable-filter" export * from "./mikro-orm/mikro-orm-serializer" export * from "./mikro-orm/utils" +export * from "./mikro-orm/decorators/searchable" export * from "./repositories" export * from "./utils" diff --git a/packages/utils/src/dal/mikro-orm/__fixtures__/utils.ts b/packages/utils/src/dal/mikro-orm/__fixtures__/utils.ts index 339d2b68cd..e1d27de993 100644 --- a/packages/utils/src/dal/mikro-orm/__fixtures__/utils.ts +++ b/packages/utils/src/dal/mikro-orm/__fixtures__/utils.ts @@ -6,6 +6,7 @@ import { PrimaryKey, Property, } from "@mikro-orm/core" +import { Searchable } from "../decorators/searchable" // Circular dependency one level @Entity() @@ -279,6 +280,60 @@ class Entity2WithUnDecoratedProp { entity1: Entity1WithUnDecoratedProp } +// Searchable fields + +@Entity() +class SearchableEntity1 { + constructor(props: { id: string; deleted_at: Date | null }) { + this.id = props.id + this.deleted_at = props.deleted_at + } + + @PrimaryKey() + id: string + + @Property() + deleted_at: Date | null + + @Searchable() + @Property() + searchableField: string + + @Searchable() + @OneToMany(() => SearchableEntity2, (entity2) => entity2.entity1) + entity2 = new Collection(this) +} + +@Entity() +class SearchableEntity2 { + constructor(props: { + id: string + deleted_at: Date | null + entity1: SearchableEntity1 + }) { + this.id = props.id + this.deleted_at = props.deleted_at + this.entity1 = props.entity1 + this.entity1_id = props.entity1.id + } + + @PrimaryKey() + id: string + + @Property() + deleted_at: Date | null + + @Searchable() + @Property() + searchableField: string + + @ManyToOne(() => SearchableEntity1, { mapToPk: true }) + entity1_id: string + + @ManyToOne(() => SearchableEntity1, { persist: false }) + entity1: SearchableEntity1 +} + export { RecursiveEntity1, RecursiveEntity2, @@ -291,4 +346,6 @@ export { InternalCircularDependencyEntity1, Entity1WithUnDecoratedProp, Entity2WithUnDecoratedProp, + SearchableEntity1, + SearchableEntity2, } diff --git a/packages/utils/src/dal/mikro-orm/__tests__/mikro-orm-free-text-search-filter.spec.ts b/packages/utils/src/dal/mikro-orm/__tests__/mikro-orm-free-text-search-filter.spec.ts new file mode 100644 index 0000000000..d932d5cb7f --- /dev/null +++ b/packages/utils/src/dal/mikro-orm/__tests__/mikro-orm-free-text-search-filter.spec.ts @@ -0,0 +1,73 @@ +import { MikroORM } from "@mikro-orm/core" +import { SearchableEntity1, SearchableEntity2 } from "../__fixtures__/utils" +import { mikroOrmFreeTextSearchFilterOptionsFactory } from "../mikro-orm-free-text-search-filter" + +describe("mikroOrmFreeTextSearchFilterOptionsFactory", () => { + let orm + + beforeEach(async () => { + orm = await MikroORM.init({ + entities: [SearchableEntity1, SearchableEntity2], + dbName: "test", + type: "postgresql", + }) + }) + + it("should return a filter function that filters entities based on the free text search value", async () => { + const entityManager = orm.em.fork() + const freeTextSearchValue = "search" + + const models = [SearchableEntity1, SearchableEntity2] + + let filterConstraints = mikroOrmFreeTextSearchFilterOptionsFactory( + models + ).cond( + { + value: freeTextSearchValue, + fromEntity: SearchableEntity1.name, + }, + "read", + entityManager + ) + + expect(filterConstraints).toEqual({ + $or: [ + { + searchableField: { + $ilike: `%${freeTextSearchValue}%`, + }, + }, + { + entity2: { + $or: [ + { + searchableField: { + $ilike: `%${freeTextSearchValue}%`, + }, + }, + ], + }, + }, + ], + }) + + filterConstraints = mikroOrmFreeTextSearchFilterOptionsFactory(models).cond( + { + value: freeTextSearchValue, + fromEntity: SearchableEntity2.name, + }, + "read", + entityManager + ) + + expect(filterConstraints).toEqual({ + $or: [ + { + searchableField: { + $ilike: `%${freeTextSearchValue}%`, + }, + }, + ], + }) + }) +}) diff --git a/packages/utils/src/dal/mikro-orm/decorators/searchable.ts b/packages/utils/src/dal/mikro-orm/decorators/searchable.ts new file mode 100644 index 0000000000..d98f592413 --- /dev/null +++ b/packages/utils/src/dal/mikro-orm/decorators/searchable.ts @@ -0,0 +1,10 @@ +import { MetadataStorage } from "@mikro-orm/core" + +export function Searchable() { + return function (target, propertyName) { + const meta = MetadataStorage.getMetadataFromDecorator(target.constructor) + const prop = meta.properties[propertyName] || {} + prop["searchable"] = true + meta.properties[prop.name] = prop + } +} diff --git a/packages/utils/src/dal/mikro-orm/mikro-orm-create-connection.ts b/packages/utils/src/dal/mikro-orm/mikro-orm-create-connection.ts index b9754f1859..0ca55e3774 100644 --- a/packages/utils/src/dal/mikro-orm/mikro-orm-create-connection.ts +++ b/packages/utils/src/dal/mikro-orm/mikro-orm-create-connection.ts @@ -1,6 +1,7 @@ import { ModuleServiceInitializeOptions } from "@medusajs/types" import { TSMigrationGenerator } from "@mikro-orm/migrations" import { isString } from "../../common" +import { FilterDef } from "@mikro-orm/core/typings" // Monkey patch due to the compilation version issue which prevents us from creating a proper class that extends the TSMigrationGenerator const originalCreateStatement = TSMigrationGenerator.prototype.createStatement @@ -67,8 +68,15 @@ TSMigrationGenerator.prototype.createStatement = function ( export { TSMigrationGenerator } +export type Filter = { + name?: string +} & Omit + export async function mikroOrmCreateConnection( - database: ModuleServiceInitializeOptions["database"] & { connection?: any }, + database: ModuleServiceInitializeOptions["database"] & { + connection?: any + filters?: Record + }, entities: any[], pathToMigrations: string ) { @@ -100,6 +108,7 @@ export async function mikroOrmCreateConnection( driverOptions, tsNode: process.env.APP_ENV === "development", type: "postgresql", + filters: database.filters ?? {}, migrations: { path: pathToMigrations, generator: TSMigrationGenerator, diff --git a/packages/utils/src/dal/mikro-orm/mikro-orm-free-text-search-filter.ts b/packages/utils/src/dal/mikro-orm/mikro-orm-free-text-search-filter.ts new file mode 100644 index 0000000000..1d85ae3bce --- /dev/null +++ b/packages/utils/src/dal/mikro-orm/mikro-orm-free-text-search-filter.ts @@ -0,0 +1,178 @@ +import { EntityClass, EntityProperty } from "@mikro-orm/core/typings" +import { EntityMetadata, EntitySchema, ReferenceType } from "@mikro-orm/core" +import { SqlEntityManager } from "@mikro-orm/postgresql" +import type { FindOneOptions, FindOptions } from "@mikro-orm/core/drivers" + +export const FreeTextSearchFilterKey = "freeTextSearch" + +interface FilterArgument { + value: string + fromEntity: string +} + +function getEntityProperties(entity: EntityClass | EntitySchema): { + [key: string]: EntityProperty +} { + return ( + (entity as EntityClass)?.prototype.__meta?.properties ?? + (entity as EntitySchema).meta?.properties + ) +} + +function retrieveRelationsConstraints( + relation: { + targetMeta?: EntityMetadata + searchable?: boolean + mapToPk?: boolean + type: string + name: string + }, + models: (EntityClass | EntitySchema)[], + searchValue: string, + visited: Set = new Set(), + shouldStop: boolean = false +) { + if (shouldStop || !relation.searchable) { + return + } + + const relationClassName = relation.targetMeta!.className + + visited.add(relationClassName) + + const relationFreeTextSearchWhere: any = [] + + const relationClass = models.find((m) => m.name === relation.type)! + const relationProperties = getEntityProperties(relationClass) + + for (const propertyConfiguration of Object.values(relationProperties)) { + if ( + !(propertyConfiguration as any).searchable || + propertyConfiguration.reference !== ReferenceType.SCALAR + ) { + continue + } + + relationFreeTextSearchWhere.push({ + [propertyConfiguration.name]: { + $ilike: `%${searchValue}%`, + }, + }) + } + + const innerRelations: EntityProperty[] = + (relationClass as EntityClass)?.prototype.__meta?.relations ?? + (relationClass as EntitySchema).meta?.relations + + for (const innerRelation of innerRelations) { + const branchVisited = new Set(Array.from(visited)) + const innerRelationClassName = innerRelation.targetMeta!.className + const isSelfCircularDependency = + innerRelationClassName === relationClassName + + if ( + !isSelfCircularDependency && + branchVisited.has(innerRelationClassName) + ) { + continue + } + + branchVisited.add(innerRelationClassName) + + const innerRelationName = !innerRelation.mapToPk + ? innerRelation.name + : relation.targetMeta!.relations.find( + (r) => r.type === innerRelation.type && !r.mapToPk + )?.name + + if (!innerRelationName) { + throw new Error( + `Unable to retrieve the counter part relation definition for the mapToPk relation ${innerRelation.name} on entity ${relation.name}` + ) + } + + const relationConstraints = retrieveRelationsConstraints( + { + name: innerRelationName, + targetMeta: innerRelation.targetMeta, + searchable: (innerRelation as any).searchable, + mapToPk: innerRelation.mapToPk, + type: innerRelation.type, + }, + models, + searchValue, + branchVisited, + isSelfCircularDependency + ) + + if (!relationConstraints?.length) { + continue + } + + relationFreeTextSearchWhere.push({ + [innerRelationName]: { + $or: relationConstraints, + }, + }) + } + + return relationFreeTextSearchWhere +} + +export const mikroOrmFreeTextSearchFilterOptionsFactory = ( + models: (EntityClass | EntitySchema)[] +) => { + return { + cond: ( + freeTextSearchArgs: FilterArgument, + operation: string, + manager: SqlEntityManager, + options?: (FindOptions | FindOneOptions) & { + visited?: Set> + } + ) => { + if (!freeTextSearchArgs || !freeTextSearchArgs.value) { + return {} + } + + const { value, fromEntity } = freeTextSearchArgs + + if (options?.visited?.size) { + /** + * When being in select in strategy, the filter gets applied to all queries even the ones that are not related to the entity + */ + const hasFilterAlreadyBeenAppliedForEntity = [ + ...options.visited.values(), + ].some((v) => v.constructor.name === freeTextSearchArgs.fromEntity) + if (hasFilterAlreadyBeenAppliedForEntity) { + return {} + } + } + + const entityMetadata = manager.getDriver().getMetadata().get(fromEntity) + + const freeTextSearchWhere = retrieveRelationsConstraints( + { + targetMeta: entityMetadata, + mapToPk: false, + searchable: true, + type: fromEntity, + name: entityMetadata.name!, + }, + models, + value + ) + + if (!freeTextSearchWhere.length) { + return {} + } + + return { + $or: freeTextSearchWhere, + } + }, + default: true, + args: false, + entity: models.map((m) => m.name) as string[], + } +} diff --git a/packages/utils/src/modules-sdk/internal-module-service-factory.ts b/packages/utils/src/modules-sdk/internal-module-service-factory.ts index c7d5f9a3b6..4d8a4dbc94 100644 --- a/packages/utils/src/modules-sdk/internal-module-service-factory.ts +++ b/packages/utils/src/modules-sdk/internal-module-service-factory.ts @@ -2,19 +2,20 @@ import { BaseFilterable, Context, FilterQuery, - FindConfig, FilterQuery as InternalFilterQuery, + FindConfig, ModulesSdkTypes, + UpsertWithReplaceConfig, } from "@medusajs/types" import { EntitySchema } from "@mikro-orm/core" import { EntityClass } from "@mikro-orm/core/typings" import { - MedusaError, doNotForceTransaction, isDefined, isObject, isString, lowerCaseFirst, + MedusaError, shouldForceTransaction, } from "../common" import { buildQuery } from "./build-query" @@ -23,7 +24,7 @@ import { InjectTransactionManager, MedusaContext, } from "./decorators" -import { UpsertWithReplaceConfig } from "@medusajs/types" +import { FreeTextSearchFilterKey } from "../dal" type SelectorAndData = { selector: FilterQuery | BaseFilterable> @@ -53,6 +54,20 @@ export function internalModuleServiceFactory< this[propertyRepositoryName] = container[injectedRepositoryName] } + static applyFreeTextSearchFilter( + filters: FilterQuery, + config: FindConfig + ): void { + if (isDefined(filters?.q)) { + config.filters ??= {} + config.filters[FreeTextSearchFilterKey] = { + value: filters.q, + fromEntity: model.name, + } + delete filters.q + } + } + static retrievePrimaryKeys(entity: EntityClass | EntitySchema) { return ( (entity as EntitySchema).meta?.primaryKeys ?? @@ -149,6 +164,8 @@ export function internalModuleServiceFactory< @MedusaContext() sharedContext: Context = {} ): Promise { AbstractService_.applyDefaultOrdering(config) + AbstractService_.applyFreeTextSearchFilter(filters, config) + const queryOptions = buildQuery(filters, config) return await this[propertyRepositoryName].find( @@ -164,6 +181,8 @@ export function internalModuleServiceFactory< @MedusaContext() sharedContext: Context = {} ): Promise<[TEntity[], number]> { AbstractService_.applyDefaultOrdering(config) + AbstractService_.applyFreeTextSearchFilter(filters, config) + const queryOptions = buildQuery(filters, config) return await this[propertyRepositoryName].findAndCount( diff --git a/packages/utils/src/modules-sdk/loaders/mikro-orm-connection-loader.ts b/packages/utils/src/modules-sdk/loaders/mikro-orm-connection-loader.ts index 480743f315..a0c3d6c8f5 100644 --- a/packages/utils/src/modules-sdk/loaders/mikro-orm-connection-loader.ts +++ b/packages/utils/src/modules-sdk/loaders/mikro-orm-connection-loader.ts @@ -8,7 +8,11 @@ import { import { PostgreSqlDriver, SqlEntityManager } from "@mikro-orm/postgresql" import { asValue } from "awilix" import { ContainerRegistrationKeys, MedusaError } from "../../common" -import { mikroOrmCreateConnection } from "../../dal" +import { + FreeTextSearchFilterKey, + mikroOrmCreateConnection, + mikroOrmFreeTextSearchFilterOptionsFactory, +} from "../../dal" import { loadDatabaseConfig } from "../load-module-database-config" /** @@ -17,6 +21,7 @@ import { loadDatabaseConfig } from "../load-module-database-config" * @param moduleName * @param container * @param options + * @param filters * @param moduleDeclaration * @param entities * @param pathToMigrations @@ -39,6 +44,9 @@ export async function mikroOrmConnectionLoader({ logger?: Logger pathToMigrations: string }) { + const freeTextSearchGlobalFilter = + mikroOrmFreeTextSearchFilterOptionsFactory(entities) + let manager = ( options as ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions )?.manager @@ -62,7 +70,12 @@ export async function mikroOrmConnectionLoader({ shouldSwallowError ) return await loadShared({ - database: dbConfig, + database: { + ...dbConfig, + filters: { + [FreeTextSearchFilterKey as string]: freeTextSearchGlobalFilter, + }, + }, container, entities, pathToMigrations, @@ -87,7 +100,12 @@ export async function mikroOrmConnectionLoader({ } manager ??= await loadDefault({ - database: dbConfig, + database: { + ...dbConfig, + filters: { + [FreeTextSearchFilterKey as string]: freeTextSearchGlobalFilter, + }, + }, entities, pathToMigrations, })