fix(): Remove redundant indexes to resolve schema warnings (#13736)

Fixes #13735

### What

This Pull Request introduces new database migrations to remove multiple redundant indexes across several core modules, including product, cart, order, customer, and inventory.

### Why

As detailed in issue #13735, a fresh Medusa installation produces numerous "Duplicate Index" warnings. These legacy indexes add unnecessary write overhead and provide no performance benefit. This PR cleans up the schema to resolve these warnings and improve database health.

### How

I have added one new, reversible migration file for each of the five affected modules:
- `@medusajs/product`
- `@medusajs/cart`
- `@medusajs/order`
- `@medusajs/customer`
- `@medusajs/inventory`

Each migration's `up()` method safely drops the older, redundant index, and its `down()` method re-creates it, ensuring the change is fully reversible and non-destructive.


### Testing

I have tested these migrations by following the local development workflow outlined in the `CONTRIBUTING.md` guide.

1.  **Setup:**
    *   Cloned my forked Medusa repository locally .
    *   Created a separate, fresh test project using `npx create-medusa-app@latest my-medusa-store`.
    *   The test project's PostgreSQL database, which already contained the schema with the duplicate indexes.

2.  **Linking Local Source Code:**
    *   In the test project's `package.json`, I modified all `@medusajs/*` dependencies and resolutions to point to the local packages in my forked repository (e.g., `"@medusajs/product": "file:../medusa/packages/modules/product"`).
    *   From the test project's directory, I ran `yarn install` to link the local, modified Medusa source code into its `node_modules`.

3.  **Build & Migration:**
    *   Inside my forked Medusa repository, I ran `yarn build` to compile the new TypeScript migration files.
    *   From the root of the **test project**, I then executed the migration command: `npx medusa migration run`.

4.  **Verification:**
    *   The command successfully identified and ran only the new migration files I had created.
    *   I also confirmed via direct SQL queries that the old, redundant indexes were correctly dropped from all affected tables (`product_collection`, `customer_group`, etc.).
This commit is contained in:
debashish
2025-10-21 19:20:10 +05:30
committed by GitHub
parent 0d83918348
commit a34fcfab35
18 changed files with 501 additions and 352 deletions

View File

@@ -0,0 +1,9 @@
---
"@medusajs/cart": patch
"@medusajs/customer": patch
"@medusajs/inventory": patch
"@medusajs/order": patch
"@medusajs/product": patch
---
fix(): Remove redundant indexes from product, cart, order, customer, and inventory modules to improve database schema health.

View File

@@ -895,15 +895,6 @@
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_cart_line_item_deleted_at\" ON \"cart_line_item\" (deleted_at) WHERE deleted_at IS NULL"
},
{
"keyName": "IDX_line_item_cart_id",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_line_item_cart_id\" ON \"cart_line_item\" (cart_id) WHERE deleted_at IS NULL"
},
{
"keyName": "IDX_line_item_variant_id",
"columnNames": [],
@@ -1116,15 +1107,6 @@
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_line_item_adjustment_promotion_id\" ON \"cart_line_item_adjustment\" (promotion_id) WHERE deleted_at IS NULL AND promotion_id IS NOT NULL"
},
{
"keyName": "IDX_adjustment_item_id",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_adjustment_item_id\" ON \"cart_line_item_adjustment\" (item_id) WHERE deleted_at IS NULL"
},
{
"keyName": "cart_line_item_adjustment_pkey",
"columnNames": [
@@ -1297,15 +1279,6 @@
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_line_item_tax_line_tax_rate_id\" ON \"cart_line_item_tax_line\" (tax_rate_id) WHERE deleted_at IS NULL AND tax_rate_id IS NOT NULL"
},
{
"keyName": "IDX_tax_line_item_id",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_tax_line_item_id\" ON \"cart_line_item_tax_line\" (item_id) WHERE deleted_at IS NULL"
},
{
"keyName": "cart_line_item_tax_line_pkey",
"columnNames": [
@@ -1482,15 +1455,6 @@
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_cart_shipping_method_deleted_at\" ON \"cart_shipping_method\" (deleted_at) WHERE deleted_at IS NULL"
},
{
"keyName": "IDX_shipping_method_cart_id",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_shipping_method_cart_id\" ON \"cart_shipping_method\" (cart_id) WHERE deleted_at IS NULL"
},
{
"keyName": "IDX_shipping_method_option_id",
"columnNames": [],
@@ -1681,15 +1645,6 @@
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_shipping_method_adjustment_promotion_id\" ON \"cart_shipping_method_adjustment\" (promotion_id) WHERE deleted_at IS NULL AND promotion_id IS NOT NULL"
},
{
"keyName": "IDX_adjustment_shipping_method_id",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_adjustment_shipping_method_id\" ON \"cart_shipping_method_adjustment\" (shipping_method_id) WHERE deleted_at IS NULL"
},
{
"keyName": "cart_shipping_method_adjustment_pkey",
"columnNames": [
@@ -1846,15 +1801,6 @@
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_cart_shipping_method_tax_line_deleted_at\" ON \"cart_shipping_method_tax_line\" (deleted_at) WHERE deleted_at IS NULL"
},
{
"keyName": "IDX_tax_line_shipping_method_id",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_tax_line_shipping_method_id\" ON \"cart_shipping_method_tax_line\" (shipping_method_id) WHERE deleted_at IS NULL"
},
{
"keyName": "IDX_shipping_method_tax_line_tax_rate_id",
"columnNames": [],

View File

@@ -0,0 +1,33 @@
import { Migration } from '@mikro-orm/migrations';
export class Migration20251017153909 extends Migration {
override async up(): Promise<void> {
this.addSql(`drop index if exists "IDX_line_item_cart_id";`);
this.addSql(`drop index if exists "IDX_adjustment_item_id";`);
this.addSql(`drop index if exists "IDX_tax_line_item_id";`);
this.addSql(`drop index if exists "IDX_shipping_method_cart_id";`);
this.addSql(`drop index if exists "IDX_adjustment_shipping_method_id";`);
this.addSql(`drop index if exists "IDX_tax_line_shipping_method_id";`);
}
override async down(): Promise<void> {
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_line_item_cart_id" ON "cart_line_item" (cart_id) WHERE deleted_at IS NULL;`);
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_adjustment_item_id" ON "cart_line_item_adjustment" (item_id) WHERE deleted_at IS NULL;`);
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_tax_line_item_id" ON "cart_line_item_tax_line" (item_id) WHERE deleted_at IS NULL;`);
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_shipping_method_cart_id" ON "cart_shipping_method" (cart_id) WHERE deleted_at IS NULL;`);
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_adjustment_shipping_method_id" ON "cart_shipping_method_adjustment" (shipping_method_id) WHERE deleted_at IS NULL;`);
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_tax_line_shipping_method_id" ON "cart_shipping_method_tax_line" (shipping_method_id) WHERE deleted_at IS NULL;`);
}
}

View File

@@ -24,8 +24,8 @@ const LineItemAdjustment = model
on: ["promotion_id"],
where: "deleted_at IS NULL AND promotion_id IS NOT NULL",
},
{
name: "IDX_adjustment_item_id",
{
name: "IDX_cart_line_item_adjustment_item_id",
on: ["item_id"],
where: "deleted_at IS NULL",
},

View File

@@ -27,7 +27,7 @@ const LineItemTaxLine = model
where: "deleted_at IS NULL AND tax_rate_id IS NOT NULL",
},
{
name: "IDX_tax_line_item_id",
name: "IDX_cart_line_item_tax_line_item_id",
on: ["item_id"],
where: "deleted_at IS NULL",
},

View File

@@ -46,7 +46,7 @@ const LineItem = model
)
.indexes([
{
name: "IDX_line_item_cart_id",
name: "IDX_cart_line_item_cart_id",
on: ["cart_id"],
where: "deleted_at IS NULL",
},

View File

@@ -27,7 +27,7 @@ const ShippingMethodAdjustment = model
where: "deleted_at IS NULL AND promotion_id IS NOT NULL",
},
{
name: "IDX_adjustment_shipping_method_id",
name: "IDX_cart_shipping_method_adjustment_shipping_method_id",
on: ["shipping_method_id"],
where: "deleted_at IS NULL",
},

View File

@@ -22,7 +22,7 @@ const ShippingMethodTaxLine = model
)
.indexes([
{
name: "IDX_tax_line_shipping_method_id",
name: "IDX_cart_shipping_method_tax_line_shipping_method_id",
on: ["shipping_method_id"],
where: "deleted_at IS NULL",
},

View File

@@ -31,7 +31,7 @@ const ShippingMethod = model
)
.indexes([
{
name: "IDX_shipping_method_cart_id",
name: "IDX_cart_shipping_method_cart_id",
on: ["cart_id"],
where: "deleted_at IS NULL",
},

View File

@@ -128,6 +128,7 @@
"keyName": "IDX_customer_deleted_at",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_customer_deleted_at\" ON \"customer\" (deleted_at) WHERE deleted_at IS NULL"
@@ -136,6 +137,7 @@
"keyName": "IDX_customer_email_has_account_unique",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_customer_email_has_account_unique\" ON \"customer\" (email, has_account) WHERE deleted_at IS NULL"
@@ -146,12 +148,14 @@
"id"
],
"composite": false,
"constraint": true,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {}
"foreignKeys": {},
"nativeEnums": {}
},
{
"columns": {
@@ -341,6 +345,7 @@
"keyName": "IDX_customer_address_customer_id",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_customer_address_customer_id\" ON \"customer_address\" (customer_id) WHERE deleted_at IS NULL"
@@ -349,6 +354,7 @@
"keyName": "IDX_customer_address_deleted_at",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_customer_address_deleted_at\" ON \"customer_address\" (deleted_at) WHERE deleted_at IS NULL"
@@ -357,6 +363,7 @@
"keyName": "IDX_customer_address_unique_customer_billing",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_customer_address_unique_customer_billing\" ON \"customer_address\" (customer_id) WHERE \"is_default_billing\" = true AND deleted_at IS NULL"
@@ -365,6 +372,7 @@
"keyName": "IDX_customer_address_unique_customer_shipping",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_customer_address_unique_customer_shipping\" ON \"customer_address\" (customer_id) WHERE \"is_default_shipping\" = true AND deleted_at IS NULL"
@@ -375,6 +383,7 @@
"id"
],
"composite": false,
"constraint": true,
"primary": true,
"unique": true
}
@@ -394,7 +403,8 @@
"deleteRule": "cascade",
"updateRule": "cascade"
}
}
},
"nativeEnums": {}
},
{
"columns": {
@@ -474,6 +484,7 @@
"keyName": "IDX_customer_group_deleted_at",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_customer_group_deleted_at\" ON \"customer_group\" (deleted_at) WHERE deleted_at IS NULL"
@@ -482,6 +493,7 @@
"keyName": "IDX_customer_group_name_unique",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_customer_group_name_unique\" ON \"customer_group\" (name) WHERE deleted_at IS NULL"
@@ -492,12 +504,14 @@
"id"
],
"composite": false,
"constraint": true,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {}
"foreignKeys": {},
"nativeEnums": {}
},
{
"columns": {
@@ -586,6 +600,7 @@
"keyName": "IDX_customer_group_customer_customer_id",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_customer_group_customer_customer_id\" ON \"customer_group_customer\" (customer_id) WHERE deleted_at IS NULL"
@@ -594,6 +609,7 @@
"keyName": "IDX_customer_group_customer_customer_group_id",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_customer_group_customer_customer_group_id\" ON \"customer_group_customer\" (customer_group_id) WHERE deleted_at IS NULL"
@@ -602,6 +618,7 @@
"keyName": "IDX_customer_group_customer_deleted_at",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_customer_group_customer_deleted_at\" ON \"customer_group_customer\" (deleted_at) WHERE deleted_at IS NULL"
@@ -612,12 +629,15 @@
"id"
],
"composite": false,
"constraint": true,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {}
"foreignKeys": {},
"nativeEnums": {}
}
]
],
"nativeEnums": {}
}

