@medusajs/search-postgres 2.22.0-snapshot-20261021091347 → 2.22.0

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.
Files changed (3) hide show
  1. package/AGENTS.md +57 -0
  2. package/README.md +107 -140
  3. package/package.json +8 -4
package/AGENTS.md ADDED
@@ -0,0 +1,57 @@
1
+ # @medusajs/search-postgres
2
+
3
+ Search Module provider that stores and queries search indexes in the application's PostgreSQL database. Two engines: `native` (GIN + `ts_rank` + `pg_trgm`, any Postgres) and `lakebase` (BM25 + ANN vectors, Medusa Cloud only). Registered by default by `defineConfig`.
4
+
5
+ ## Using this package
6
+
7
+ Registered automatically with `id: "postgres"` and `engine: "lakebase"` on Medusa Cloud, `"native"` elsewhere. Register explicitly to change options:
8
+
9
+ ```ts
10
+ module.exports = defineConfig({
11
+ modules: [
12
+ {
13
+ resolve: "@medusajs/medusa/search",
14
+ options: {
15
+ providers: [
16
+ {
17
+ resolve: "@medusajs/medusa/search-postgres",
18
+ id: "search-postgres",
19
+ options: {
20
+ // requires a medusa_search_german text search configuration
21
+ language: "german",
22
+ },
23
+ },
24
+ ],
25
+ },
26
+ },
27
+ ],
28
+ })
29
+ ```
30
+
31
+ - Options (`PostgresSearchProviderOptions`): `engine` (`"native"` default | `"lakebase"`), `language` (default `"english"`), `embedder` (`(text) => Promise<number[]>`, lakebase only, needed for `.embed()` fields and `search_options.vector.query`), `vector_distance` (`"cosine"` default | `"l2"` | `"inner_product"`).
32
+ - After registering: `npx medusa db:migrate` (extensions + catalog table), then `npx medusa db:migrate-search` to build indexes from your index definitions.
33
+ - Index definitions and queries go through the Search Module / Query (`query.search(...)`), not this provider.
34
+ - Docs: https://docs.medusajs.com/resources/infrastructure-modules/search/providers/postgres.md
35
+ - Build a custom search provider: https://docs.medusajs.com/resources/references/search/provider.md
36
+ - Feature matrix, vector fields, filter semantics: `README.md` in this package.
37
+
38
+ ## Architecture
39
+
40
+ - `src/services/postgres-search.ts`: `PostgresSearchService extends AbstractSearchProviderService` (from `@medusajs/framework/utils`), `static identifier = "search-postgres"`. Implements `upsertIndex`, `deleteIndex`, `listIndexes`, `upsertDocuments` (chunks of 200), `deleteDocuments`, `clearIndex`, `search`, `searchMany`.
41
+ - Uses the injected `manager` with raw SQL only (no MikroORM entities; `mikro-orm.config.dev.ts` has `entities: []`).
42
+ - `src/utils/plan.ts`: turns an index definition into an `IndexPlan` (one table per index, columns, GIN/BM25/ANN indexes); the catalog table `search_postgres_index` stores each plan and its `schema_hash`.
43
+ - `src/utils/filters.ts` (filters to SQL, jsonb containment for equality/`$in`), `facets.ts`, `documents.ts`, `extensions.ts`.
44
+ - `src/migrations/`: enables `pg_trgm`, `unaccent`, creates `medusa_search_english` and the catalog table. Extension creation soft-fails (`RAISE NOTICE`) when the DB role lacks privileges.
45
+
46
+ ## Development
47
+
48
+ - `yarn build`, `yarn test` (unit specs in `src/utils/__tests__`).
49
+ - `yarn test:integration`: `integration-tests/__tests__/postgres-search.spec.ts` via `moduleIntegrationTestRunner`, needs a local Postgres with `pg_trgm`/`unaccent` available.
50
+
51
+ ## Gotchas
52
+
53
+ - Providers are keyed by `identifier`, not `id`: index definitions and `default_provider` reference `search-postgres`, and registering this provider twice throws.
54
+ - `language` must match `/^[a-z0-9_]+$/i`, and the provider always queries the `medusa_search_<language>` text search config. Only `medusa_search_english` is created by the migration; create others yourself.
55
+ - Native engine: vector fields in a definition are dropped with a warning, but queries with `search_options.vector` are rejected.
56
+ - Unsupported on both engines (rejected explicitly): highlighting, geo, cursor pagination, query-time locales.
57
+ - Range operators, `$prefix`, and `$like` are rejected on array fields; a bare value on an array field means membership.
package/README.md CHANGED
@@ -1,156 +1,123 @@
1
- # PostgreSQL search provider
2
-
3
- Search provider for Medusa backed by PostgreSQL, with two engines:
4
-
5
- | Engine | When to use | Keyword | Vector |
6
- | ------------------ | ---------------------------------- | --------------------------- | -------------------- |
7
- | `native` (default) | Local / self-hosted / any Postgres | GIN + `ts_rank` + `pg_trgm` | Not supported |
8
- | `lakebase` | Medusa Cloud / Lakebase Search | `lakebase_bm25` (BM25) | `lakebase_ann` (ANN) |
9
-
10
- ## Enable it
11
-
12
- ### Native (default, works locally and on Medusa Cloud)
13
-
14
- ```ts
15
- modules: [
16
- {
17
- resolve: "@medusajs/medusa/search",
18
- options: {
19
- providers: [
20
- {
21
- resolve: "@medusajs/medusa/search-postgres",
22
- id: "postgres",
23
- options: {
24
- // engine: "native", // default
25
- // language: "english",
26
- },
27
- },
28
- ],
29
- },
30
- },
31
- ]
32
- ```
33
-
34
- ### Lakebase (works only on Medusa Cloud)
1
+ <p align="center">
2
+ <a href="https://www.medusajs.com">
3
+ <picture>
4
+ <source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/59018053/229103275-b5e482bb-4601-46e6-8142-244f531cebdb.svg">
5
+ <source media="(prefers-color-scheme: light)" srcset="https://user-images.githubusercontent.com/59018053/229103726-e5b529a3-9b3f-4970-8a1f-c6af37f087bf.svg">
6
+ <img alt="Medusa logo" src="https://user-images.githubusercontent.com/59018053/229103726-e5b529a3-9b3f-4970-8a1f-c6af37f087bf.svg">
7
+ </picture>
8
+ </a>
9
+ </p>
10
+ <h1 align="center">
11
+ @medusajs/search-postgres
12
+ </h1>
13
+
14
+ <h4 align="center">
15
+ <a href="https://docs.medusajs.com">Documentation</a> |
16
+ <a href="https://www.medusajs.com">Website</a>
17
+ </h4>
18
+
19
+ <p align="center">
20
+ Building blocks for digital commerce
21
+ </p>
22
+ <p align="center">
23
+ <a href="https://github.com/medusajs/medusa/blob/develop/LICENSE">
24
+ <img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="Medusa is released under the MIT license." />
25
+ </a>
26
+ <a href="https://github.com/medusajs/medusa/blob/develop/CONTRIBUTING.md">
27
+ <img src="https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat" alt="PRs welcome!" />
28
+ </a>
29
+ <a href="https://discord.gg/xpCwq3Kfn8">
30
+ <img src="https://img.shields.io/badge/chat-on%20discord-7289DA.svg" alt="Discord Chat" />
31
+ </a>
32
+ <a href="https://twitter.com/intent/follow?screen_name=medusajs">
33
+ <img src="https://img.shields.io/twitter/follow/medusajs.svg?label=Follow%20@medusajs" alt="Follow @medusajs" />
34
+ </a>
35
+ </p>
36
+
37
+ PostgreSQL Search Module Provider for Medusa: full-text search, typo tolerance, filters, and facets over your search indexes, stored in the application's own PostgreSQL database. It's the default search provider, so search works without an external search engine. It ships with `@medusajs/medusa` and is registered through the `@medusajs/medusa/search-postgres` path.
38
+
39
+ ## At a glance
40
+
41
+ | | |
42
+ | --- | --- |
43
+ | Package | `@medusajs/search-postgres` |
44
+ | Type | Module Provider (Search Module) |
45
+ | Provider ID | `search-postgres` |
46
+ | Registered by default | Yes, with `id: "postgres"` (`engine: "native"` when self-hosted, `"lakebase"` on Medusa Cloud) |
47
+ | Import from | `@medusajs/medusa/search-postgres` |
48
+
49
+ ## Engines
50
+
51
+ | Engine | Where | Keyword search | Vector search |
52
+ | --- | --- | --- | --- |
53
+ | `native` (default) | Any PostgreSQL database | PostgreSQL full-text search (`tsvector`) with `pg_trgm` typo tolerance | Not supported |
54
+ | `lakebase` | Medusa Cloud only | BM25 (`lakebase_bm25`) with `pg_trgm` typo tolerance | ANN (`lakebase_ann`), and hybrid keyword + vector search |
55
+
56
+ ## Features
57
+
58
+ - Free-text search with field weights and `match_strategy` `"all"` (default), `"any"`, or `"last"` (typeahead: the last term matches as a prefix).
59
+ - Filters with `$eq`, `$ne`, `$in`, `$nin`, range operators, and `$and` / `$or` / `$not`. On array fields, a bare value or `$eq` means "contains".
60
+ - Value, range, and stats facets scoped to the query's matches.
61
+ - `distinct` (one hit per value) and `min_score`.
62
+ - Vector and hybrid search on the `lakebase` engine. On `native`, vector fields are dropped from the index with a warning, and vector queries are rejected.
63
+
64
+ Highlighting, geo fields, cursor pagination, and query-time locales aren't supported and are rejected.
65
+
66
+ ## Installation
67
+
68
+ No installation needed. The provider is a dependency of `@medusajs/medusa` and is registered by default.
69
+
70
+ ## Usage
71
+
72
+ Register the provider yourself to pass options:
35
73
 
