Skip to content

Commit c432832

Browse files
authored
Merge pull request #769 from constructive-io/devin/1772486156-pg-textsearch-plugin
feat(graphile-pg-textsearch-plugin): auto-discover BM25 indexes with condition, score, and orderBy
2 parents 687d71b + e1de78c commit c432832

16 files changed

Lines changed: 1426 additions & 0 deletions

File tree

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# graphile-pg-textsearch-plugin
2+
3+
<p align="center" width="100%">
4+
<img height="250" src="https://raw.githubusercontent.com/constructive-io/constructive/refs/heads/main/assets/outline-logo.svg" />
5+
</p>
6+
7+
<p align="center" width="100%">
8+
<a href="https://github.com/constructive-io/constructive/actions/workflows/run-tests.yaml">
9+
<img height="20" src="https://github.com/constructive-io/constructive/actions/workflows/run-tests.yaml/badge.svg" />
10+
</a>
11+
<a href="https://github.com/constructive-io/constructive/blob/main/LICENSE">
12+
<img height="20" src="https://img.shields.io/badge/license-MIT-blue.svg"/>
13+
</a>
14+
<a href="https://www.npmjs.com/package/graphile-pg-textsearch-plugin">
15+
<img height="20" src="https://img.shields.io/github/package-json/v/constructive-io/constructive?filename=graphile%2Fgraphile-pg-textsearch-plugin%2Fpackage.json"/>
16+
</a>
17+
</p>
18+
19+
**`graphile-pg-textsearch-plugin`** enables auto-discovered BM25 ranked full-text search for PostGraphile v5 schemas using [pg_textsearch](https://github.com/timescale/pg_textsearch).
20+
21+
## Installation
22+
23+
```sh
24+
npm install graphile-pg-textsearch-plugin
25+
```
26+
27+
## Features
28+
29+
- **Auto-discovery**: Finds all text columns with BM25 indexes automatically — zero configuration
30+
- **Condition fields**: `bm25<Column>` condition inputs accepting `{ query, threshold? }` for BM25 ranked search
31+
- **Score fields**: `bm25<Column>Score` computed fields returning BM25 relevance scores (negative values, more negative = more relevant)
32+
- **OrderBy**: `BM25_<COLUMN>_SCORE_ASC/DESC` enum values for sorting by relevance
33+
- Works with PostGraphile v5 preset/plugin pipeline
34+
35+
## Usage
36+
37+
### With Preset (Recommended)
38+
39+
```typescript
40+
import { Bm25SearchPreset } from 'graphile-pg-textsearch-plugin';
41+
42+
const preset = {
43+
extends: [
44+
// ... your other presets
45+
Bm25SearchPreset(),
46+
],
47+
};
48+
```
49+
50+
### With Plugin Directly
51+
52+
```typescript
53+
import { Bm25CodecPlugin, Bm25SearchPlugin } from 'graphile-pg-textsearch-plugin';
54+
55+
const preset = {
56+
plugins: [
57+
Bm25CodecPlugin,
58+
Bm25SearchPlugin(),
59+
],
60+
};
61+
```
62+
63+
### GraphQL Query
64+
65+
```graphql
66+
query SearchArticles($search: Bm25SearchInput!) {
67+
articles(condition: { bm25Body: $search }) {
68+
nodes {
69+
id
70+
title
71+
body
72+
bm25BodyScore
73+
}
74+
}
75+
}
76+
```
77+
78+
Variables:
79+
80+
```json
81+
{
82+
"search": {
83+
"query": "postgres full text search",
84+
"threshold": -0.5
85+
}
86+
}
87+
```
88+
89+
### OrderBy
90+
91+
```graphql
92+
query SearchArticlesSorted($search: Bm25SearchInput!) {
93+
articles(
94+
condition: { bm25Body: $search }
95+
orderBy: BM25_BODY_SCORE_ASC
96+
) {
97+
nodes {
98+
id
99+
title
100+
bm25BodyScore
101+
}
102+
}
103+
}
104+
```
105+
106+
## Requirements
107+
108+
- PostgreSQL with [pg_textsearch](https://github.com/timescale/pg_textsearch) extension installed
109+
- PostGraphile v5 (rc.5+)
110+
- A BM25 index on the text column(s) you want to search:
111+
112+
```sql
113+
CREATE INDEX articles_body_idx ON articles USING bm25(body)
114+
WITH (text_config='english');
115+
```
116+
117+
## Testing
118+
119+
```sh
120+
# requires pyramation/postgres:17 Docker image with pg_textsearch pre-installed
121+
pnpm --filter graphile-pg-textsearch-plugin test
122+
```
123+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/** @type {import('ts-jest').JestConfigWithTsJest} */
2+
module.exports = {
3+
preset: 'ts-jest',
4+
testEnvironment: 'node',
5+
transform: {
6+
'^.+\\.tsx?$': [
7+
'ts-jest',
8+
{
9+
babelConfig: false,
10+
tsconfig: 'tsconfig.json'
11+
}
12+
]
13+
},
14+
transformIgnorePatterns: [`/node_modules/*`],
15+
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$',
16+
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
17+
modulePathIgnorePatterns: ['dist/*']
18+
};
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
{
2+
"name": "graphile-pg-textsearch-plugin",
3+
"version": "1.0.0",
4+
"description": "PostGraphile v5 plugin for pg_textsearch BM25 ranked full-text search — auto-discovers BM25 indexes and adds search condition, score, orderBy, and filter fields",
5+
"author": "Constructive <developers@constructive.io>",
6+
"homepage": "https://github.com/constructive-io/constructive",
7+
"license": "MIT",
8+
"main": "index.js",
9+
"module": "esm/index.js",
10+
"types": "index.d.ts",
11+
"scripts": {
12+
"clean": "makage clean",
13+
"prepack": "npm run build",
14+
"build": "makage build",
15+
"build:dev": "makage build --dev",
16+
"lint": "eslint . --fix",
17+
"test": "jest",
18+
"test:watch": "jest --watch"
19+
},
20+
"publishConfig": {
21+
"access": "public",
22+
"directory": "dist"
23+
},
24+
"repository": {
25+
"type": "git",
26+
"url": "https://github.com/constructive-io/constructive"
27+
},
28+
"bugs": {
29+
"url": "https://github.com/constructive-io/constructive/issues"
30+
},
31+
"devDependencies": {
32+
"@types/node": "^22.19.1",
33+
"@types/pg": "^8.16.0",
34+
"graphile-test": "workspace:^",
35+
"makage": "^0.1.10",
36+
"pg": "^8.17.1",
37+
"pgsql-test": "workspace:^"
38+
},
39+
"peerDependencies": {
40+
"@dataplan/pg": "1.0.0-rc.5",
41+
"graphile-build": "5.0.0-rc.4",
42+
"graphile-build-pg": "5.0.0-rc.5",
43+
"graphile-config": "1.0.0-rc.5",
44+
"graphql": "^16.9.0",
45+
"pg-sql2": "5.0.0-rc.4",
46+
"postgraphile": "5.0.0-rc.7",
47+
"postgraphile-plugin-connection-filter": "3.0.0-rc.1"
48+
},
49+
"peerDependenciesMeta": {
50+
"postgraphile-plugin-connection-filter": {
51+
"optional": true
52+
}
53+
},
54+
"keywords": [
55+
"postgraphile",
56+
"graphile",
57+
"constructive",
58+
"plugin",
59+
"postgres",
60+
"graphql",
61+
"pg_textsearch",
62+
"bm25",
63+
"full-text-search",
64+
"text-search",
65+
"ranking"
66+
]
67+
}

0 commit comments

Comments
 (0)