Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add MySQL to full text search Concepts page #2678

Merged
merged 14 commits into from Jan 11, 2022
@@ -1,30 +1,35 @@
---
title: 'Full-Text Search'
metaTitle: 'Full-Text Search (Preview)'
title: 'Full-text search'
metaTitle: 'Full-text search (Preview)'
metaDescription: 'This page explains how to search for text within a field.'
preview: true
---

# Full-Text Search (Preview)
<TopBlock>

The Prisma Client supports a full-text search API for Postgres databases. With full-text search enabled, you can add search functionality to your application by searching for text within a database column.
The Prisma Client supports full-text search for **PostgreSQL** databases in versions 2.30.0 and later, and **MySQL** databases in versions 3.8.0 and later. With full-text search enabled, you can add search functionality to your application by searching for text within a database column.

<Admonition type="info">
</TopBlock>

**Note**: Full text search can **only** be used in a Postgres database
## Enabling full-text search

</Admonition>
The full-text search API is currently a Preview feature. To enable this feature, carry out the following steps:

## Enable full-text search
1. Update the [`previewFeatures`](/concepts/components/preview-features) block in your schema to include the `fullTextSearch` preview feature flag:

The full-text Search API is still in **Preview**. You can enable `fullTextSearch` in your Prisma Schema:
```prisma file=schema.prisma
generator client {
provider = "prisma-client-js"
previewFeatures = ["fullTextSearch"]
}
```

1. Update the [`previewFeatures`](/concepts/components/preview-features) block in your schema:
For MySQL, you will also need to include the `fullTextIndex` preview feature flag:

