frappe-core-search について
Frappe の開発者向けに、リンクフィールド、グローバル、Web 全文、SQLite の検索を扱うスキルです。各方式の選択、設定、カスタマイズを説明します。
主な機能
- 01リンクフィールドのオートコンプリート設定
- 02リンク検索クエリのカスタマイズ
- 03グローバル検索インデックスの設定
- 04SQLiteSearch のスコアリングとスペル修正
- 05検索サブシステムの選択指針
活用シーン
- 01DocType レコードを分かりやすいフィールドで検索
- 02フィルターでリンクフィールドの結果を調整
- 03DocType 横断検索の設定
- 04プロジェクトデータの SQLiteSearch インデックス作成
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ impertio-studio/ frappe_claude_skill_package --skill frappe-core-searchインストール後、Claude Code で/frappe-core-searchを入力して読み込まれたか確認します。
同梱ファイルreferences/global-website-search.mdreferences/link-search-api.md
詳細
Frappe の検索システム
4 つの検索サブシステム
| サブシステム | モジュール | 用途 | リアルタイム? |
|---|---|---|---|
| リンクフィールド検索 | frappe.desk.search | リンクフィールドの自動補完 | はい |
| グローバル検索 | frappe.utils.global_search | DocType をまたいだ検索(デスクトップ+ウェブ) | いいえ(15 分ごとに同期) |
| FullTextSearch | frappe.search.full_text_search | Whoosh ベースのインデックス(ウェブサイト) | 再構築時 |
| SQLiteSearch [v15+] | frappe.search.sqlite_search | スコアリングとスペルチェック機能を備えた FTS5 | はい(5 分ごとにキュー処理) |
判断ツリー
What search do you need?
│
├─ Link field autocomplete (user types in a Link field)?
│ ├─ Default behavior sufficient → Configure search_fields on DocType
│ └─ Custom logic needed → standard_queries hook or query parameter
│
├─ Cross-doctype search (user searches for anything)?
│ ├─ Desk users → Global Search (auto-enabled)
│ │ └─ Set in_global_search=1 on important fields
│ └─ Website visitors → web_search() or WebsiteSearch (Whoosh)
│
├─ Custom full-text search for your app [v15+]?
│ └─ SQLiteSearch subclass + sqlite_search hook
│ → Spelling correction, recency boost, custom scoring
│
└─ Awesomebar customization?
└─ Client-side: override build_options or use search dialog
リンクフィールド検索
search_fields の設定(最も一般的な要件)
# In DocType JSON or via customize form
{
"search_fields": "customer_name, customer_group",
"title_field": "customer_name",
"show_title_field_in_link": 1
}
必ず search_fields を設定してください — 設定しない場合、ユーザーは name(多くの場合、CUST-001 のようなコード)でしか検索できません。
リンク検索の仕組み
- ユーザーがリンクフィールドに入力 →
search_link(doctype, txt)を呼び出します name+title_field+search_fieldsを検索します- 使用可能なフィールド型:Data、Text、Small Text、Long Text、Link、Select、Autocomplete、Read Only、Text Editor
- 前方一致は部分一致より上位に表示されます
enabled/disabledフィールドを自動的に考慮します
カスタムリンククエリ
# hooks.py — override search for a specific DocType
standard_queries = {
"Customer": "my_app.queries.customer_query"
}
# my_app/queries.py — MUST be @frappe.whitelist()
@frappe.whitelist()
def customer_query(doctype, txt, searchfield, start, page_length, filters,
as_dict=False, reference_doctype=None,
ignore_user_permissions=False):
# Return list of dicts: [{"value": name, "description": label}, ...]
return frappe.db.sql("""
SELECT name, customer_name as description
FROM `tabCustomer`
WHERE (name LIKE %(txt)s OR customer_name LIKE %(txt)s)
AND status = 'Active'
ORDER BY customer_name
LIMIT %(start)s, %(page_length)s
""", {"txt": f"%{txt}%", "start": start, "page_length": page_length},
as_dict=True)
フィールドごとのクエリの上書き
// In Client Script or Form JS
frappe.ui.form.on("Sales Order", {
setup(frm) {
frm.set_query("customer", () => ({
filters: { status: "Active", territory: frm.doc.territory }
}));
}
});
グローバル検索
有効化
検索対象にする DocType フィールドで in_global_search = 1 を設定します。
仕組み
- インデックス対象のフィールドは
__global_searchテーブルに保存されます - Redis キューを介して 15 分ごとに同期されます
- DB ネイティブの全文検索を使用します:MariaDB
MATCH...AGAINST、PostgreSQLTSVECTOR - 権限に基づいて結果が絞り込まれます
インデックスの再構築
# Rebuild for specific DocType
from frappe.utils.global_search import rebuild_for_doctype
rebuild_for_doctype("Sales Order")
# Rebuild everything
from frappe.utils.global_search import rebuild
rebuild()
hooks.py の設定
# Default doctypes for global search
global_search_doctypes = {
"Default": [
{"doctype": "Contact"},
{"doctype": "Customer"},
{"doctype": "Sales Order"},
]
}
SQLiteSearch [v15+]
カスタム検索の作成
# my_app/search.py
from frappe.search.sqlite_search import SQLiteSearch
class ProjectSearch(SQLiteSearch):
INDEX_SCHEMA = {
"metadata_fields": ["project", "owner", "status"],
"tokenizer": "unicode61 remove_diacritics 2 tokenchars '-_'",
}
INDEXABLE_DOCTYPES = {
"Task": {
"fields": ["name", {"title": "subject"}, {"content": "description"},
"modified", "project"],
"filters": {"status": ("!=", "Cancelled")}
},
"Project": {
"fields": ["name", {"title": "project_name"}, {"content": "notes"},
"modified", "status"],
}
}
def get_search_filters(self, query, scope=None):
"""Permission filtering — return additional WHERE conditions"""
return {}
hooks.py への登録
sqlite_search = ['my_app.search.ProjectSearch']
機能(自動)
- スペル修正:トライグラムベースのファジーマッチング
- 新しさによるブースト:1.8 倍(24 時間)→ 1.5 倍(7 日)→ 1.2 倍(30 日)→ 1.1 倍(90 日)
- 再開可能なインデックス作成:進捗を追跡し、アトミックに置き換えます
- 自動スケジュール設定:3 時間ごとにビルド、5 分ごとにキュー処理、ドキュメントのイベントで更新をトリガーします
アンチパターン
| 絶対にしない | 必ず行う | 理由 |
|---|---|---|
DocType で search_fields を省略する | ユーザーにわかりやすい名前を指定するため search_fields を設定する | ユーザーが名前コードでレコードを見つけられません |
@frappe.whitelist() のないカスタムクエリを使う | @frappe.whitelist() を付ける | ひそかに失敗します — セキュリティチェックで拒否されます |
| 検索でパラメーターなしの生 SQL を使う | パラメーター化クエリ(%(txt)s)を使う | SQL インジェクションのリスクがあります |
| グローバル検索ですべてのフィールドをインデックス化する | 重要なフィールドにのみ in_global_search=1 を設定する | テーブルが肥大化し、15 分ごとの同期が遅くなります |
| リアルタイム検索にグローバル検索を使う | リアルタイム検索にはリンクフィールド検索を使う | グローバル検索には 15 分の同期遅延があります |
SQLiteSearch で get_search_filters() を省略する | 権限によるフィルタリングを実装する | アクセス権限に関係なく、すべての結果が返されます |
| キャンセル済み/削除済みのドキュメントをインデックス化する | INDEXABLE_DOCTYPES に filters を設定する | 古い検索結果がユーザーを混乱させます |
バージョンの違い
| 機能 | v14 | v15+ |
|---|---|---|
| リンク検索のキャッシュ | -- | @http_cache(max_age=60) |
link_fieldname パラメーター | -- | 追加 |
page_length のデフォルト値 | 20 | 10 |
| SQLiteSearch(FTS5) | -- | 完全実装 |
| スペル修正 | -- | トライグラムベース |
| 新しさによるブースト | -- | 時間ベースの倍率 |
sqlite_search フック | -- | 利用可能 |
| グローバル検索 | はい | はい |
| Whoosh FullTextSearch | はい | はい(レガシー) |
参照ファイル
- リンク検索 API — search_link、search_widget、カスタムクエリ
- グローバル検索とウェブサイト検索 — グローバル検索、WebsiteSearch、SQLiteSearch
コメント
Markdown 対応