fix(): handle empty q filters - allow to query deleted records from graph API - staled_at fixes (#11544)

* fix(): Allow to query deleted records from graph API

* fix(): Allow to query deleted records from graph API

* handle empty q value

* update staled at sync

* rename integration tests file

* Create strong-houses-marry.md

* try to fix flacky tests

* fix pricing context

* update changeset

* update changeset

* fix import

* skip test for now

---------

Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
Adrien de Peretti
2025-02-21 13:24:12 +01:00
committed by GitHub
parent cfffd55ae6
commit 065df75e7d
10 changed files with 239 additions and 131 deletions

View File

@@ -1,7 +1,6 @@
import {
CommonEvents,
ContainerRegistrationKeys,
groupBy,
Modules,
promiseAll,
} from "@medusajs/framework/utils"
@@ -41,10 +40,6 @@ export class DataSynchronizer {
return this.#container.indexSyncService
}
get #indexDataService(): ModulesSdkTypes.IMedusaInternalService<any> {
return this.#container.indexDataService
}
// @ts-ignore
get #indexRelationService(): ModulesSdkTypes.IMedusaInternalService<any> {
return this.#container.indexRelationService
@@ -103,48 +98,20 @@ export class DataSynchronizer {
async removeEntities(entities: string[], staleOnly: boolean = false) {
this.#isReadyOrThrow()
const staleCondition = staleOnly ? { staled_at: { $ne: null } } : {}
const staleCondition = staleOnly ? "staled_at IS NOT NULL" : ""
const dataToDelete = await this.#indexDataService.list({
...staleCondition,
name: entities,
})
const toDeleteByEntity = groupBy(dataToDelete, "name")
for (const entity of toDeleteByEntity.keys()) {
const records = toDeleteByEntity.get(entity)
const ids = records?.map(
(record: { data: { id: string } }) => record.data.id
for (const entity of entities) {
await this.#container.manager.execute(
`WITH deleted_data AS (
DELETE FROM "index_data"
WHERE "name" = ? ${staleCondition ? `AND ${staleCondition}` : ""}
RETURNING id
)
DELETE FROM "index_relation"
WHERE ("parent_name" = ? AND "parent_id" IN (SELECT id FROM deleted_data))
OR ("child_name" = ? AND "child_id" IN (SELECT id FROM deleted_data))`,
[entity, entity, entity]
)
if (!ids?.length) {
continue
}
if (this.#schemaObjectRepresentation[entity]) {
// Here we assume that some data have been deleted from from the source and we are cleaning since they are still staled in the index and we remove them from the index
// TODO: expand storage provider interface
await (this.#storageProvider as any).onDelete({
entity,
data: ids,
schemaEntityObjectRepresentation:
this.#schemaObjectRepresentation[entity],
})
} else {
// Here we assume that the entity is not indexed anymore as it is not part of the schema object representation and we are cleaning the index
// TODO: Drop the partition somewhere
await promiseAll([
this.#container.manager.execute(
`DELETE FROM "index_data" WHERE "name" = ?`,
[entity]
),
this.#container.manager.execute(
`DELETE FROM "index_relation" WHERE "parent_name" = ? OR "child_name" = ?`,
[entity, entity]
),
])
}
}
}

View File

@@ -2,11 +2,13 @@ import {
Context,
Event,
IndexTypes,
QueryGraphFunction,
RemoteQueryFunction,
Subscriber,
} from "@medusajs/framework/types"
import {
MikroOrmBaseRepository as BaseRepository,
CommonEvents,
ContainerRegistrationKeys,
deepMerge,
InjectManager,
@@ -210,13 +212,20 @@ export class PostgresProvider implements IndexTypes.StorageProvider {
}
const { fields, alias } = schemaEntityObjectRepresentation
const { data: entityData } = await this.query_.graph({
const graphConfig: Parameters<QueryGraphFunction>[0] = {
entity: alias,
filters: {
id: ids,
},
fields: [...new Set(["id", ...fields])],
})
}
if (action === CommonEvents.DELETED || action === CommonEvents.DETACHED) {
graphConfig.withDeleted = true
}
const { data: entityData } = await this.query_.graph(graphConfig)
const argument = {
entity: schemaEntityObjectRepresentation.entity,

View File

@@ -591,10 +591,14 @@ export class QueryBuilder {
let textSearchQuery: string | null = null
const searchQueryFilterProp = `${rootEntity}.q`
if (filter[searchQueryFilterProp]) {
hasTextSearch = true
textSearchQuery = filter[searchQueryFilterProp]
delete filter[searchQueryFilterProp]
if (searchQueryFilterProp in filter) {
if (!filter[searchQueryFilterProp]) {
delete filter[searchQueryFilterProp]
} else {
hasTextSearch = true
textSearchQuery = filter[searchQueryFilterProp]
delete filter[searchQueryFilterProp]
}
}
const joinParts = this.buildQueryParts(