Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 123 additions & 0 deletions graphile/graphile-pg-textsearch-plugin/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# graphile-pg-textsearch-plugin

<p align="center" width="100%">
<img height="250" src="https://raw.githubusercontent.com/constructive-io/constructive/refs/heads/main/assets/outline-logo.svg" />
</p>

<p align="center" width="100%">
<a href="https://github.com/constructive-io/constructive/actions/workflows/run-tests.yaml">
<img height="20" src="https://github.com/constructive-io/constructive/actions/workflows/run-tests.yaml/badge.svg" />
</a>
<a href="https://github.com/constructive-io/constructive/blob/main/LICENSE">
<img height="20" src="https://img.shields.io/badge/license-MIT-blue.svg"/>
</a>
<a href="https://www.npmjs.com/package/graphile-pg-textsearch-plugin">
<img height="20" src="https://img.shields.io/github/package-json/v/constructive-io/constructive?filename=graphile%2Fgraphile-pg-textsearch-plugin%2Fpackage.json"/>
</a>
</p>

**`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).

## Installation

```sh
npm install graphile-pg-textsearch-plugin
```

## Features

- **Auto-discovery**: Finds all text columns with BM25 indexes automatically — zero configuration
- **Condition fields**: `bm25<Column>` condition inputs accepting `{ query, threshold? }` for BM25 ranked search
- **Score fields**: `bm25<Column>Score` computed fields returning BM25 relevance scores (negative values, more negative = more relevant)
- **OrderBy**: `BM25_<COLUMN>_SCORE_ASC/DESC` enum values for sorting by relevance
- Works with PostGraphile v5 preset/plugin pipeline

## Usage

### With Preset (Recommended)

```typescript
import { Bm25SearchPreset } from 'graphile-pg-textsearch-plugin';

const preset = {
extends: [
// ... your other presets
Bm25SearchPreset(),
],
};
```

### With Plugin Directly

```typescript
import { Bm25CodecPlugin, Bm25SearchPlugin } from 'graphile-pg-textsearch-plugin';

const preset = {
plugins: [
Bm25CodecPlugin,
Bm25SearchPlugin(),
],
};
```

### GraphQL Query

```graphql
query SearchArticles($search: Bm25SearchInput!) {
articles(condition: { bm25Body: $search }) {
nodes {
id
title
body
bm25BodyScore
}
}
}
```

Variables:

```json
{
"search": {
"query": "postgres full text search",
"threshold": -0.5
}
}
```

### OrderBy

```graphql
query SearchArticlesSorted($search: Bm25SearchInput!) {
articles(
condition: { bm25Body: $search }
orderBy: BM25_BODY_SCORE_ASC
) {
nodes {
id
title
bm25BodyScore
}
}
}
```

## Requirements

- PostgreSQL with [pg_textsearch](https://github.com/timescale/pg_textsearch) extension installed
- PostGraphile v5 (rc.5+)
- A BM25 index on the text column(s) you want to search:

```sql
CREATE INDEX articles_body_idx ON articles USING bm25(body)
WITH (text_config='english');
```

## Testing

```sh
# requires pyramation/postgres:17 Docker image with pg_textsearch pre-installed
pnpm --filter graphile-pg-textsearch-plugin test
```

18 changes: 18 additions & 0 deletions graphile/graphile-pg-textsearch-plugin/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
transform: {
'^.+\\.tsx?$': [
'ts-jest',
{
babelConfig: false,
tsconfig: 'tsconfig.json'
}
]
},
transformIgnorePatterns: [`/node_modules/*`],
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
modulePathIgnorePatterns: ['dist/*']
};
67 changes: 67 additions & 0 deletions graphile/graphile-pg-textsearch-plugin/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"name": "graphile-pg-textsearch-plugin",
"version": "1.0.0",
"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",
"author": "Constructive <developers@constructive.io>",
"homepage": "https://github.com/constructive-io/constructive",
"license": "MIT",
"main": "index.js",
"module": "esm/index.js",
"types": "index.d.ts",
"scripts": {
"clean": "makage clean",
"prepack": "npm run build",
"build": "makage build",
"build:dev": "makage build --dev",
"lint": "eslint . --fix",
"test": "jest",
"test:watch": "jest --watch"
},
"publishConfig": {
"access": "public",
"directory": "dist"
},
"repository": {
"type": "git",
"url": "https://github.com/constructive-io/constructive"
},
"bugs": {
"url": "https://github.com/constructive-io/constructive/issues"
},
"devDependencies": {
"@types/node": "^22.19.1",
"@types/pg": "^8.16.0",
"graphile-test": "workspace:^",
"makage": "^0.1.10",
"pg": "^8.17.1",
"pgsql-test": "workspace:^"
},
"peerDependencies": {
"@dataplan/pg": "1.0.0-rc.5",
"graphile-build": "5.0.0-rc.4",
"graphile-build-pg": "5.0.0-rc.5",
"graphile-config": "1.0.0-rc.5",
"graphql": "^16.9.0",
"pg-sql2": "5.0.0-rc.4",
"postgraphile": "5.0.0-rc.7",
"postgraphile-plugin-connection-filter": "3.0.0-rc.1"
},
"peerDependenciesMeta": {
"postgraphile-plugin-connection-filter": {
"optional": true
}
},
"keywords": [
"postgraphile",
"graphile",
"constructive",
"plugin",
"postgres",
"graphql",
"pg_textsearch",
"bm25",
"full-text-search",
"text-search",
"ranking"
]
}
Loading