View File

@@ -0,0 +1,17 @@
import { Migration } from '@mikro-orm/migrations';
export class Migration20251010130829 extends Migration {
override async up(): Promise<void> {
this.addSql(
'DROP INDEX IF EXISTS "IDX_customer_group_name";'
);
}
override async down(): Promise<void> {
this.addSql(
'CREATE UNIQUE INDEX IF NOT EXISTS "IDX_customer_group_name" ON "customer_group" ("name") WHERE "deleted_at" IS NULL;'
);
}
}

View File

@@ -182,22 +182,16 @@
"keyName": "IDX_inventory_item_deleted_at",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_inventory_item_deleted_at\" ON \"inventory_item\" (deleted_at) WHERE deleted_at IS NULL"
},
{
"keyName": "IDX_inventory_item_deleted_at",
"columnNames": [],
"composite": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_inventory_item_deleted_at\" ON \"inventory_item\" (deleted_at) WHERE deleted_at IS NOT NULL"
},
{
"keyName": "IDX_inventory_item_sku",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_inventory_item_sku\" ON \"inventory_item\" (sku) WHERE deleted_at IS NULL"
@@ -208,12 +202,14 @@
"id"
],
"composite": false,
"constraint": true,
"primary": true,
"unique": true
}
],
"checks": [],
"foreignKeys": {}
"foreignKeys": {},
"nativeEnums": {}
},
{
"columns": {
@@ -350,6 +346,7 @@
"keyName": "IDX_inventory_level_inventory_item_id",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_inventory_level_inventory_item_id\" ON \"inventory_level\" (inventory_item_id) WHERE deleted_at IS NULL"
@@ -358,6 +355,7 @@
"keyName": "IDX_inventory_level_deleted_at",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_inventory_level_deleted_at\" ON \"inventory_level\" (deleted_at) WHERE deleted_at IS NULL"
@@ -366,6 +364,7 @@
"keyName": "IDX_inventory_level_location_id",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_inventory_level_location_id\" ON \"inventory_level\" (location_id) WHERE deleted_at IS NULL"
@@ -374,6 +373,7 @@
"keyName": "IDX_inventory_level_location_id_inventory_item_id",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_inventory_level_location_id_inventory_item_id\" ON \"inventory_level\" (inventory_item_id, location_id) WHERE deleted_at IS NULL"
@@ -384,6 +384,7 @@
"id"
],
"composite": false,
"constraint": true,
"primary": true,
"unique": true
}
@@ -403,7 +404,8 @@
"deleteRule": "cascade",
"updateRule": "cascade"
}
}
},
"nativeEnums": {}
},
{
"columns": {
@@ -547,6 +549,7 @@
"keyName": "IDX_reservation_item_inventory_item_id",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_reservation_item_inventory_item_id\" ON \"reservation_item\" (inventory_item_id) WHERE deleted_at IS NULL"
@@ -555,6 +558,7 @@
"keyName": "IDX_reservation_item_deleted_at",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_reservation_item_deleted_at\" ON \"reservation_item\" (deleted_at) WHERE deleted_at IS NULL"
@@ -563,6 +567,7 @@
"keyName": "IDX_reservation_item_line_item_id",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_reservation_item_line_item_id\" ON \"reservation_item\" (line_item_id) WHERE deleted_at IS NULL"
@@ -571,6 +576,7 @@
"keyName": "IDX_reservation_item_location_id",
"columnNames": [],
"composite": false,
"constraint": false,
"primary": false,
"unique": false,
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_reservation_item_location_id\" ON \"reservation_item\" (location_id) WHERE deleted_at IS NULL"
@@ -581,6 +587,7 @@
"id"
],
"composite": false,
"constraint": true,
"primary": true,
"unique": true
}
@@ -600,7 +607,9 @@
"deleteRule": "cascade",
"updateRule": "cascade"
}
}
},
"nativeEnums": {}
}
]
],
"nativeEnums": {}
}