36
74
  ```ts
37
- {
38
- resolve: "@medusajs/medusa/search-postgres",
39
- id: "postgres",
40
- options: {
41
- engine: "lakebase",
42
- // Optional: embed text for search_options.vector.query
43
- // embedder: async (text) => { ... return number[] },
44
- // vector_distance: "cosine", // or "l2" | "inner_product"
45
- },
46
- }
47
- ```
48
-
49
- Then:
50
-
51
- ```bash
52
- npx medusa db:migrate
53
- npx medusa db:migrate-search
54
- ```
55
-
56
- The migration enables `pg_trgm` + `unaccent` and creates the catalog. On Medusa Cloud it also enables `lakebase_vector` and `lakebase_text` (`CREATE EXTENSION ... CASCADE`). Those statements soft-fail on engines that do not ship Lakebase Search.
57
-
58
- ## Provider options
59
-
60
- | Option | Default | Description |
61
- | ----------------- | ----------- | -------------------------------------------------------------------------------------- |
62
- | `engine` | `"native"` | `"native"` or `"lakebase"` |
63
- | `language` | `"english"` | Text search config. With `unaccent`, creates `medusa_search_<language>`. |
64
- | `embedder` | — | `(text) => Promise<number[]>`. Required for `search_options.vector.query` on lakebase. |
65
- | `vector_distance` | `"cosine"` | ANN metric: `cosine`, `l2`, or `inner_product`. |
66
-
67
- ## Vector fields (lakebase only)
75
+ import { defineConfig } from "@medusajs/framework/utils"
68
76
 
