What:
- `query.index` helper. It queries the index module, and aggregate the rest of requested fields/relations if needed like `query.graph`.
Not covered in this PR:
- Hydrate only sub entities returned by the query. Example: 1 out of 5 variants have returned, it should only hydrate the data of the single entity, currently it will merge all the variants of the product.
- Generate types of indexed data
example:
```ts
const query = container.resolve(ContainerRegistrationKeys.QUERY)
await query.index({
entity: "product",
fields: [
"id",
"description",
"status",
"variants.sku",
"variants.barcode",
"variants.material",
"variants.options.value",
"variants.prices.amount",
"variants.prices.currency_code",
"variants.inventory_items.inventory.sku",
"variants.inventory_items.inventory.description",
],
filters: {
"variants.sku": { $like: "%-1" },
"variants.prices.amount": { $gt: 30 },
},
pagination: {
order: {
"variants.prices.amount": "DESC",
},
},
})
```
This query return all products where at least one variant has the title ending in `-1` and at least one price bigger than `30`.
The Index Module only hold the data used to paginate and filter, and the returned object is:
```json
{
"id": "prod_01JKEAM2GJZ14K64R0DHK0JE72",
"title": null,
"variants": [
{
"id": "variant_01JKEAM2HC89GWS95F6GF9C6YA",
"sku": "extra-variant-1",
"prices": [
{
"id": "price_01JKEAM2JADEWWX72F8QDP6QXT",
"amount": 80,
"currency_code": "USD"
}
]
}
]
}
```
All the rest of the fields will be hydrated from their respective modules, and the final result will be:
```json
{
"id": "prod_01JKEAY2RJTF8TW9A23KTGY1GD",
"description": "extra description",
"status": "draft",
"variants": [
{
"sku": "extra-variant-1",
"barcode": null,
"material": null,
"id": "variant_01JKEAY2S945CRZ6X4QZJ7GVBJ",
"options": [
{
"value": "Red"
}
],
"prices": [
{
"amount": 20,
"currency_code": "CAD",
"id": "price_01JKEAY2T2EEYSWZHPGG11B7W7"
},
{
"amount": 80,
"currency_code": "USD",
"id": "price_01JKEAY2T2NJK2E5468RK84CAR"
}
],
"inventory_items": [
{
"variant_id": "variant_01JKEAY2S945CRZ6X4QZJ7GVBJ",
"inventory_item_id": "iitem_01JKEAY2SNY2AWEHPZN0DDXVW6",
"inventory": {
"sku": "extra-variant-1",
"description": "extra variant 1",
"id": "iitem_01JKEAY2SNY2AWEHPZN0DDXVW6"
}
}
]
}
]
}
```
Co-authored-by: Adrien de Peretti <25098370+adrien2p@users.noreply.github.com>
88 lines
2.9 KiB
TypeScript
88 lines
2.9 KiB
TypeScript
import { IndexTypes } from "@medusajs/framework/types"
|
|
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
|
import { schemaObjectRepresentationPropertiesToOmit } from "@types"
|
|
|
|
export async function createPartitions(
|
|
schemaObjectRepresentation: IndexTypes.SchemaObjectRepresentation,
|
|
manager: SqlEntityManager
|
|
): Promise<void> {
|
|
const activeSchema = manager.config.get("schema")
|
|
? `"${manager.config.get("schema")}".`
|
|
: ""
|
|
const partitions = Object.keys(schemaObjectRepresentation)
|
|
.filter(
|
|
(key) =>
|
|
!schemaObjectRepresentationPropertiesToOmit.includes(key) &&
|
|
schemaObjectRepresentation[key].listeners.length > 0
|
|
)
|
|
.map((key) => {
|
|
const cName = key.toLowerCase()
|
|
const part: string[] = []
|
|
part.push(
|
|
`CREATE TABLE IF NOT EXISTS ${activeSchema}cat_${cName} PARTITION OF ${activeSchema}index_data FOR VALUES IN ('${key}')`
|
|
)
|
|
|
|
for (const parent of schemaObjectRepresentation[key].parents) {
|
|
const pKey = `${parent.ref.entity}-${key}`
|
|
const pName = `${parent.ref.entity}${key}`.toLowerCase()
|
|
part.push(
|
|
`CREATE TABLE IF NOT EXISTS ${activeSchema}cat_pivot_${pName} PARTITION OF ${activeSchema}index_relation FOR VALUES IN ('${pKey}')`
|
|
)
|
|
}
|
|
return part
|
|
})
|
|
.flat()
|
|
|
|
if (!partitions.length) {
|
|
return
|
|
}
|
|
|
|
await manager.execute(partitions.join("; "))
|
|
|
|
// Create indexes for each partition
|
|
const indexCreationCommands = Object.keys(schemaObjectRepresentation)
|
|
.filter(
|
|
(key) =>
|
|
!schemaObjectRepresentationPropertiesToOmit.includes(key) &&
|
|
schemaObjectRepresentation[key].listeners.length > 0
|
|
)
|
|
.map((key) => {
|
|
const cName = key.toLowerCase()
|
|
const part: string[] = []
|
|
|
|
part.push(
|
|
`CREATE INDEX CONCURRENTLY IF NOT EXISTS "IDX_cat_${cName}_data_gin" ON ${activeSchema}cat_${cName} USING GIN ("data" jsonb_path_ops)`
|
|
)
|
|
|
|
part.push(
|
|
`CREATE INDEX CONCURRENTLY IF NOT EXISTS "IDX_cat_${cName}_id" ON ${activeSchema}cat_${cName} ("id")`
|
|
)
|
|
|
|
// create child id index on pivot partitions
|
|
for (const parent of schemaObjectRepresentation[key].parents) {
|
|
const pName = `${parent.ref.entity}${key}`.toLowerCase()
|
|
part.push(
|
|
`CREATE INDEX CONCURRENTLY IF NOT EXISTS "IDX_cat_pivot_${pName}_child_id" ON ${activeSchema}cat_pivot_${pName} ("child_id")`
|
|
)
|
|
}
|
|
|
|
return part
|
|
})
|
|
.flat()
|
|
|
|
// Execute index creation commands separately to avoid blocking
|
|
for (const cmd of indexCreationCommands) {
|
|
try {
|
|
await manager.execute(cmd)
|
|
} catch (error) {
|
|
// Log error but continue with other indexes
|
|
console.error(`Failed to create index: ${error.message}`)
|
|
}
|
|
}
|
|
|
|
partitions.push(`analyse ${activeSchema}index_data`)
|
|
partitions.push(`analyse ${activeSchema}index_relation`)
|
|
|
|
await manager.execute(partitions.join("; "))
|
|
}
|