Skip to content

Commit 528fcbb

Browse files
authored
Add full text search example (#9807)
1 parent 0a82d42 commit 528fcbb

8 files changed

Lines changed: 637 additions & 0 deletions

File tree

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
## Feature Example - Prisma Full-Text Search (PostgreSQL)
2+
3+
This project demonstrates how to use [Prisma's Full-Text Search](https://www.prisma.io/docs/v6/orm/prisma-client/queries/full-text-search) on PostgreSQL with Keystone, using `extendPrismaSchema` to enable the `fullTextSearchPostgres` preview feature and a custom GraphQL query to expose it.
4+
5+
> **Note:** This example requires a running PostgreSQL database. Full-text search is not available on SQLite.
6+
7+
## Instructions
8+
9+
To run this project, clone the Keystone repository locally then run `pnpm install` at the root of the repository.
10+
11+
Set your database URL:
12+
13+
```shell
14+
export DATABASE_URL="postgresql://localhost/keystone-example"
15+
```
16+
17+
Then navigate to this directory and run:
18+
19+
```shell
20+
pnpm dev
21+
```
22+
23+
This will start the Admin UI at [localhost:3000](http://localhost:3000).
24+
You can use the Admin UI to create items in your database.
25+
26+
You can also access a GraphQL Playground at [localhost:3000/api/graphql](http://localhost:3000/api/graphql), which allows you to directly run GraphQL queries and mutations.
27+
28+
## Features
29+
30+
### Enabling Full-Text Search with `extendPrismaSchema`
31+
32+
Prisma's `fullTextSearchPostgres` is a [preview feature](https://www.prisma.io/docs/v6/orm/reference/preview-features/client-preview-features) that must be explicitly enabled in the Prisma generator block. Since Keystone manages the `schema.prisma` file automatically, you can inject this using `extendPrismaSchema` in your Keystone config:
33+
34+
```typescript
35+
db: {
36+
provider: 'postgresql',
37+
extendPrismaSchema: schema => {
38+
return schema.replace(
39+
/(generator [^}]+)}/g,
40+
['$1', ' previewFeatures = ["fullTextSearchPostgres"]', '}'].join('\n')
41+
)
42+
},
43+
}
44+
```
45+
46+
This produces a generator block like:
47+
48+
```prisma
49+
generator client {
50+
provider = "prisma-client-js"
51+
previewFeatures = ["fullTextSearchPostgres"]
52+
}
53+
```
54+
55+
### Custom `searchPosts` Query
56+
57+
Prisma's `search` operator is a low-level Prisma feature not yet surfaced through Keystone's `context.db` query layer. To use it, the example reaches for the raw Prisma client via `context.prisma`, then converts the results back to Keystone-typed objects using `context.db`:
58+
59+
```typescript
60+
// 1. Use raw Prisma client to perform the full-text search
61+
const matches = await context.prisma.post.findMany({
62+
where: {
63+
OR: [{ title: { search: query } }, { content: { search: query } }],
64+
},
65+
select: { id: true },
66+
})
67+
68+
// 2. Fetch proper Keystone Post objects using the matched IDs
69+
const ids = matches.map(p => p.id)
70+
return context.db.Post.findMany({ where: { id: { in: ids } } })
71+
```
72+
73+
### PostgreSQL Full-Text Search Syntax
74+
75+
The `query` argument accepts PostgreSQL's tsquery syntax:
76+
77+
| Operator | Meaning | Example |
78+
| -------- | ---------------- | ------------------------------------------------------- |
79+
| `&` | AND | `"cat & dog"` — must contain both |
80+
| `\|` | OR | `"cat \| dog"` — must contain either |
81+
| `!` | NOT | `"!cat"` — must not contain |
82+
| `<->` | Phrase/proximity | `"fox <-> dog"` — "dog" follows immediately after "fox" |
83+
84+
### Try it in the GraphQL Playground
85+
86+
After creating some posts in the Admin UI, open [localhost:3000/api/graphql](http://localhost:3000/api/graphql) and run:
87+
88+
```graphql
89+
query {
90+
searchPosts(query: "keystone & graphql") {
91+
id
92+
title
93+
content
94+
author {
95+
name
96+
}
97+
}
98+
}
99+
```
100+
101+
## Try it out in CodeSandbox 🧪
102+
103+
You can play with this example online in a web browser using the free [codesandbox.io](https://codesandbox.io/) service. To launch this example, open the URL <https://githubbox.com/keystonejs/keystone/tree/main/examples/extend-full-text-search>. You can also fork this sandbox to make your own changes.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { config } from '@keystone-6/core'
2+
import { lists, extendGraphqlSchema } from './schema'
3+
4+
export default config({
5+
db: {
6+
provider: 'postgresql',
7+
url: process.env.DATABASE_URL || 'postgresql://localhost/keystone-example',
8+
9+
extendPrismaSchema: schema => {
10+
return schema.replace(
11+
/(generator [^}]+)}/g,
12+
['$1', ' previewFeatures = ["fullTextSearchPostgres"]', '}'].join('\n')
13+
)
14+
},
15+
16+
// WARNING: this is only needed for our monorepo examples, dont do this
17+
prismaClientPath: 'node_modules/myprisma',
18+
},
19+
graphql: {
20+
extendGraphqlSchema,
21+
},
22+
lists,
23+
})
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "@keystone-6/example-extend-full-text-search",
3+
"version": null,
4+
"private": true,
5+
"license": "MIT",
6+
"scripts": {
7+
"dev": "keystone dev",
8+
"start": "keystone start",
9+
"build": "keystone build",
10+
"postinstall": "keystone postinstall"
11+
},
12+
"dependencies": {
13+
"@keystone-6/core": "^6.5.2",
14+
"@prisma/client": "6.16.1"
15+
},
16+
"devDependencies": {
17+
"prisma": "6.16.1",
18+
"typescript": "^5.9.0"
19+
}
20+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"template": "node",
3+
"container": {
4+
"startScript": "keystone dev",
5+
"node": "22"
6+
}
7+
}

0 commit comments

Comments
 (0)