69
- On the native engine vector fields are not rejected: the provider logs a warning and drops them from the index, so one definition stays portable across providers. A query that asks for `search_options.vector` there is still rejected, since answering it with keyword results would be wrong.
70
-
71
- Supply embeddings yourself:
72
-
73
- ```ts
74
- defineSearchIndex({
75
- name: "product",
76
- entity: "product",
77
- fields: search.define({
78
- id: search.keyword().filterable(),
79
- title: search.text().searchable({ weight: 3 }),
80
- embedding: search.vector(1536),
81
- }),
77
+ module.exports = defineConfig({
82
78
  // ...
83
- })
84
- ```
85
-
86
- Documents must include the embedding array on upsert. Query with `search_options.vector.value`.
87
-
88
- Or let the provider embed a string on the same field (`.embed()` requires `embedder`):
89
-
90
- ```ts
91
- embedding: search.vector(1536).embed()
92
-
93
- // documents: { embedding: "title and description to encode" }
94
-
95
- await query.search({
96
- entity: "product",
97
- search_options: {
98
- vector: {
99
- field: "embedding",
100
- query: "red shoes",
101
- semantic_ratio: 0.5, // 0 = keyword, 1 = vector, in between = RRF hybrid
79
+ modules: [
80
+ {
81
+ resolve: "@medusajs/medusa/search",
82
+ options: {
83
+ providers: [
84
+ {
85
+ resolve: "@medusajs/medusa/search-postgres",
86
+ id: "postgres",
87
+ options: {
88
+ // requires a medusa_search_german text search configuration
89
+ language: "german",
90
+ },
91
+ },
92
+ ],
93
+ },
102
94
  },
103
- },
95
+ ],
104
96
  })
105
97
  ```
106
98
 
107
- Query with a client-supplied embedding against either kind of field:
99
+ Then run the migrations, which enable the `pg_trgm` and `unaccent` extensions, create the `medusa_search_english` text search configuration, and create your search indexes:
108
100
 