View File

@@ -0,0 +1,17 @@
import { Migration } from '@mikro-orm/migrations';
export class Migration20251010131115 extends Migration {
override async up(): Promise<void> {
this.addSql(
'DROP INDEX IF EXISTS "IDX_inventory_level_item_location";'
);
}
override async down(): Promise<void> {
this.addSql(
'CREATE UNIQUE INDEX IF NOT EXISTS "IDX_inventory_level_item_location" ON "inventory_level" (inventory_item_id, location_id) WHERE deleted_at IS NULL;'
);
}
}

View File

@@ -0,0 +1,25 @@
import { Migration } from '@mikro-orm/migrations';
export class Migration20251017155709 extends Migration {
override async up(): Promise<void> {
this.addSql(`drop index if exists "IDX_order_item_version";`);
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_order_item_order_id_version" ON "order_item" (order_id, version) WHERE deleted_at IS NULL;`);
this.addSql(`drop index if exists "IDX_order_shipping_version";`);
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_order_shipping_order_id_version" ON "order_shipping" (order_id, version) WHERE deleted_at IS NULL;`);
}
override async down(): Promise<void> {
this.addSql(`drop index if exists "IDX_order_item_order_id_version";`);
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_order_item_version" ON "order_item" (order_id, version) WHERE deleted_at IS NULL;`);
this.addSql(`drop index if exists "IDX_order_shipping_order_id_version";`);
this.addSql(`CREATE INDEX IF NOT EXISTS "IDX_order_shipping_version" ON "order_shipping" (order_id, version) WHERE deleted_at IS NULL;`);
}
}

View File

@@ -34,8 +34,8 @@ const _OrderItem = model
where: "deleted_at IS NULL",
},
{
name: "IDX_order_item_version",
on: ["version"],
name: "IDX_order_item_order_id_version",
on: ["order_id", "version"],
unique: false,
where: "deleted_at IS NULL",
},

View File

@@ -61,8 +61,8 @@ const _OrderShipping = model
where: "claim_id IS NOT NULL AND deleted_at IS NULL",
},
{
name: "IDX_order_shipping_version",
on: ["version"],
name: "IDX_order_shipping_order_id_version",
on: ["order_id", "version"],
unique: false,
where: "deleted_at IS NULL",
},

View File

@@ -0,0 +1,18 @@
import { Migration } from '@mikro-orm/migrations';
export class Migration20251011090511 extends Migration {
// UP: Fixes the bug by dropping the bad index from product_collection.
override async up(): Promise<void> {
this.addSql(
'DROP INDEX IF EXISTS "IDX_product_category_deleted_at";'
);
}
// DOWN: Reverts the fix by re-creating the original bug.
override async down(): Promise<void> {
this.addSql(
'CREATE INDEX IF NOT EXISTS "IDX_product_category_deleted_at" ON "product_collection" ("deleted_at");'
);
}
}