メインコンテンツへスキップ

全文検索

Prisma Clientは、PostgreSQLデータベースではバージョン2.30.0以降、MySQLデータベースではバージョン3.8.0以降で全文検索をサポートしています。全文検索 (FTS) を有効にすることで、データベースのカラム内のテキストを検索してアプリケーションに検索機能を追加できます。

情報

Prisma v6では、FTSがMySQLで一般提供に昇格しました。PostgreSQLでは引き続きプレビュー版であり、fullTextSearchPostgresプレビュー機能フラグの使用が必要です。

PostgreSQLの全文検索を有効にする

全文検索APIは現在プレビュー機能です。この機能を有効にするには、以下の手順を実行します

  1. スキーマ内のpreviewFeaturesブロックを更新し、fullTextSearchPostgresプレビュー機能フラグを含めます

    schema.prisma
    generator client {
    provider = "prisma-client-js"
    previewFeatures = ["fullTextSearchPostgres"]
    }
  2. Prisma Clientを生成する

    npx prisma generate

クライアントを再生成すると、モデルに作成されたすべてのStringフィールドで新しいsearchフィールドが利用可能になります。たとえば、次の検索では「cat」という単語を含むすべての投稿が返されます。

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

: 現在、PostgreSQLの全文検索機能には既知の問題があります。検索クエリが遅い場合は、生のSQLでクエリを最適化できます。

データベースをクエリする

searchフィールドは、内部でデータベースのネイティブなクエリ機能を使用しています。つまり、利用できる正確なクエリオペレーターはデータベース固有です。

PostgreSQL

以下の例は、PostgreSQLの'and' (&) および'or' (|) オペレーターの使用法を示しています

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

// All drafts that contain the words 'cat' and 'dog'.
const result = await prisma.posts.findMany({
where: {
status: 'Draft',
body: {
search: 'cat & dog',
},
},
})

クエリ形式がどのように機能するかを理解するために、以下のテキストを考えてみましょう

"素早い茶色のキツネが怠惰な犬を飛び越える"

以下のクエリがそのテキストとどのように一致するかを以下に示します

クエリ一致?説明
fox & dogはいテキストに「fox」と「dog」が含まれる
dog & foxはいテキストに「dog」と「fox」が含まれる
dog & catいいえテキストに「dog」は含まれるが「cat」は含まれない
!catはいテキストに「cat」が含まれない
fox | catはいテキストに「fox」または「cat」が含まれる
cat | pigいいえテキストに「cat」も「pig」も含まれない
fox <-> dogはいテキストで「fox」の後に「dog」が続く
dog <-> foxいいえテキストで「dog」の後に「fox」が続かない

サポートされている操作の全範囲については、PostgreSQL全文検索ドキュメントを参照してください。

MySQL

以下の例は、MySQLの'and' (+) および'not' (-) オペレーターの使用法を示しています

// 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',
},
},
})

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

クエリ形式がどのように機能するかを理解するために、以下のテキストを考えてみましょう

"素早い茶色のキツネが怠惰な犬を飛び越える"

以下のクエリがそのテキストとどのように一致するかを以下に示します

クエリ一致?説明
+fox +dogはいテキストに「fox」と「dog」が含まれる
+dog +foxはいテキストに「dog」と「fox」が含まれる
+dog -catはいテキストに「dog」は含まれるが「cat」は含まれない
-catいいえマイナス演算子は単独では使用できません (以下の注を参照)
fox dogはいテキストに「fox」または「dog」が含まれる
quic*はいテキストに「quic」で始まる単語が含まれる
quick fox @2はい「fox」が「quick」から2単語以内に始まる
fox dog @2いいえ「dog」が「fox」から2単語以内に始まらない
"jumps over"はいテキストに「jumps over」というフレーズ全体が含まれる

: マイナス演算子 (-) は、他の検索語によって一致した行を除外するためにのみ機能します。したがって、-が付加された語のみを含むブールモード検索は空の結果を返します。「除外された語のいずれかを含む行を除くすべての行」を返すわけではありません。

MySQLには、検索結果のランキング順序を変更するための><、および~演算子もあります。例として、次の2つのレコードを考えてみましょう

1. "素早い茶色のキツネが怠惰な犬を飛び越える"

2. "素早い茶色のキツネが怠惰な猫を飛び越える"

クエリ結果説明
fox ~catまず1を返し、次に2を返す「fox」を含むすべてのレコードを返す。ただし、「cat」を含むレコードはランクを低くする
fox (<cat >dog)まず1を返し、次に2を返す「fox」を含むすべてのレコードを返す。ただし、「cat」を含むレコードは「dog」を含む行よりもランクを低くする

サポートされている操作の全範囲については、MySQL全文検索ドキュメントを参照してください。

_relevanceによる結果のソート

警告

関連度によるソートは、PostgreSQLおよびMySQLでのみ利用可能です。

Prisma ClientのデフォルトのorderBy動作に加えて、全文検索では指定された文字列または文字列に対する関連度によるソートも追加されます。たとえば、投稿をタイトルの'database'という用語への関連度で並べ替える場合は、次のように使用できます

const posts = await prisma.post.findMany({
orderBy: {
_relevance: {
fields: ['title'],
search: 'database',
sort: 'asc'
},
},
})

インデックスの追加

PostgreSQL

Prisma Clientは現在、全文検索を高速化するためのインデックスの使用をサポートしていません。これに関する既存のGitHub Issueがあります。

MySQL

MySQLの場合、schema.prismaファイルの@@fulltext引数を使用して、検索するすべてのカラムにインデックスを追加する必要があります。

以下の例では、Blogモデルのcontentフィールドに1つの全文インデックスが追加され、もう1つはcontentフィールドとtitleフィールドの両方にまとめて追加されています

schema.prisma
generator client {
provider = "prisma-client-js"
}

model Blog {
id Int @unique
content String
title String

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

最初のインデックスは、contentフィールド内で「cat」という単語の出現を検索することを可能にします

const result = await prisma.blogs.findMany({
where: {
content: {
search: 'cat',
},
},
})

2番目のインデックスは、contentフィールドで「cat」、titleフィールドで「food」という単語の出現を、contenttitleの両方のフィールドで検索することを可能にします

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

しかし、titleのみで検索しようとすると、「Cannot find a fulltext index to use for the search」というエラーで検索が失敗し、メッセージコードはP2030となります。これは、検索に両方のフィールドに対するインデックスが必要であるためです。

生のSQLによる全文検索

全文検索は現在プレビュー版であり、既知の問題により、検索クエリが遅くなる場合があります。その場合、TypedSQLを使用してクエリを最適化できます。

PostgreSQL

TypedSQLを使用すると、PostgreSQLのto_tsvectorto_tsqueryを使用して検索クエリを表現できます。

SELECT * FROM "Blog" WHERE to_tsvector('english', "Blog"."content") @@ to_tsquery('english', ${term});

: 言語設定によっては、SQLステートメント内のenglishを別の言語に置き換えることができます。

検索語にワイルドカードを含めたい場合は、次のようにします

SELECT * FROM "Blog" WHERE to_tsvector('english', "Blog"."content") @@ to_tsquery('english', ${term});

MySQL

MySQLでは、次のように検索クエリを表現できます

SELECT * FROM Blog WHERE MATCH(content) AGAINST(${term} IN NATURAL LANGUAGE MODE);
© . All rights reserved.