109
- ```ts
110
- await query.search({
111
- entity: "product",
112
- filters: { q: "red shoes" },
113
- search_options: {
114
- vector: {
115
- field: "embedding",
116
- value: embeddingArray,
117
- semantic_ratio: 0.5,
118
- },
119
- },
120
- })
101
+ ```bash
102
+ npx medusa db:migrate
121
103
  ```
122
104
 
123
- ## What it supports
124
-
125
- | | native | lakebase |
126
- | ---------------- | ------------------------------------------------------- | ------------------------ |
127
- | Free text | `ts_rank` + weights | BM25 via `lakebase_bm25` |
128
- | Typo tolerance | `pg_trgm` (`word_similarity`) | `pg_trgm` (same) |
129
- | `match_strategy` | `"all"` (default), `"any"`, `"last"` | same |
130
- | Filters | `$eq` `$ne` `$in` `$nin` ranges, `$and`/`$or`/`$not`, … | same |
131
- | Facets | value, range, stats — scoped to the query matches | same |
132
- | `distinct` | one hit per value, count follows | same |
133
- | `min_score` | yes, keeps the requested sort | same |
134
- | Vector / hybrid | fields ignored, queries rejected | ANN + RRF |
135
-
136
- Unsupported on both (rejected explicitly): highlighting, geo, cursor pagination, query-time locales.
105
+ To create or update only the search indexes, run `npx medusa db:migrate:search`.
137
106
 
138
- `"last"` is typeahead: completed terms must match in full and the last term is a prefix, so `"dtc sta"` matches `"Dtc starter"`.
107
+ If you set `language` to anything other than `english`, create a matching `medusa_search_<language>` text search configuration yourself.
139
108
 
140
- ### Filter semantics
109
+ ## Configuration
141
110
 
142
- - Equality and `$in` compile to jsonb containment (`indexed @> …`), which the
143
- `jsonb_path_ops` GIN index accelerates and which compares numbers and
144
- booleans natively.
145
- - On array fields, a bare value or `$eq` means membership — `{ tags: "sale" }`
146
- matches documents whose `tags` array contains `"sale"`.
147
- - Range operators (`$gt`, …), `$prefix` and `$like` are rejected on array
148
- fields.
111
+ | Option | Type | Description | Default |
112
+ | --- | --- | --- | --- |
113
+ | `engine` | `"native" \| "lakebase"` | The search backend. `lakebase` works only on Medusa Cloud. | `"native"` |
114
+ | `language` | `string` | The PostgreSQL text search configuration language, such as `english` or `simple`. | `"english"` |
115
+ | `embedder` | `(text: string) => Promise<number[]>` | Turns text into an embedding. Required for vector fields that use `.embed()` and for `search_options.vector.query`. | - |
116
+ | `vector_distance` | `"cosine" \| "l2" \| "inner_product"` | Distance metric for vector indexes. | `"cosine"` |
149
117
 
150
- ### Hybrid queries (lakebase)
118
+ ## Resources
151
119
 
152
- - Results are rank-fused (RRF), so they can only be ordered by `_score`.
153
- - `min_score` applies to the fused RRF score (per-arm contributions are at most
154
- `weight / 61`).
155
- - `metadata.count` is the exact size of the union of both arms' match sets;
156
- the fused hit list itself is a bounded candidate window.
120
+ - [PostgreSQL Search Module Provider](https://docs.medusajs.com/resources/infrastructure-modules/search/providers/postgres)
121
+ - [Search Module](https://docs.medusajs.com/resources/infrastructure-modules/search)
122
+ - [Search Index Definitions](https://docs.medusajs.com/resources/infrastructure-modules/search/index-definitions)
123
+ - [Search with Query](https://docs.medusajs.com/learn/fundamentals/query/search)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@medusajs/search-postgres",
3
- "version": "2.22.0-snapshot-20261021091347",
3
+ "version": "2.22.0",
4
4
  "description": "PostgreSQL search provider for Medusa, backed by tsvector and pg_trgm",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -17,10 +17,14 @@
17
17
  },
18
18
  "files": [
19
19
  "dist",
20
+ "AGENTS.md",
20
21
  "!dist/**/__tests__",
21
22
  "!dist/**/__mocks__",
22
23
  "!dist/**/__fixtures__"
23
24
  ],
25
+ "llms": "https://docs.medusajs.com/llms.txt",
26
+ "llmsFull": "https://docs.medusajs.com/llms-full.txt",
27
+ "mcpServer": "https://docs.medusajs.com/mcp",
24
28
  "engines": {
25
29
  "node": ">=22.22.0"
26
30
  },
@@ -37,11 +41,11 @@
37
41
  "orm:cache:clear": "MIKRO_ORM_CLI_CONFIG=./mikro-orm.config.dev.ts MIKRO_ORM_ALLOW_GLOBAL_CLI=true medusa-mikro-orm cache:clear"
38
42
  },
39
43
  "devDependencies": {
40
- "@medusajs/framework": "2.22.0-snapshot-20261021091347",
41
- "@medusajs/test-utils": "2.22.0-snapshot-20261021091347"
44
+ "@medusajs/framework": "2.22.0",
45
+ "@medusajs/test-utils": "2.22.0"
42
46
  },
43
47
  "peerDependencies": {
44
- "@medusajs/framework": "2.22.0-snapshot-20261021091347"
48
+ "@medusajs/framework": "2.22.0"
45
49
  },
46
50
  "keywords": [
47
51
  "medusa-plugin",