```prisma
```prisma file=schema.prisma highlight=3;add
generator client {
provider = "prisma-client-js"
previewFeatures = ["fullTextSearch"]
previewFeatures = ["fullTextSearch, fullTextIndex"]
}
```

Expand All @@ -34,12 +39,29 @@ The full-text Search API is still in **Preview**. You can enable `fullTextSearch
npx prisma generate
```

After you regenerate your client, a new `search` field will be available on any `String` fields created on your models.
After you regenerate your client, a new `search` field will be available on any `String` fields created on your models. For example, the following search will return all posts that contain the word 'cat'.

```ts
// All posts that contain the word 'cat'.
const result = await prisma.posts.findMany({
where: {
body: {
search: 'cat',
},
},
})
```

## Querying the database

The `search` field uses the database's native querying capabilities under the hood. This means that the exact operations available are also database-specific.

### PostgreSQL

### Example of new <inlinecode>search</inlinecode> field
The following examples demonstrate use of the PostgreSQL 'and' (`&`) and 'or' (`|`) operators:
keerlu marked this conversation as resolved.
Show resolved Hide resolved

```ts
// All posts that contain the words cat or dog.
// All posts that contain the words 'cat' or 'dog'.
const result = await prisma.posts.findMany({
where: {
body: {
Expand All @@ -48,7 +70,7 @@ const result = await prisma.posts.findMany({
},
})

// All drafts that contain the words cat and dog.
// All drafts that contain the words 'cat' and 'dog'.
const result = await prisma.posts.findMany({
where: {
status: 'Draft',
Expand All @@ -59,41 +81,152 @@ const result = await prisma.posts.findMany({
})
```

## Query Format
To get a sense of how the query format works, consider the following text:

**"The quick brown fox jumps over the lazy dog"**

Here's how the following queries would match that text:

| Query | Match? | Description |
| :-------------------------------------- | :----- | :-------------------------------------- |
| `fox & dog` | Yes | The text contains 'fox' and 'dog' |
| `dog & fox` | Yes | The text contains 'dog' and 'fox' |
| `dog & cat` | No | The text contains 'dog' but not 'cat' |
| `!cat` | Yes | 'cat' is not in the text |
| <inlinecode>fox &#124; cat</inlinecode> | Yes | The text contains 'fox' or 'cat' |
| <inlinecode>cat &#124; pig</inlinecode> | No | The text doesn't contain 'cat' or 'pig' |
| `fox <-> dog` | Yes | 'dog' follows 'fox' in the text |
| `dog <-> fox` | No | 'fox' doesn't follow 'dog' in the text |

For the full range of supported operations, see the [PostgreSQL full text search documentation](https://www.postgresql.org/docs/12/functions-textsearch.html).

### MySQL

The following examples demonstrate use of the MySQL 'and' (`+`) and 'not' (`-`) operators:

```ts
// All posts that contain the words 'cat' or 'dog'.
const result = await prisma.posts.findMany({
where: {
body: {
search: 'cat dog',
},
},
})

// All posts that contain the words 'cat' and not 'dog'.
const result = await prisma.posts.findMany({
where: {
body: {
search: '+cat -dog',
},
},
})

The `search` field uses the database's native querying capabilities under the hood. This means that the Prisma Client [supports what the database supports](https://www.postgresql.org/docs/12/functions-textsearch.html).
// All drafts that contain the words 'cat' and 'dog'.
const result = await prisma.posts.findMany({
where: {
status: 'Draft',
body: {
search: '+cat +dog',
},
},
})
```

To get a sense of how the query format works, consider the following text:

**"The quick brown fox jumps over the lazy dog"**

Here's how the following queries would match that text:

| Query | Match? | Description |
| :-------------------------------------- | :----- | :---------------------------------- |
| `fox & dog` | Yes | The text contains fox and dog |
| `dog & fox` | Yes | The text contains dog and fox |
| `dog & cat` | No | The text doesn't contain cat |
| `!cat` | Yes | There is not cat in the text |
| <inlinecode>fox &#124; cat</inlinecode> | Yes | The text contains fox or cat |
| <inlinecode>cat &#124; pig</inlinecode> | No | The text doesn't contain cat or pig |
| `fox <-> dog` | Yes | dog follows fox in the text |
| `dog <-> fox` | No | dog doesn't follow fox in the text |
| Query | Match? | Description |
| :------------- | :----- | :----------------------------------------------------- |
| `+fox +dog` | Yes | The text contains 'fox' and 'dog' |
| `+dog +fox` | Yes | The text contains 'dog' and 'fox' |
| `+dog -cat` | No | The text contains 'dog' but not 'cat' |
| `-cat` | Yes | 'cat' is not in the text |
| `fox dog` | Yes | The text contains 'fox' or 'cat' |
| `-cat -pig` | No | The text does not contain 'cat' or 'pig' |
| `quic*` | Yes | The text contains a word starting with 'quic' |
| `quick fox @2` | Yes | 'fox' starts within a 2 word distance of 'quick' |
| `fox dog @2` | No | 'dog' does not start within a 2 word distance of 'fox' |
| `"jumps over"` | Yes | The text contains the whole phrase 'jumps over' |

## Adding Indexes
MySQL also has `>`, `<` and `~` operators for altering the ranking order of search results. As an example, consider the following two records:

To speed up your full-text queries, you should add an index to your database.
Prisma Migrate currently doesn't support adding search indices, but you can
easily add one yourself.
**1. "The quick brown fox jumps over the lazy dog"**

**2. "The quick brown fox jumps over the lazy cat"**

| Query | Result | Description |
| :---------------- | :----------------------- | :------------------------------------------------------------------------------------------------------ |
| `fox ~cat` | Return 1. first, then 2. | Return all records containing 'fox', but rank records containing 'cat' lower |
| `fox (<cat >dog)` | Return 1. first, then 2. | Return all records containing 'fox', but rank records containing 'cat' lower than rows containing 'dog' |

For the full range of supported operations, see the [MySQL full text search documentation](https://dev.mysql.com/doc/refman/8.0/en/fulltext-boolean.html).
keerlu marked this conversation as resolved.
Show resolved Hide resolved

## Adding indexes

### PostgreSQL

To speed up your full-text queries, you should add an index to your database. Prisma Migrate currently doesn't support adding search indices in PostgreSQL, so this should be added in SQL. For example, the following SQL statement would add an index called `post_body_index` on the `posts_body_index` column of the `posts` table:

```sql
CREATE INDEX post_body_index ON posts USING GIN (body);
```

You can continue using Prisma Migrate as you were before, it will ignore indexes
that it doesn't know about.
For further information, see the [Postgres documentation on indexes](https://www.postgresql.org/docs/12/sql-createindex.html).
keerlu marked this conversation as resolved.
Show resolved Hide resolved

You can continue using Prisma Migrate as you were before, it will ignore indexes that it doesn't know about.

### MySQL

For MySQL, it is necessary to add indexes to any columns you search using the `@@fulltext` argument in the `schema.prisma` file. To do this, the `"fullTextIndex"` preview feature must be enabled.

In the following example, one full text index is added to the `content` field of the `Blog` model, and another is added to both the `content` and `title` fields together:

```prisma file=schema.prisma
generator client {
provider = "prisma-client-js"
previewFeatures = ["fullTextSearch", "fullTextIndex"]
}

model Blog {
id Int @unique
content String
title String

@@fulltext([content])
@@fulltext([content, title])
}
```

The first index allows searching the `content` field for occurrences of the word 'cat':

```ts
const result = await prisma.blogs.findMany({
where: {
content: {
search: 'cat',
keerlu marked this conversation as resolved.
Show resolved Hide resolved
},
},
})
```

The second index allows searching both the `content` and `title` fields for occurrences of the word 'cat' in the `content` and 'food' in the `title`:

## Limitations
```ts
const result = await prisma.blogs.findMany({
where: {
content: {
search: 'cat',
},
title: {
search: 'food',
},
},
})
```

- The `search` field is only supported in PostgreSQL
- You'll need to manage indexes yourself.
However, if you try to search on `title` alone, the search will fail with the error "Cannot find a fulltext index to use for the search" and the message code is P2030, because the index requires a search on both fields.
keerlu marked this conversation as resolved.
Show resolved Hide resolved