@floxiz/medusa-plugin-meilisearch 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.medusa/server/src/api/store/meilisearch/categories/route.d.ts.map +1 -1
- package/.medusa/server/src/api/store/meilisearch/categories/route.js +3 -2
- package/.medusa/server/src/api/store/meilisearch/products/route.js +2 -2
- package/.medusa/server/src/jobs/meilisearch-categories-index.d.ts.map +1 -1
- package/.medusa/server/src/jobs/meilisearch-categories-index.js +14 -4
- package/.medusa/server/src/jobs/meilisearch-products-index.d.ts.map +1 -1
- package/.medusa/server/src/jobs/meilisearch-products-index.js +14 -4
- package/.medusa/server/src/subscribers/meilisearch-category-delete.d.ts +6 -0
- package/.medusa/server/src/subscribers/meilisearch-category-delete.d.ts.map +1 -0
- package/.medusa/server/src/subscribers/meilisearch-category-delete.js +27 -0
- package/.medusa/server/src/subscribers/meilisearch-category-upsert.d.ts +6 -0
- package/.medusa/server/src/subscribers/meilisearch-category-upsert.d.ts.map +1 -0
- package/.medusa/server/src/subscribers/meilisearch-category-upsert.js +29 -0
- package/.medusa/server/src/subscribers/meilisearch-collection-delete.d.ts +6 -0
- package/.medusa/server/src/subscribers/meilisearch-collection-delete.d.ts.map +1 -0
- package/.medusa/server/src/subscribers/meilisearch-collection-delete.js +27 -0
- package/.medusa/server/src/subscribers/meilisearch-collection-upsert.d.ts +6 -0
- package/.medusa/server/src/subscribers/meilisearch-collection-upsert.d.ts.map +1 -0
- package/.medusa/server/src/subscribers/meilisearch-collection-upsert.js +33 -0
- package/.medusa/server/src/subscribers/meilisearch-image-upsert.d.ts +6 -0
- package/.medusa/server/src/subscribers/meilisearch-image-upsert.d.ts.map +1 -0
- package/.medusa/server/src/subscribers/meilisearch-image-upsert.js +29 -0
- package/.medusa/server/src/subscribers/meilisearch-inventory-delete.d.ts +6 -0
- package/.medusa/server/src/subscribers/meilisearch-inventory-delete.d.ts.map +1 -0
- package/.medusa/server/src/subscribers/meilisearch-inventory-delete.js +27 -0
- package/.medusa/server/src/subscribers/meilisearch-inventory-level-delete.d.ts +6 -0
- package/.medusa/server/src/subscribers/meilisearch-inventory-level-delete.d.ts.map +1 -0
- package/.medusa/server/src/subscribers/meilisearch-inventory-level-delete.js +27 -0
- package/.medusa/server/src/subscribers/meilisearch-inventory-level-upsert.d.ts +6 -0
- package/.medusa/server/src/subscribers/meilisearch-inventory-level-upsert.d.ts.map +1 -0
- package/.medusa/server/src/subscribers/meilisearch-inventory-level-upsert.js +29 -0
- package/.medusa/server/src/subscribers/meilisearch-inventory-upsert.d.ts +6 -0
- package/.medusa/server/src/subscribers/meilisearch-inventory-upsert.d.ts.map +1 -0
- package/.medusa/server/src/subscribers/meilisearch-inventory-upsert.js +29 -0
- package/.medusa/server/src/subscribers/meilisearch-option-delete.d.ts +6 -0
- package/.medusa/server/src/subscribers/meilisearch-option-delete.d.ts.map +1 -0
- package/.medusa/server/src/subscribers/meilisearch-option-delete.js +27 -0
- package/.medusa/server/src/subscribers/meilisearch-option-upsert.d.ts +6 -0
- package/.medusa/server/src/subscribers/meilisearch-option-upsert.d.ts.map +1 -0
- package/.medusa/server/src/subscribers/meilisearch-option-upsert.js +29 -0
- package/.medusa/server/src/subscribers/meilisearch-option-value-delete.d.ts +6 -0
- package/.medusa/server/src/subscribers/meilisearch-option-value-delete.d.ts.map +1 -0
- package/.medusa/server/src/subscribers/meilisearch-option-value-delete.js +27 -0
- package/.medusa/server/src/subscribers/meilisearch-option-value-upsert.d.ts +6 -0
- package/.medusa/server/src/subscribers/meilisearch-option-value-upsert.d.ts.map +1 -0
- package/.medusa/server/src/subscribers/meilisearch-option-value-upsert.js +29 -0
- package/.medusa/server/src/subscribers/meilisearch-price-delete.d.ts +6 -0
- package/.medusa/server/src/subscribers/meilisearch-price-delete.d.ts.map +1 -0
- package/.medusa/server/src/subscribers/meilisearch-price-delete.js +27 -0
- package/.medusa/server/src/subscribers/meilisearch-price-upsert.d.ts +6 -0
- package/.medusa/server/src/subscribers/meilisearch-price-upsert.d.ts.map +1 -0
- package/.medusa/server/src/subscribers/meilisearch-price-upsert.js +29 -0
- package/.medusa/server/src/subscribers/meilisearch-product-delete.d.ts +6 -0
- package/.medusa/server/src/subscribers/meilisearch-product-delete.d.ts.map +1 -0
- package/.medusa/server/src/subscribers/meilisearch-product-delete.js +27 -0
- package/.medusa/server/src/subscribers/meilisearch-product-upsert.d.ts +6 -0
- package/.medusa/server/src/subscribers/meilisearch-product-upsert.d.ts.map +1 -0
- package/.medusa/server/src/subscribers/meilisearch-product-upsert.js +29 -0
- package/.medusa/server/src/subscribers/meilisearch-sync.d.ts.map +1 -1
- package/.medusa/server/src/subscribers/meilisearch-sync.js +15 -9
- package/.medusa/server/src/subscribers/meilisearch-tag-delete.d.ts +6 -0
- package/.medusa/server/src/subscribers/meilisearch-tag-delete.d.ts.map +1 -0
- package/.medusa/server/src/subscribers/meilisearch-tag-delete.js +27 -0
- package/.medusa/server/src/subscribers/meilisearch-tag-upsert.d.ts +6 -0
- package/.medusa/server/src/subscribers/meilisearch-tag-upsert.d.ts.map +1 -0
- package/.medusa/server/src/subscribers/meilisearch-tag-upsert.js +33 -0
- package/.medusa/server/src/subscribers/meilisearch-type-delete.d.ts +6 -0
- package/.medusa/server/src/subscribers/meilisearch-type-delete.d.ts.map +1 -0
- package/.medusa/server/src/subscribers/meilisearch-type-delete.js +27 -0
- package/.medusa/server/src/subscribers/meilisearch-type-upsert.d.ts +6 -0
- package/.medusa/server/src/subscribers/meilisearch-type-upsert.d.ts.map +1 -0
- package/.medusa/server/src/subscribers/meilisearch-type-upsert.js +33 -0
- package/.medusa/server/src/subscribers/meilisearch-variant-delete.d.ts +6 -0
- package/.medusa/server/src/subscribers/meilisearch-variant-delete.d.ts.map +1 -0
- package/.medusa/server/src/subscribers/meilisearch-variant-delete.js +27 -0
- package/.medusa/server/src/subscribers/meilisearch-variant-upsert.d.ts +6 -0
- package/.medusa/server/src/subscribers/meilisearch-variant-upsert.d.ts.map +1 -0
- package/.medusa/server/src/subscribers/meilisearch-variant-upsert.js +29 -0
- package/.medusa/server/src/workflows/delete-category.d.ts +6 -0
- package/.medusa/server/src/workflows/delete-category.d.ts.map +1 -0
- package/.medusa/server/src/workflows/delete-category.js +10 -0
- package/.medusa/server/src/workflows/delete-collection.d.ts +8 -0
- package/.medusa/server/src/workflows/delete-collection.d.ts.map +1 -0
- package/.medusa/server/src/workflows/delete-collection.js +10 -0
- package/.medusa/server/src/workflows/delete-inventory-level.d.ts +8 -0
- package/.medusa/server/src/workflows/delete-inventory-level.d.ts.map +1 -0
- package/.medusa/server/src/workflows/delete-inventory-level.js +10 -0
- package/.medusa/server/src/workflows/delete-inventory.d.ts +8 -0
- package/.medusa/server/src/workflows/delete-inventory.d.ts.map +1 -0
- package/.medusa/server/src/workflows/delete-inventory.js +10 -0
- package/.medusa/server/src/workflows/delete-option-value.d.ts +8 -0
- package/.medusa/server/src/workflows/delete-option-value.d.ts.map +1 -0
- package/.medusa/server/src/workflows/delete-option-value.js +10 -0
- package/.medusa/server/src/workflows/delete-option.d.ts +8 -0
- package/.medusa/server/src/workflows/delete-option.d.ts.map +1 -0
- package/.medusa/server/src/workflows/delete-option.js +10 -0
- package/.medusa/server/src/workflows/delete-price.d.ts +8 -0
- package/.medusa/server/src/workflows/delete-price.d.ts.map +1 -0
- package/.medusa/server/src/workflows/delete-price.js +10 -0
- package/.medusa/server/src/workflows/delete-product.d.ts +6 -0
- package/.medusa/server/src/workflows/delete-product.d.ts.map +1 -0
- package/.medusa/server/src/workflows/delete-product.js +10 -0
- package/.medusa/server/src/workflows/delete-tag.d.ts +8 -0
- package/.medusa/server/src/workflows/delete-tag.d.ts.map +1 -0
- package/.medusa/server/src/workflows/delete-tag.js +10 -0
- package/.medusa/server/src/workflows/delete-type.d.ts +8 -0
- package/.medusa/server/src/workflows/delete-type.d.ts.map +1 -0
- package/.medusa/server/src/workflows/delete-type.js +10 -0
- package/.medusa/server/src/workflows/delete-variant.d.ts +8 -0
- package/.medusa/server/src/workflows/delete-variant.d.ts.map +1 -0
- package/.medusa/server/src/workflows/delete-variant.js +12 -0
- package/.medusa/server/src/workflows/steps/delete-category.d.ts +2 -2
- package/.medusa/server/src/workflows/steps/delete-category.d.ts.map +1 -1
- package/.medusa/server/src/workflows/steps/delete-category.js +4 -4
- package/.medusa/server/src/workflows/steps/delete-collection.d.ts +8 -0
- package/.medusa/server/src/workflows/steps/delete-collection.d.ts.map +1 -0
- package/.medusa/server/src/workflows/steps/delete-collection.js +86 -0
- package/.medusa/server/src/workflows/steps/delete-inventory-level.d.ts +8 -0
- package/.medusa/server/src/workflows/steps/delete-inventory-level.d.ts.map +1 -0
- package/.medusa/server/src/workflows/steps/delete-inventory-level.js +105 -0
- package/.medusa/server/src/workflows/steps/delete-inventory.d.ts +8 -0
- package/.medusa/server/src/workflows/steps/delete-inventory.d.ts.map +1 -0
- package/.medusa/server/src/workflows/steps/delete-inventory.js +76 -0
- package/.medusa/server/src/workflows/steps/delete-option-value.d.ts +8 -0
- package/.medusa/server/src/workflows/steps/delete-option-value.d.ts.map +1 -0
- package/.medusa/server/src/workflows/steps/delete-option-value.js +90 -0
- package/.medusa/server/src/workflows/steps/delete-option.d.ts +8 -0
- package/.medusa/server/src/workflows/steps/delete-option.d.ts.map +1 -0
- package/.medusa/server/src/workflows/steps/delete-option.js +87 -0
- package/.medusa/server/src/workflows/steps/delete-price.d.ts +8 -0
- package/.medusa/server/src/workflows/steps/delete-price.d.ts.map +1 -0
- package/.medusa/server/src/workflows/steps/delete-price.js +104 -0
- package/.medusa/server/src/workflows/steps/delete-product.d.ts +4 -2
- package/.medusa/server/src/workflows/steps/delete-product.d.ts.map +1 -1
- package/.medusa/server/src/workflows/steps/delete-product.js +6 -4
- package/.medusa/server/src/workflows/steps/delete-tag.d.ts +8 -0
- package/.medusa/server/src/workflows/steps/delete-tag.d.ts.map +1 -0
- package/.medusa/server/src/workflows/steps/delete-tag.js +86 -0
- package/.medusa/server/src/workflows/steps/delete-type.d.ts +8 -0
- package/.medusa/server/src/workflows/steps/delete-type.d.ts.map +1 -0
- package/.medusa/server/src/workflows/steps/delete-type.js +86 -0
- package/.medusa/server/src/workflows/steps/delete-variant.d.ts +8 -0
- package/.medusa/server/src/workflows/steps/delete-variant.d.ts.map +1 -0
- package/.medusa/server/src/workflows/steps/delete-variant.js +89 -0
- package/.medusa/server/src/workflows/steps/sync-categories.d.ts.map +1 -1
- package/.medusa/server/src/workflows/steps/sync-categories.js +3 -2
- package/.medusa/server/src/workflows/steps/sync-products.d.ts.map +1 -1
- package/.medusa/server/src/workflows/steps/sync-products.js +35 -3
- package/.medusa/server/src/workflows/steps/upsert-category.d.ts +1 -1
- package/.medusa/server/src/workflows/steps/upsert-category.d.ts.map +1 -1
- package/.medusa/server/src/workflows/steps/upsert-category.js +5 -4
- package/.medusa/server/src/workflows/steps/upsert-collection.d.ts +8 -0
- package/.medusa/server/src/workflows/steps/upsert-collection.d.ts.map +1 -0
- package/.medusa/server/src/workflows/steps/upsert-collection.js +69 -0
- package/.medusa/server/src/workflows/steps/upsert-image.d.ts +8 -0
- package/.medusa/server/src/workflows/steps/upsert-image.d.ts.map +1 -0
- package/.medusa/server/src/workflows/steps/upsert-image.js +70 -0
- package/.medusa/server/src/workflows/steps/upsert-inventory-level.d.ts +8 -0
- package/.medusa/server/src/workflows/steps/upsert-inventory-level.d.ts.map +1 -0
- package/.medusa/server/src/workflows/steps/upsert-inventory-level.js +86 -0
- package/.medusa/server/src/workflows/steps/upsert-inventory.d.ts +8 -0
- package/.medusa/server/src/workflows/steps/upsert-inventory.d.ts.map +1 -0
- package/.medusa/server/src/workflows/steps/upsert-inventory.js +77 -0
- package/.medusa/server/src/workflows/steps/upsert-option-value.d.ts +8 -0
- package/.medusa/server/src/workflows/steps/upsert-option-value.d.ts.map +1 -0
- package/.medusa/server/src/workflows/steps/upsert-option-value.js +75 -0
- package/.medusa/server/src/workflows/steps/upsert-option.d.ts +8 -0
- package/.medusa/server/src/workflows/steps/upsert-option.d.ts.map +1 -0
- package/.medusa/server/src/workflows/steps/upsert-option.js +67 -0
- package/.medusa/server/src/workflows/steps/upsert-price.d.ts +8 -0
- package/.medusa/server/src/workflows/steps/upsert-price.d.ts.map +1 -0
- package/.medusa/server/src/workflows/steps/upsert-price.js +84 -0
- package/.medusa/server/src/workflows/steps/upsert-product.d.ts +1 -1
- package/.medusa/server/src/workflows/steps/upsert-product.d.ts.map +1 -1
- package/.medusa/server/src/workflows/steps/upsert-product.js +37 -5
- package/.medusa/server/src/workflows/steps/upsert-tag.d.ts +8 -0
- package/.medusa/server/src/workflows/steps/upsert-tag.d.ts.map +1 -0
- package/.medusa/server/src/workflows/steps/upsert-tag.js +68 -0
- package/.medusa/server/src/workflows/steps/upsert-type.d.ts +8 -0
- package/.medusa/server/src/workflows/steps/upsert-type.d.ts.map +1 -0
- package/.medusa/server/src/workflows/steps/upsert-type.js +61 -0
- package/.medusa/server/src/workflows/steps/upsert-variant.d.ts +8 -0
- package/.medusa/server/src/workflows/steps/upsert-variant.d.ts.map +1 -0
- package/.medusa/server/src/workflows/steps/upsert-variant.js +70 -0
- package/.medusa/server/src/workflows/sync-categories.d.ts.map +1 -1
- package/.medusa/server/src/workflows/sync-categories.js +3 -3
- package/.medusa/server/src/workflows/sync-products.d.ts.map +1 -1
- package/.medusa/server/src/workflows/sync-products.js +1 -1
- package/.medusa/server/src/workflows/upsert-category.d.ts +8 -0
- package/.medusa/server/src/workflows/upsert-category.d.ts.map +1 -0
- package/.medusa/server/src/workflows/upsert-category.js +10 -0
- package/.medusa/server/src/workflows/upsert-collection.d.ts +8 -0
- package/.medusa/server/src/workflows/upsert-collection.d.ts.map +1 -0
- package/.medusa/server/src/workflows/upsert-collection.js +10 -0
- package/.medusa/server/src/workflows/upsert-image.d.ts +8 -0
- package/.medusa/server/src/workflows/upsert-image.d.ts.map +1 -0
- package/.medusa/server/src/workflows/upsert-image.js +10 -0
- package/.medusa/server/src/workflows/upsert-inventory-level.d.ts +8 -0
- package/.medusa/server/src/workflows/upsert-inventory-level.d.ts.map +1 -0
- package/.medusa/server/src/workflows/upsert-inventory-level.js +10 -0
- package/.medusa/server/src/workflows/upsert-inventory.d.ts +8 -0
- package/.medusa/server/src/workflows/upsert-inventory.d.ts.map +1 -0
- package/.medusa/server/src/workflows/upsert-inventory.js +10 -0
- package/.medusa/server/src/workflows/upsert-option-value.d.ts +8 -0
- package/.medusa/server/src/workflows/upsert-option-value.d.ts.map +1 -0
- package/.medusa/server/src/workflows/upsert-option-value.js +10 -0
- package/.medusa/server/src/workflows/upsert-option.d.ts +8 -0
- package/.medusa/server/src/workflows/upsert-option.d.ts.map +1 -0
- package/.medusa/server/src/workflows/upsert-option.js +10 -0
- package/.medusa/server/src/workflows/upsert-price.d.ts +8 -0
- package/.medusa/server/src/workflows/upsert-price.d.ts.map +1 -0
- package/.medusa/server/src/workflows/upsert-price.js +10 -0
- package/.medusa/server/src/workflows/upsert-product.d.ts +8 -0
- package/.medusa/server/src/workflows/upsert-product.d.ts.map +1 -0
- package/.medusa/server/src/workflows/upsert-product.js +12 -0
- package/.medusa/server/src/workflows/upsert-tag.d.ts +8 -0
- package/.medusa/server/src/workflows/upsert-tag.d.ts.map +1 -0
- package/.medusa/server/src/workflows/upsert-tag.js +10 -0
- package/.medusa/server/src/workflows/upsert-type.d.ts +8 -0
- package/.medusa/server/src/workflows/upsert-type.d.ts.map +1 -0
- package/.medusa/server/src/workflows/upsert-type.js +10 -0
- package/.medusa/server/src/workflows/upsert-variant.d.ts +8 -0
- package/.medusa/server/src/workflows/upsert-variant.d.ts.map +1 -0
- package/.medusa/server/src/workflows/upsert-variant.js +12 -0
- package/README.md +511 -511
- package/package.json +76 -76
- package/.medusa/server/src/subscribers/meilisearch-category-created.d.ts +0 -6
- package/.medusa/server/src/subscribers/meilisearch-category-created.d.ts.map +0 -1
- package/.medusa/server/src/subscribers/meilisearch-category-created.js +0 -14
- package/.medusa/server/src/subscribers/meilisearch-category-deleted.d.ts +0 -6
- package/.medusa/server/src/subscribers/meilisearch-category-deleted.d.ts.map +0 -1
- package/.medusa/server/src/subscribers/meilisearch-category-deleted.js +0 -14
- package/.medusa/server/src/subscribers/meilisearch-category-updated.d.ts +0 -6
- package/.medusa/server/src/subscribers/meilisearch-category-updated.d.ts.map +0 -1
- package/.medusa/server/src/subscribers/meilisearch-category-updated.js +0 -14
- package/.medusa/server/src/subscribers/meilisearch-product-created.d.ts +0 -6
- package/.medusa/server/src/subscribers/meilisearch-product-created.d.ts.map +0 -1
- package/.medusa/server/src/subscribers/meilisearch-product-created.js +0 -19
- package/.medusa/server/src/subscribers/meilisearch-product-deleted.d.ts +0 -6
- package/.medusa/server/src/subscribers/meilisearch-product-deleted.d.ts.map +0 -1
- package/.medusa/server/src/subscribers/meilisearch-product-deleted.js +0 -19
- package/.medusa/server/src/subscribers/meilisearch-product-updated.d.ts +0 -6
- package/.medusa/server/src/subscribers/meilisearch-product-updated.d.ts.map +0 -1
- package/.medusa/server/src/subscribers/meilisearch-product-updated.js +0 -19
- package/.medusa/server/src/workflows/category-created.d.ts +0 -7
- package/.medusa/server/src/workflows/category-created.d.ts.map +0 -1
- package/.medusa/server/src/workflows/category-created.js +0 -10
- package/.medusa/server/src/workflows/category-deleted.d.ts +0 -7
- package/.medusa/server/src/workflows/category-deleted.d.ts.map +0 -1
- package/.medusa/server/src/workflows/category-deleted.js +0 -10
- package/.medusa/server/src/workflows/category-updated.d.ts +0 -7
- package/.medusa/server/src/workflows/category-updated.d.ts.map +0 -1
- package/.medusa/server/src/workflows/category-updated.js +0 -10
- package/.medusa/server/src/workflows/product-created.d.ts +0 -8
- package/.medusa/server/src/workflows/product-created.d.ts.map +0 -1
- package/.medusa/server/src/workflows/product-created.js +0 -12
- package/.medusa/server/src/workflows/product-deleted.d.ts +0 -6
- package/.medusa/server/src/workflows/product-deleted.d.ts.map +0 -1
- package/.medusa/server/src/workflows/product-deleted.js +0 -10
- package/.medusa/server/src/workflows/product-updated.d.ts +0 -8
- package/.medusa/server/src/workflows/product-updated.d.ts.map +0 -1
- package/.medusa/server/src/workflows/product-updated.js +0 -12
package/README.md
CHANGED
|
@@ -1,511 +1,511 @@
|
|
|
1
|
-
# MedusaJS v2 MeiliSearch Plugin with i18n Support
|
|
2
|
-
|
|
3
|
-
This plugin integrates MeiliSearch with your Medusa e-commerce store and adds support for internationalization (i18n) of your product catalog.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- Full-text search for your Medusa store
|
|
8
|
-
- Real-time indexing
|
|
9
|
-
- Typo-tolerance
|
|
10
|
-
- Faceted search
|
|
11
|
-
- Support for both products and categories
|
|
12
|
-
- Internationalization (i18n) support with multiple strategies:
|
|
13
|
-
1. Separate index per language
|
|
14
|
-
2. Language-specific fields with suffix
|
|
15
|
-
- Flexible translation configuration
|
|
16
|
-
- Custom field transformations
|
|
17
|
-
- Automatic field detection
|
|
18
|
-
|
|
19
|
-
## Installation
|
|
20
|
-
|
|
21
|
-
Run the following command to install the plugin with **npm**:
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
npm install --save @rokmohar/medusa-plugin-meilisearch
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
Or with **yarn**:
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
yarn add @rokmohar/medusa-plugin-meilisearch
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
### Upgrade to v1.0
|
|
34
|
-
|
|
35
|
-
_This step is required only if you are upgrading from previous version to v1.0._
|
|
36
|
-
|
|
37
|
-
- The plugin now supports new MedusaJS plugin system.
|
|
38
|
-
- Subscribers are included in the plugin.
|
|
39
|
-
- You don't need custom subscribers anymore, you can remove them.
|
|
40
|
-
|
|
41
|
-
## ⚠️ MedusaJS v2.4.0 or newer
|
|
42
|
-
|
|
43
|
-
This plugin is only for MedusaJS v2.4.0 or newer.
|
|
44
|
-
|
|
45
|
-
If you are using MedusaJS v2.3.1 or older, please use the [older version of this plugin](https://github.com/rokmohar/medusa-plugin-meilisearch/tree/v0.2.1).
|
|
46
|
-
|
|
47
|
-
## Configuration
|
|
48
|
-
|
|
49
|
-
Add the plugin to your `medusa-config.ts` file:
|
|
50
|
-
|
|
51
|
-
```js
|
|
52
|
-
import { loadEnv, defineConfig } from '@medusajs/framework/utils'
|
|
53
|
-
import { MeilisearchPluginOptions } from '@rokmohar/medusa-plugin-meilisearch'
|
|
54
|
-
|
|
55
|
-
loadEnv(process.env.NODE_ENV || 'development', process.cwd())
|
|
56
|
-
|
|
57
|
-
module.exports = defineConfig({
|
|
58
|
-
// ... other config
|
|
59
|
-
plugins: [
|
|
60
|
-
// ... other plugins
|
|
61
|
-
{
|
|
62
|
-
resolve: '@rokmohar/medusa-plugin-meilisearch',
|
|
63
|
-
options: {
|
|
64
|
-
config: {
|
|
65
|
-
host: process.env.MEILISEARCH_HOST ?? '',
|
|
66
|
-
apiKey: process.env.MEILISEARCH_API_KEY ?? '',
|
|
67
|
-
},
|
|
68
|
-
settings: {
|
|
69
|
-
// The key is used as the index name in Meilisearch
|
|
70
|
-
products: {
|
|
71
|
-
// Required: Index type
|
|
72
|
-
type: 'products',
|
|
73
|
-
// Optional: Whether the index is enabled. When disabled:
|
|
74
|
-
// - Index won't be created or updated
|
|
75
|
-
// - Documents won't be added or removed
|
|
76
|
-
// - Index won't be included in searches
|
|
77
|
-
// - All operations will be silently skipped
|
|
78
|
-
enabled: true,
|
|
79
|
-
// Optional: Specify which fields to include in the index
|
|
80
|
-
// If not specified, all fields will be included
|
|
81
|
-
fields: ['id', 'title', 'description', 'handle', 'variant_sku', 'thumbnail'],
|
|
82
|
-
indexSettings: {
|
|
83
|
-
searchableAttributes: ['title', 'description', 'variant_sku'],
|
|
84
|
-
displayedAttributes: ['id', 'handle', 'title', 'description', 'variant_sku', 'thumbnail'],
|
|
85
|
-
filterableAttributes: ['id', 'handle'],
|
|
86
|
-
},
|
|
87
|
-
primaryKey: 'id',
|
|
88
|
-
// Create your own transformer
|
|
89
|
-
/*transformer: (product) => ({
|
|
90
|
-
id: product.id,
|
|
91
|
-
// other attributes...
|
|
92
|
-
}),*/
|
|
93
|
-
},
|
|
94
|
-
categories: {
|
|
95
|
-
// Required: Index type
|
|
96
|
-
type: 'categories',
|
|
97
|
-
// Optional: Whether the index is enabled
|
|
98
|
-
enabled: true,
|
|
99
|
-
// Optional: Specify which fields to include in the index
|
|
100
|
-
// If not specified, all fields will be included
|
|
101
|
-
fields: ['id', 'name', 'description', 'handle', 'is_active', 'parent_id'],
|
|
102
|
-
indexSettings: {
|
|
103
|
-
searchableAttributes: ['name', 'description'],
|
|
104
|
-
displayedAttributes: ['id', 'name', 'description', 'handle', 'is_active', 'parent_id'],
|
|
105
|
-
filterableAttributes: ['id', 'handle', 'is_active', 'parent_id'],
|
|
106
|
-
},
|
|
107
|
-
primaryKey: 'id',
|
|
108
|
-
// Create your own transformer
|
|
109
|
-
/*transformer: (category) => ({
|
|
110
|
-
id: category.id,
|
|
111
|
-
name: category.name,
|
|
112
|
-
// other attributes...
|
|
113
|
-
}),*/
|
|
114
|
-
},
|
|
115
|
-
},
|
|
116
|
-
i18n: {
|
|
117
|
-
// Choose one of the following strategies:
|
|
118
|
-
|
|
119
|
-
// 1. Separate index per language
|
|
120
|
-
// strategy: 'separate-index',
|
|
121
|
-
// languages: ['en', 'fr', 'de'],
|
|
122
|
-
// defaultLanguage: 'en',
|
|
123
|
-
|
|
124
|
-
// 2. Language-specific fields with suffix
|
|
125
|
-
strategy: 'field-suffix',
|
|
126
|
-
languages: ['en', 'fr', 'de'],
|
|
127
|
-
defaultLanguage: 'en',
|
|
128
|
-
translatableFields: ['title', 'description'],
|
|
129
|
-
},
|
|
130
|
-
} satisfies MeilisearchPluginOptions,
|
|
131
|
-
},
|
|
132
|
-
],
|
|
133
|
-
})
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
### ⚠️ Worker Mode Considerations
|
|
137
|
-
|
|
138
|
-
> **Important:** Product events and background tasks will **not work** if your Medusa instance is running in `server` mode, because the server instance does **not** process subscribers or background jobs.
|
|
139
|
-
|
|
140
|
-
Depending on your setup:
|
|
141
|
-
|
|
142
|
-
- **Monolithic architecture** (only one backend instance):
|
|
143
|
-
Ensure you **do not set** the `MEDUSA_WORKER_MODE` or `WORKER_MODE` environment variable. By default, Medusa will use `shared` mode, which supports both background processing and serving HTTP requests from the same instance.
|
|
144
|
-
|
|
145
|
-
- **Split architecture** (separate server and worker instances):
|
|
146
|
-
Follow the [official Medusa documentation on worker mode](https://docs.medusajs.com/learn/production/worker-mode#content).
|
|
147
|
-
In this case, you **must add this plugin in the worker instance**, as the server instance does not handle event subscribers or background tasks.
|
|
148
|
-
|
|
149
|
-
## i18n Configuration
|
|
150
|
-
|
|
151
|
-
The plugin supports two main strategies for handling translations, with flexible configuration options for each.
|
|
152
|
-
|
|
153
|
-
### Basic Configuration
|
|
154
|
-
|
|
155
|
-
```typescript
|
|
156
|
-
{
|
|
157
|
-
i18n: {
|
|
158
|
-
// Choose strategy: 'separate-index' or 'field-suffix'
|
|
159
|
-
strategy: 'field-suffix',
|
|
160
|
-
// List of supported languages
|
|
161
|
-
languages: ['en', 'fr', 'de'],
|
|
162
|
-
// Default language to fall back to
|
|
163
|
-
defaultLanguage: 'en',
|
|
164
|
-
// Optional: List of translatable fields
|
|
165
|
-
translatableFields: ['title', 'description', 'handle']
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
### Advanced Field Configuration
|
|
171
|
-
|
|
172
|
-
You can provide detailed configuration for each translatable field:
|
|
173
|
-
|
|
174
|
-
```typescript
|
|
175
|
-
{
|
|
176
|
-
i18n: {
|
|
177
|
-
strategy: 'field-suffix',
|
|
178
|
-
languages: ['en', 'fr', 'de'],
|
|
179
|
-
defaultLanguage: 'en',
|
|
180
|
-
translatableFields: [
|
|
181
|
-
// Simple field name
|
|
182
|
-
'title',
|
|
183
|
-
|
|
184
|
-
// Field with different target name
|
|
185
|
-
{
|
|
186
|
-
source: 'description',
|
|
187
|
-
target: 'content' // Will be indexed as content_en, content_fr, etc.
|
|
188
|
-
},
|
|
189
|
-
|
|
190
|
-
// Field with transformation
|
|
191
|
-
{
|
|
192
|
-
source: 'handle',
|
|
193
|
-
transform: (value) => value.toLowerCase().replace(/\s+/g, '-')
|
|
194
|
-
}
|
|
195
|
-
]
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
### Custom Translation Transformer
|
|
201
|
-
|
|
202
|
-
The plugin provides a flexible way to transform your products with custom translations. Instead of relying on specific storage formats, you can provide translations directly to the transformer:
|
|
203
|
-
|
|
204
|
-
```typescript
|
|
205
|
-
import { transformProduct } from '@rokmohar/medusa-plugin-meilisearch'
|
|
206
|
-
|
|
207
|
-
const getProductTranslations = async (productId: string) => {
|
|
208
|
-
// Example: fetch from your translation service/database
|
|
209
|
-
return {
|
|
210
|
-
title: [
|
|
211
|
-
{ language_code: 'en', value: 'Blue T-Shirt' },
|
|
212
|
-
{ language_code: 'fr', value: 'T-Shirt Bleu' },
|
|
213
|
-
],
|
|
214
|
-
description: [
|
|
215
|
-
{ language_code: 'en', value: 'A comfortable blue t-shirt' },
|
|
216
|
-
{ language_code: 'fr', value: 'Un t-shirt bleu confortable' },
|
|
217
|
-
],
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Example usage in your custom transformer
|
|
222
|
-
const customTransformer = async (product, options) => {
|
|
223
|
-
const translations = await getProductTranslations(product.id)
|
|
224
|
-
|
|
225
|
-
return transformProduct(product, {
|
|
226
|
-
...options,
|
|
227
|
-
translations,
|
|
228
|
-
})
|
|
229
|
-
}
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
### Integration with Tolgee
|
|
233
|
-
|
|
234
|
-
For production environments, you'll often want to integrate with external translation management platforms. Here's an example of integrating with [Tolgee](https://tolgee.io/), a popular translations management platform:
|
|
235
|
-
|
|
236
|
-
```typescript
|
|
237
|
-
import { default as axios } from 'axios'
|
|
238
|
-
import { logger } from '@medusajs/framework'
|
|
239
|
-
import { TranslationMap } from '@rokmohar/medusa-plugin-meilisearch'
|
|
240
|
-
|
|
241
|
-
type TranslationsHttpResponse = {
|
|
242
|
-
[lang: string]: {
|
|
243
|
-
[id: string]: Record<string, string>
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const options = {
|
|
248
|
-
apiKey: process.env.TOLGEE_API_KEY ?? '',
|
|
249
|
-
baseURL: process.env.TOLGEE_API_URL ?? '',
|
|
250
|
-
projectId: process.env.TOLGEE_PROJECT_ID ?? '',
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
const httpClient = axios.create({
|
|
254
|
-
baseURL: `${options.baseURL}/v2/projects/${options.projectId}`,
|
|
255
|
-
headers: {
|
|
256
|
-
Accept: 'application/json',
|
|
257
|
-
'X-API-Key': options.apiKey,
|
|
258
|
-
},
|
|
259
|
-
maxBodyLength: Infinity,
|
|
260
|
-
})
|
|
261
|
-
|
|
262
|
-
export const getTranslations = async (id: string, langs: string[]) => {
|
|
263
|
-
const translations: TranslationMap = {}
|
|
264
|
-
|
|
265
|
-
try {
|
|
266
|
-
const response = await httpClient.get<TranslationsHttpResponse>(`/translations/${langs.join(',')}?ns=${id}`)
|
|
267
|
-
Object.entries(response.data).forEach(([language_code, { [id]: values }]) => {
|
|
268
|
-
Object.entries(values).forEach(([key, value]) => {
|
|
269
|
-
if (!(key in translations)) {
|
|
270
|
-
translations[key] = []
|
|
271
|
-
}
|
|
272
|
-
translations[key].push({ language_code, value })
|
|
273
|
-
})
|
|
274
|
-
})
|
|
275
|
-
} catch (e) {
|
|
276
|
-
logger.error(e)
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
return translations
|
|
280
|
-
}
|
|
281
|
-
```
|
|
282
|
-
|
|
283
|
-
Usage in your transformer configuration:
|
|
284
|
-
|
|
285
|
-
```typescript
|
|
286
|
-
{
|
|
287
|
-
settings: {
|
|
288
|
-
products: {
|
|
289
|
-
type: 'products',
|
|
290
|
-
// ... other config
|
|
291
|
-
transformer: async (product, defaultTransformer, options) => {
|
|
292
|
-
const translations = await getTranslations(product.id, ['sl', 'en'])
|
|
293
|
-
return defaultTransformer(product, {
|
|
294
|
-
...options,
|
|
295
|
-
translations,
|
|
296
|
-
includeAllTranslations: true
|
|
297
|
-
})
|
|
298
|
-
},
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
This integration fetches translations from Tolgee's API and transforms them into the format expected by this plugin. For complete storefront translation management, consider using the [medusa-plugin-tolgee](https://github.com/SteelRazor47/medusa-plugin-tolgee) by SteelRazor47, which provides comprehensive translation management features including admin widgets.
|
|
305
|
-
|
|
306
|
-
## i18n Strategies
|
|
307
|
-
|
|
308
|
-
### 1. Separate Index per Language
|
|
309
|
-
|
|
310
|
-
This strategy creates a separate MeiliSearch index for each language. For example, if your base index is named "products", it will create:
|
|
311
|
-
|
|
312
|
-
- products_en
|
|
313
|
-
- products_fr
|
|
314
|
-
- products_de
|
|
315
|
-
|
|
316
|
-
Benefits:
|
|
317
|
-
|
|
318
|
-
- Better performance for language-specific searches
|
|
319
|
-
- Language-specific settings and ranking rules
|
|
320
|
-
- Cleaner index structure
|
|
321
|
-
|
|
322
|
-
### 2. Language-specific Fields with Suffix
|
|
323
|
-
|
|
324
|
-
This strategy adds language suffixes to translatable fields in the same index. For example:
|
|
325
|
-
|
|
326
|
-
- title_en, title_fr, title_de
|
|
327
|
-
- description_en, description_fr, description_de
|
|
328
|
-
|
|
329
|
-
Benefits:
|
|
330
|
-
|
|
331
|
-
- Single index to maintain
|
|
332
|
-
- Ability to search across all languages at once
|
|
333
|
-
- Smaller storage requirements
|
|
334
|
-
|
|
335
|
-
## Custom Translatable Fields
|
|
336
|
-
|
|
337
|
-
If no translatable fields are specified and using the field-suffix strategy, the plugin will automatically detect string fields as translatable. You can override this by explicitly specifying the fields:
|
|
338
|
-
|
|
339
|
-
```typescript
|
|
340
|
-
{
|
|
341
|
-
i18n: {
|
|
342
|
-
strategy: 'field-suffix',
|
|
343
|
-
languages: ['en', 'fr'],
|
|
344
|
-
defaultLanguage: 'en',
|
|
345
|
-
// Only these fields will be translatable
|
|
346
|
-
translatableFields: ['title', 'description']
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
```
|
|
350
|
-
|
|
351
|
-
## Product API Endpoints
|
|
352
|
-
|
|
353
|
-
### Search for Product Hits
|
|
354
|
-
|
|
355
|
-
```http
|
|
356
|
-
GET /store/meilisearch/products-hits
|
|
357
|
-
```
|
|
358
|
-
|
|
359
|
-
Query Parameters:
|
|
360
|
-
|
|
361
|
-
- `query`: Search query string
|
|
362
|
-
- `limit`: (Optional) Limit results from search
|
|
363
|
-
- `offset`: (Optional) Offset results from search
|
|
364
|
-
- `language`: (Optional) Language code
|
|
365
|
-
- `semanticSearch` - Enable AI-powered semantic search (boolean)
|
|
366
|
-
- `semanticRatio` - Semantic vs keyword search ratio (0-1)
|
|
367
|
-
|
|
368
|
-
### Search for Products
|
|
369
|
-
|
|
370
|
-
```http
|
|
371
|
-
GET /store/meilisearch/products
|
|
372
|
-
```
|
|
373
|
-
|
|
374
|
-
Query Parameters:
|
|
375
|
-
|
|
376
|
-
- `fields` - MedusaJS fields expression
|
|
377
|
-
- `limit`: (Optional) Limit results from search
|
|
378
|
-
- `offset`: (Optional) Offset results from search
|
|
379
|
-
- `region_id`: (Optional, but required for `calculated_price`) Current region ID
|
|
380
|
-
- `currency_code`: (Optional, but required for `calculated_price`) Current currency code
|
|
381
|
-
- `query`: Search query string
|
|
382
|
-
- `language`: (Optional) Language code
|
|
383
|
-
- `semanticSearch` - Enable AI-powered semantic search (boolean)
|
|
384
|
-
- `semanticRatio` - Semantic vs keyword search ratio (0-1)
|
|
385
|
-
|
|
386
|
-
## Category Support
|
|
387
|
-
|
|
388
|
-
This plugin provides full support for MedusaJS v2 categories, including:
|
|
389
|
-
|
|
390
|
-
- Real-time indexing of category changes
|
|
391
|
-
- i18n support for category names and descriptions
|
|
392
|
-
- Hierarchical category structure support with parent-child relationships
|
|
393
|
-
- Custom category field transformations
|
|
394
|
-
|
|
395
|
-
### Category Configuration Example
|
|
396
|
-
|
|
397
|
-
```typescript
|
|
398
|
-
{
|
|
399
|
-
settings: {
|
|
400
|
-
categories: {
|
|
401
|
-
type: 'categories',
|
|
402
|
-
enabled: true,
|
|
403
|
-
fields: ['id', 'name', 'description', 'handle', 'is_active', 'parent_id'],
|
|
404
|
-
indexSettings: {
|
|
405
|
-
searchableAttributes: ['name', 'description'],
|
|
406
|
-
displayedAttributes: ['id', 'name', 'description', 'handle', 'is_active', 'parent_id'],
|
|
407
|
-
filterableAttributes: ['id', 'handle', 'is_active', 'parent_id'],
|
|
408
|
-
},
|
|
409
|
-
primaryKey: 'id',
|
|
410
|
-
},
|
|
411
|
-
},
|
|
412
|
-
i18n: {
|
|
413
|
-
strategy: 'field-suffix',
|
|
414
|
-
languages: ['en', 'fr', 'de'],
|
|
415
|
-
defaultLanguage: 'en',
|
|
416
|
-
translatableFields: ['name', 'description'], // Category-specific translatable fields
|
|
417
|
-
},
|
|
418
|
-
}
|
|
419
|
-
```
|
|
420
|
-
|
|
421
|
-
### Category API Endpoints
|
|
422
|
-
|
|
423
|
-
### Search for Category Hits
|
|
424
|
-
|
|
425
|
-
```http
|
|
426
|
-
GET /store/meilisearch/categories-hits
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
Query Parameters:
|
|
430
|
-
|
|
431
|
-
- `query`: Search query string
|
|
432
|
-
- `limit`: (Optional) Limit results from search
|
|
433
|
-
- `offset`: (Optional) Offset results from search
|
|
434
|
-
- `language`: (Optional) Language code
|
|
435
|
-
- `semanticSearch` - Enable AI-powered semantic search (boolean)
|
|
436
|
-
- `semanticRatio` - Semantic vs keyword search ratio (0-1)
|
|
437
|
-
|
|
438
|
-
### Search for Categories
|
|
439
|
-
|
|
440
|
-
```http
|
|
441
|
-
GET /store/meilisearch/categories
|
|
442
|
-
```
|
|
443
|
-
|
|
444
|
-
Query Parameters:
|
|
445
|
-
|
|
446
|
-
- `fields` - MedusaJS fields expression
|
|
447
|
-
- `limit`: (Optional) Limit results from search
|
|
448
|
-
- `offset`: (Optional) Offset results from search
|
|
449
|
-
- `query`: Search query string
|
|
450
|
-
- `language`: (Optional) Language code
|
|
451
|
-
- `semanticSearch` - Enable AI-powered semantic search (boolean)
|
|
452
|
-
- `semanticRatio` - Semantic vs keyword search ratio (0-1)
|
|
453
|
-
|
|
454
|
-
## AI-Powered Semantic Search
|
|
455
|
-
|
|
456
|
-
This plugin supports AI-powered semantic search using vector embeddings. See [docs/semantic-search.md](docs/semantic-search.md) for detailed configuration and usage instructions.
|
|
457
|
-
|
|
458
|
-
## ENV variables
|
|
459
|
-
|
|
460
|
-
Add the environment variables to your `.env` and `.env.template` file:
|
|
461
|
-
|
|
462
|
-
```env
|
|
463
|
-
# ... others vars
|
|
464
|
-
MEILISEARCH_HOST=
|
|
465
|
-
MEILISEARCH_API_KEY=
|
|
466
|
-
```
|
|
467
|
-
|
|
468
|
-
If you want to use with the `docker-compose` from this README, use the following values:
|
|
469
|
-
|
|
470
|
-
```env
|
|
471
|
-
# ... others vars
|
|
472
|
-
MEILISEARCH_HOST=http://127.0.0.1:7700
|
|
473
|
-
MEILISEARCH_API_KEY=ms
|
|
474
|
-
```
|
|
475
|
-
|
|
476
|
-
## docker-compose
|
|
477
|
-
|
|
478
|
-
You can add the following configuration for Meilisearch to your `docker-compose.yml`:
|
|
479
|
-
|
|
480
|
-
```yml
|
|
481
|
-
services:
|
|
482
|
-
# ... other services
|
|
483
|
-
|
|
484
|
-
meilisearch:
|
|
485
|
-
image: getmeili/meilisearch:latest
|
|
486
|
-
ports:
|
|
487
|
-
- '7700:7700'
|
|
488
|
-
volumes:
|
|
489
|
-
- ~/data.ms:/data.ms
|
|
490
|
-
environment:
|
|
491
|
-
- MEILI_MASTER_KEY=ms
|
|
492
|
-
healthcheck:
|
|
493
|
-
test: ['CMD', 'curl', '-f', 'http://localhost:7700']
|
|
494
|
-
interval: 10s
|
|
495
|
-
timeout: 5s
|
|
496
|
-
retries: 5
|
|
497
|
-
```
|
|
498
|
-
|
|
499
|
-
## Add search to Medusa NextJS starter
|
|
500
|
-
|
|
501
|
-
You can find instructions on how to add search to a Medusa NextJS starter inside the [nextjs](nextjs) folder.
|
|
502
|
-
|
|
503
|
-
## FAQ
|
|
504
|
-
|
|
505
|
-
- [How do I include product categories and tags in my search index?](docs/faq-product-categories-and-tags.md)
|
|
506
|
-
- [How do I include product variant prices (min_price, max_price) in my search index?](docs/faq-product-variant-prices.md)
|
|
507
|
-
- [How do I include prices in the search response from the search endpoint?](docs/faq-product-search-prices.md)
|
|
508
|
-
|
|
509
|
-
## Contributing
|
|
510
|
-
|
|
511
|
-
Feel free to open issues and pull requests!
|
|
1
|
+
# MedusaJS v2 MeiliSearch Plugin with i18n Support
|
|
2
|
+
|
|
3
|
+
This plugin integrates MeiliSearch with your Medusa e-commerce store and adds support for internationalization (i18n) of your product catalog.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Full-text search for your Medusa store
|
|
8
|
+
- Real-time indexing
|
|
9
|
+
- Typo-tolerance
|
|
10
|
+
- Faceted search
|
|
11
|
+
- Support for both products and categories
|
|
12
|
+
- Internationalization (i18n) support with multiple strategies:
|
|
13
|
+
1. Separate index per language
|
|
14
|
+
2. Language-specific fields with suffix
|
|
15
|
+
- Flexible translation configuration
|
|
16
|
+
- Custom field transformations
|
|
17
|
+
- Automatic field detection
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
Run the following command to install the plugin with **npm**:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install --save @rokmohar/medusa-plugin-meilisearch
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Or with **yarn**:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
yarn add @rokmohar/medusa-plugin-meilisearch
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Upgrade to v1.0
|
|
34
|
+
|
|
35
|
+
_This step is required only if you are upgrading from previous version to v1.0._
|
|
36
|
+
|
|
37
|
+
- The plugin now supports new MedusaJS plugin system.
|
|
38
|
+
- Subscribers are included in the plugin.
|
|
39
|
+
- You don't need custom subscribers anymore, you can remove them.
|
|
40
|
+
|
|
41
|
+
## ⚠️ MedusaJS v2.4.0 or newer
|
|
42
|
+
|
|
43
|
+
This plugin is only for MedusaJS v2.4.0 or newer.
|
|
44
|
+
|
|
45
|
+
If you are using MedusaJS v2.3.1 or older, please use the [older version of this plugin](https://github.com/rokmohar/medusa-plugin-meilisearch/tree/v0.2.1).
|
|
46
|
+
|
|
47
|
+
## Configuration
|
|
48
|
+
|
|
49
|
+
Add the plugin to your `medusa-config.ts` file:
|
|
50
|
+
|
|
51
|
+
```js
|
|
52
|
+
import { loadEnv, defineConfig } from '@medusajs/framework/utils'
|
|
53
|
+
import { MeilisearchPluginOptions } from '@rokmohar/medusa-plugin-meilisearch'
|
|
54
|
+
|
|
55
|
+
loadEnv(process.env.NODE_ENV || 'development', process.cwd())
|
|
56
|
+
|
|
57
|
+
module.exports = defineConfig({
|
|
58
|
+
// ... other config
|
|
59
|
+
plugins: [
|
|
60
|
+
// ... other plugins
|
|
61
|
+
{
|
|
62
|
+
resolve: '@rokmohar/medusa-plugin-meilisearch',
|
|
63
|
+
options: {
|
|
64
|
+
config: {
|
|
65
|
+
host: process.env.MEILISEARCH_HOST ?? '',
|
|
66
|
+
apiKey: process.env.MEILISEARCH_API_KEY ?? '',
|
|
67
|
+
},
|
|
68
|
+
settings: {
|
|
69
|
+
// The key is used as the index name in Meilisearch
|
|
70
|
+
products: {
|
|
71
|
+
// Required: Index type
|
|
72
|
+
type: 'products',
|
|
73
|
+
// Optional: Whether the index is enabled. When disabled:
|
|
74
|
+
// - Index won't be created or updated
|
|
75
|
+
// - Documents won't be added or removed
|
|
76
|
+
// - Index won't be included in searches
|
|
77
|
+
// - All operations will be silently skipped
|
|
78
|
+
enabled: true,
|
|
79
|
+
// Optional: Specify which fields to include in the index
|
|
80
|
+
// If not specified, all fields will be included
|
|
81
|
+
fields: ['id', 'title', 'description', 'handle', 'variant_sku', 'thumbnail'],
|
|
82
|
+
indexSettings: {
|
|
83
|
+
searchableAttributes: ['title', 'description', 'variant_sku'],
|
|
84
|
+
displayedAttributes: ['id', 'handle', 'title', 'description', 'variant_sku', 'thumbnail'],
|
|
85
|
+
filterableAttributes: ['id', 'handle'],
|
|
86
|
+
},
|
|
87
|
+
primaryKey: 'id',
|
|
88
|
+
// Create your own transformer
|
|
89
|
+
/*transformer: (product) => ({
|
|
90
|
+
id: product.id,
|
|
91
|
+
// other attributes...
|
|
92
|
+
}),*/
|
|
93
|
+
},
|
|
94
|
+
categories: {
|
|
95
|
+
// Required: Index type
|
|
96
|
+
type: 'categories',
|
|
97
|
+
// Optional: Whether the index is enabled
|
|
98
|
+
enabled: true,
|
|
99
|
+
// Optional: Specify which fields to include in the index
|
|
100
|
+
// If not specified, all fields will be included
|
|
101
|
+
fields: ['id', 'name', 'description', 'handle', 'is_active', 'parent_id'],
|
|
102
|
+
indexSettings: {
|
|
103
|
+
searchableAttributes: ['name', 'description'],
|
|
104
|
+
displayedAttributes: ['id', 'name', 'description', 'handle', 'is_active', 'parent_id'],
|
|
105
|
+
filterableAttributes: ['id', 'handle', 'is_active', 'parent_id'],
|
|
106
|
+
},
|
|
107
|
+
primaryKey: 'id',
|
|
108
|
+
// Create your own transformer
|
|
109
|
+
/*transformer: (category) => ({
|
|
110
|
+
id: category.id,
|
|
111
|
+
name: category.name,
|
|
112
|
+
// other attributes...
|
|
113
|
+
}),*/
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
i18n: {
|
|
117
|
+
// Choose one of the following strategies:
|
|
118
|
+
|
|
119
|
+
// 1. Separate index per language
|
|
120
|
+
// strategy: 'separate-index',
|
|
121
|
+
// languages: ['en', 'fr', 'de'],
|
|
122
|
+
// defaultLanguage: 'en',
|
|
123
|
+
|
|
124
|
+
// 2. Language-specific fields with suffix
|
|
125
|
+
strategy: 'field-suffix',
|
|
126
|
+
languages: ['en', 'fr', 'de'],
|
|
127
|
+
defaultLanguage: 'en',
|
|
128
|
+
translatableFields: ['title', 'description'],
|
|
129
|
+
},
|
|
130
|
+
} satisfies MeilisearchPluginOptions,
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
})
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### ⚠️ Worker Mode Considerations
|
|
137
|
+
|
|
138
|
+
> **Important:** Product events and background tasks will **not work** if your Medusa instance is running in `server` mode, because the server instance does **not** process subscribers or background jobs.
|
|
139
|
+
|
|
140
|
+
Depending on your setup:
|
|
141
|
+
|
|
142
|
+
- **Monolithic architecture** (only one backend instance):
|
|
143
|
+
Ensure you **do not set** the `MEDUSA_WORKER_MODE` or `WORKER_MODE` environment variable. By default, Medusa will use `shared` mode, which supports both background processing and serving HTTP requests from the same instance.
|
|
144
|
+
|
|
145
|
+
- **Split architecture** (separate server and worker instances):
|
|
146
|
+
Follow the [official Medusa documentation on worker mode](https://docs.medusajs.com/learn/production/worker-mode#content).
|
|
147
|
+
In this case, you **must add this plugin in the worker instance**, as the server instance does not handle event subscribers or background tasks.
|
|
148
|
+
|
|
149
|
+
## i18n Configuration
|
|
150
|
+
|
|
151
|
+
The plugin supports two main strategies for handling translations, with flexible configuration options for each.
|
|
152
|
+
|
|
153
|
+
### Basic Configuration
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
{
|
|
157
|
+
i18n: {
|
|
158
|
+
// Choose strategy: 'separate-index' or 'field-suffix'
|
|
159
|
+
strategy: 'field-suffix',
|
|
160
|
+
// List of supported languages
|
|
161
|
+
languages: ['en', 'fr', 'de'],
|
|
162
|
+
// Default language to fall back to
|
|
163
|
+
defaultLanguage: 'en',
|
|
164
|
+
// Optional: List of translatable fields
|
|
165
|
+
translatableFields: ['title', 'description', 'handle']
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Advanced Field Configuration
|
|
171
|
+
|
|
172
|
+
You can provide detailed configuration for each translatable field:
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
{
|
|
176
|
+
i18n: {
|
|
177
|
+
strategy: 'field-suffix',
|
|
178
|
+
languages: ['en', 'fr', 'de'],
|
|
179
|
+
defaultLanguage: 'en',
|
|
180
|
+
translatableFields: [
|
|
181
|
+
// Simple field name
|
|
182
|
+
'title',
|
|
183
|
+
|
|
184
|
+
// Field with different target name
|
|
185
|
+
{
|
|
186
|
+
source: 'description',
|
|
187
|
+
target: 'content' // Will be indexed as content_en, content_fr, etc.
|
|
188
|
+
},
|
|
189
|
+
|
|
190
|
+
// Field with transformation
|
|
191
|
+
{
|
|
192
|
+
source: 'handle',
|
|
193
|
+
transform: (value) => value.toLowerCase().replace(/\s+/g, '-')
|
|
194
|
+
}
|
|
195
|
+
]
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Custom Translation Transformer
|
|
201
|
+
|
|
202
|
+
The plugin provides a flexible way to transform your products with custom translations. Instead of relying on specific storage formats, you can provide translations directly to the transformer:
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
import { transformProduct } from '@rokmohar/medusa-plugin-meilisearch'
|
|
206
|
+
|
|
207
|
+
const getProductTranslations = async (productId: string) => {
|
|
208
|
+
// Example: fetch from your translation service/database
|
|
209
|
+
return {
|
|
210
|
+
title: [
|
|
211
|
+
{ language_code: 'en', value: 'Blue T-Shirt' },
|
|
212
|
+
{ language_code: 'fr', value: 'T-Shirt Bleu' },
|
|
213
|
+
],
|
|
214
|
+
description: [
|
|
215
|
+
{ language_code: 'en', value: 'A comfortable blue t-shirt' },
|
|
216
|
+
{ language_code: 'fr', value: 'Un t-shirt bleu confortable' },
|
|
217
|
+
],
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Example usage in your custom transformer
|
|
222
|
+
const customTransformer = async (product, options) => {
|
|
223
|
+
const translations = await getProductTranslations(product.id)
|
|
224
|
+
|
|
225
|
+
return transformProduct(product, {
|
|
226
|
+
...options,
|
|
227
|
+
translations,
|
|
228
|
+
})
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Integration with Tolgee
|
|
233
|
+
|
|
234
|
+
For production environments, you'll often want to integrate with external translation management platforms. Here's an example of integrating with [Tolgee](https://tolgee.io/), a popular translations management platform:
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
import { default as axios } from 'axios'
|
|
238
|
+
import { logger } from '@medusajs/framework'
|
|
239
|
+
import { TranslationMap } from '@rokmohar/medusa-plugin-meilisearch'
|
|
240
|
+
|
|
241
|
+
type TranslationsHttpResponse = {
|
|
242
|
+
[lang: string]: {
|
|
243
|
+
[id: string]: Record<string, string>
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const options = {
|
|
248
|
+
apiKey: process.env.TOLGEE_API_KEY ?? '',
|
|
249
|
+
baseURL: process.env.TOLGEE_API_URL ?? '',
|
|
250
|
+
projectId: process.env.TOLGEE_PROJECT_ID ?? '',
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const httpClient = axios.create({
|
|
254
|
+
baseURL: `${options.baseURL}/v2/projects/${options.projectId}`,
|
|
255
|
+
headers: {
|
|
256
|
+
Accept: 'application/json',
|
|
257
|
+
'X-API-Key': options.apiKey,
|
|
258
|
+
},
|
|
259
|
+
maxBodyLength: Infinity,
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
export const getTranslations = async (id: string, langs: string[]) => {
|
|
263
|
+
const translations: TranslationMap = {}
|
|
264
|
+
|
|
265
|
+
try {
|
|
266
|
+
const response = await httpClient.get<TranslationsHttpResponse>(`/translations/${langs.join(',')}?ns=${id}`)
|
|
267
|
+
Object.entries(response.data).forEach(([language_code, { [id]: values }]) => {
|
|
268
|
+
Object.entries(values).forEach(([key, value]) => {
|
|
269
|
+
if (!(key in translations)) {
|
|
270
|
+
translations[key] = []
|
|
271
|
+
}
|
|
272
|
+
translations[key].push({ language_code, value })
|
|
273
|
+
})
|
|
274
|
+
})
|
|
275
|
+
} catch (e) {
|
|
276
|
+
logger.error(e)
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return translations
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Usage in your transformer configuration:
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
{
|
|
287
|
+
settings: {
|
|
288
|
+
products: {
|
|
289
|
+
type: 'products',
|
|
290
|
+
// ... other config
|
|
291
|
+
transformer: async (product, defaultTransformer, options) => {
|
|
292
|
+
const translations = await getTranslations(product.id, ['sl', 'en'])
|
|
293
|
+
return defaultTransformer(product, {
|
|
294
|
+
...options,
|
|
295
|
+
translations,
|
|
296
|
+
includeAllTranslations: true
|
|
297
|
+
})
|
|
298
|
+
},
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
This integration fetches translations from Tolgee's API and transforms them into the format expected by this plugin. For complete storefront translation management, consider using the [medusa-plugin-tolgee](https://github.com/SteelRazor47/medusa-plugin-tolgee) by SteelRazor47, which provides comprehensive translation management features including admin widgets.
|
|
305
|
+
|
|
306
|
+
## i18n Strategies
|
|
307
|
+
|
|
308
|
+
### 1. Separate Index per Language
|
|
309
|
+
|
|
310
|
+
This strategy creates a separate MeiliSearch index for each language. For example, if your base index is named "products", it will create:
|
|
311
|
+
|
|
312
|
+
- products_en
|
|
313
|
+
- products_fr
|
|
314
|
+
- products_de
|
|
315
|
+
|
|
316
|
+
Benefits:
|
|
317
|
+
|
|
318
|
+
- Better performance for language-specific searches
|
|
319
|
+
- Language-specific settings and ranking rules
|
|
320
|
+
- Cleaner index structure
|
|
321
|
+
|
|
322
|
+
### 2. Language-specific Fields with Suffix
|
|
323
|
+
|
|
324
|
+
This strategy adds language suffixes to translatable fields in the same index. For example:
|
|
325
|
+
|
|
326
|
+
- title_en, title_fr, title_de
|
|
327
|
+
- description_en, description_fr, description_de
|
|
328
|
+
|
|
329
|
+
Benefits:
|
|
330
|
+
|
|
331
|
+
- Single index to maintain
|
|
332
|
+
- Ability to search across all languages at once
|
|
333
|
+
- Smaller storage requirements
|
|
334
|
+
|
|
335
|
+
## Custom Translatable Fields
|
|
336
|
+
|
|
337
|
+
If no translatable fields are specified and using the field-suffix strategy, the plugin will automatically detect string fields as translatable. You can override this by explicitly specifying the fields:
|
|
338
|
+
|
|
339
|
+
```typescript
|
|
340
|
+
{
|
|
341
|
+
i18n: {
|
|
342
|
+
strategy: 'field-suffix',
|
|
343
|
+
languages: ['en', 'fr'],
|
|
344
|
+
defaultLanguage: 'en',
|
|
345
|
+
// Only these fields will be translatable
|
|
346
|
+
translatableFields: ['title', 'description']
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
## Product API Endpoints
|
|
352
|
+
|
|
353
|
+
### Search for Product Hits
|
|
354
|
+
|
|
355
|
+
```http
|
|
356
|
+
GET /store/meilisearch/products-hits
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Query Parameters:
|
|
360
|
+
|
|
361
|
+
- `query`: Search query string
|
|
362
|
+
- `limit`: (Optional) Limit results from search
|
|
363
|
+
- `offset`: (Optional) Offset results from search
|
|
364
|
+
- `language`: (Optional) Language code
|
|
365
|
+
- `semanticSearch` - Enable AI-powered semantic search (boolean)
|
|
366
|
+
- `semanticRatio` - Semantic vs keyword search ratio (0-1)
|
|
367
|
+
|
|
368
|
+
### Search for Products
|
|
369
|
+
|
|
370
|
+
```http
|
|
371
|
+
GET /store/meilisearch/products
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
Query Parameters:
|
|
375
|
+
|
|
376
|
+
- `fields` - MedusaJS fields expression
|
|
377
|
+
- `limit`: (Optional) Limit results from search
|
|
378
|
+
- `offset`: (Optional) Offset results from search
|
|
379
|
+
- `region_id`: (Optional, but required for `calculated_price`) Current region ID
|
|
380
|
+
- `currency_code`: (Optional, but required for `calculated_price`) Current currency code
|
|
381
|
+
- `query`: Search query string
|
|
382
|
+
- `language`: (Optional) Language code
|
|
383
|
+
- `semanticSearch` - Enable AI-powered semantic search (boolean)
|
|
384
|
+
- `semanticRatio` - Semantic vs keyword search ratio (0-1)
|
|
385
|
+
|
|
386
|
+
## Category Support
|
|
387
|
+
|
|
388
|
+
This plugin provides full support for MedusaJS v2 categories, including:
|
|
389
|
+
|
|
390
|
+
- Real-time indexing of category changes
|
|
391
|
+
- i18n support for category names and descriptions
|
|
392
|
+
- Hierarchical category structure support with parent-child relationships
|
|
393
|
+
- Custom category field transformations
|
|
394
|
+
|
|
395
|
+
### Category Configuration Example
|
|
396
|
+
|
|
397
|
+
```typescript
|
|
398
|
+
{
|
|
399
|
+
settings: {
|
|
400
|
+
categories: {
|
|
401
|
+
type: 'categories',
|
|
402
|
+
enabled: true,
|
|
403
|
+
fields: ['id', 'name', 'description', 'handle', 'is_active', 'parent_id'],
|
|
404
|
+
indexSettings: {
|
|
405
|
+
searchableAttributes: ['name', 'description'],
|
|
406
|
+
displayedAttributes: ['id', 'name', 'description', 'handle', 'is_active', 'parent_id'],
|
|
407
|
+
filterableAttributes: ['id', 'handle', 'is_active', 'parent_id'],
|
|
408
|
+
},
|
|
409
|
+
primaryKey: 'id',
|
|
410
|
+
},
|
|
411
|
+
},
|
|
412
|
+
i18n: {
|
|
413
|
+
strategy: 'field-suffix',
|
|
414
|
+
languages: ['en', 'fr', 'de'],
|
|
415
|
+
defaultLanguage: 'en',
|
|
416
|
+
translatableFields: ['name', 'description'], // Category-specific translatable fields
|
|
417
|
+
},
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### Category API Endpoints
|
|
422
|
+
|
|
423
|
+
### Search for Category Hits
|
|
424
|
+
|
|
425
|
+
```http
|
|
426
|
+
GET /store/meilisearch/categories-hits
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
Query Parameters:
|
|
430
|
+
|
|
431
|
+
- `query`: Search query string
|
|
432
|
+
- `limit`: (Optional) Limit results from search
|
|
433
|
+
- `offset`: (Optional) Offset results from search
|
|
434
|
+
- `language`: (Optional) Language code
|
|
435
|
+
- `semanticSearch` - Enable AI-powered semantic search (boolean)
|
|
436
|
+
- `semanticRatio` - Semantic vs keyword search ratio (0-1)
|
|
437
|
+
|
|
438
|
+
### Search for Categories
|
|
439
|
+
|
|
440
|
+
```http
|
|
441
|
+
GET /store/meilisearch/categories
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
Query Parameters:
|
|
445
|
+
|
|
446
|
+
- `fields` - MedusaJS fields expression
|
|
447
|
+
- `limit`: (Optional) Limit results from search
|
|
448
|
+
- `offset`: (Optional) Offset results from search
|
|
449
|
+
- `query`: Search query string
|
|
450
|
+
- `language`: (Optional) Language code
|
|
451
|
+
- `semanticSearch` - Enable AI-powered semantic search (boolean)
|
|
452
|
+
- `semanticRatio` - Semantic vs keyword search ratio (0-1)
|
|
453
|
+
|
|
454
|
+
## AI-Powered Semantic Search
|
|
455
|
+
|
|
456
|
+
This plugin supports AI-powered semantic search using vector embeddings. See [docs/semantic-search.md](docs/semantic-search.md) for detailed configuration and usage instructions.
|
|
457
|
+
|
|
458
|
+
## ENV variables
|
|
459
|
+
|
|
460
|
+
Add the environment variables to your `.env` and `.env.template` file:
|
|
461
|
+
|
|
462
|
+
```env
|
|
463
|
+
# ... others vars
|
|
464
|
+
MEILISEARCH_HOST=
|
|
465
|
+
MEILISEARCH_API_KEY=
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
If you want to use with the `docker-compose` from this README, use the following values:
|
|
469
|
+
|
|
470
|
+
```env
|
|
471
|
+
# ... others vars
|
|
472
|
+
MEILISEARCH_HOST=http://127.0.0.1:7700
|
|
473
|
+
MEILISEARCH_API_KEY=ms
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
## docker-compose
|
|
477
|
+
|
|
478
|
+
You can add the following configuration for Meilisearch to your `docker-compose.yml`:
|
|
479
|
+
|
|
480
|
+
```yml
|
|
481
|
+
services:
|
|
482
|
+
# ... other services
|
|
483
|
+
|
|
484
|
+
meilisearch:
|
|
485
|
+
image: getmeili/meilisearch:latest
|
|
486
|
+
ports:
|
|
487
|
+
- '7700:7700'
|
|
488
|
+
volumes:
|
|
489
|
+
- ~/data.ms:/data.ms
|
|
490
|
+
environment:
|
|
491
|
+
- MEILI_MASTER_KEY=ms
|
|
492
|
+
healthcheck:
|
|
493
|
+
test: ['CMD', 'curl', '-f', 'http://localhost:7700']
|
|
494
|
+
interval: 10s
|
|
495
|
+
timeout: 5s
|
|
496
|
+
retries: 5
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
## Add search to Medusa NextJS starter
|
|
500
|
+
|
|
501
|
+
You can find instructions on how to add search to a Medusa NextJS starter inside the [nextjs](nextjs) folder.
|
|
502
|
+
|
|
503
|
+
## FAQ
|
|
504
|
+
|
|
505
|
+
- [How do I include product categories and tags in my search index?](docs/faq-product-categories-and-tags.md)
|
|
506
|
+
- [How do I include product variant prices (min_price, max_price) in my search index?](docs/faq-product-variant-prices.md)
|
|
507
|
+
- [How do I include prices in the search response from the search endpoint?](docs/faq-product-search-prices.md)
|
|
508
|
+
|
|
509
|
+
## Contributing
|
|
510
|
+
|
|
511
|
+
Feel free to open issues and pull requests!
|