未分類
Nuxt Content検索機能のMDC AST対応修正
問題
検索ページで以下の問題が発生していました:
- 「SEO」で検索: タイトルに含まれているのに0件
- 「赤字」で検索: 本文に何度も出現しているのに0件
一部のキーワード(「Excel」「繰越欠損金」など)は検索できるが、他のキーワードは検索できない状態でした。
原因調査
1. FlexSearchの設定確認
最初は FlexSearch の tokenize: 'forward' が短い単語に対応していないと考え、tokenize: 'full' に変更しました。
- ✅ 「SEO」が検索可能になった
- ❌ 「赤字」はまだ0件
2. 本文抽出の問題を発見
デバッグログで kurikoshi-kessonkin-calculation-module の記事を調査:
{
bodyLength: 115, // わずか115文字!
bodyPreview: " ", // 空白ばかり
has赤字InBody: false
}
本文が正しく抽出されていないことが判明しました。
3. AST構造の違いを発見
さらに調査を進めると、ASTの構造が想定と異なることが判明しました:
// 想定していた形式(従来のAST)
{
type: 'text',
value: 'テキスト内容'
}
// 実際の形式(MDC配列形式)
["h1", {id: "excelで繰越欠損金..."}, "Excelで繰越欠損金を自動計算!..."]
Nuxt ContentはMDC (Markdown Components) 形式でASTを構築しており、配列形式 ["tag", {attributes}, ...children] を使用していました。
既存の extractTextFromAST 関数は従来の AST 形式しか想定しておらず、MDC 配列形式のノードからテキストを抽出できていませんでした。
修正内容
extractTextFromAST 関数の改善
3種類のノード形式に対応するよう修正しました:
// ASTからテキストを再帰的に抽出
function extractTextFromAST(nodes) {
if (!nodes || !Array.isArray(nodes)) return ''
return nodes.map(node => {
// 1. 文字列ノードの場合
if (typeof node === 'string') {
return node
}
// 2. 配列ノード(MDC形式: ["tag", {attrs}, ...children])の場合
if (Array.isArray(node)) {
// 最初の要素はタグ名、2番目は属性、3番目以降が子要素
const children = node.slice(2)
return extractTextFromAST(children)
}
// 3. オブジェクトノード(従来のAST形式)の場合
if (node.type === 'text') {
return node.value || ''
}
if (node.children) {
return extractTextFromAST(node.children)
}
return ''
}).join(' ')
}
対応したノード形式
| 形式 | 例 | 説明 |
|---|---|---|
| 文字列 | "テキスト" | 直接のテキストコンテンツ |
| MDC配列 | ["h1", {id: "..."}, "見出し"] | Nuxt ContentのMDC形式 |
| オブジェクト | {type: 'text', value: '...'} | 従来のAST形式 |
結果
修正後の本文抽出結果:
{
bodyLength: 3874, // 115 → 3874文字に改善!
bodyPreview: "Excelで繰越欠損金を自動計算!...",
has赤字InBody: true // ✅ 検出成功
}
検索結果の改善
- ✅ 「SEO」検索: 5件ヒット(FlexSearch の tokenize: 'full' 効果)
- ✅ 「赤字」検索: 1件ヒット(本文抽出の修正効果)
- ✅ キーワードハイライト: スニペット内で正しく強調表示
学んだこと
1. Nuxt Content の AST 形式
Nuxt Content は MDC (Markdown Components) を使用しており、AST は配列形式 ["tag", {attrs}, ...children] で構築されます。従来の {type, value, children} 形式とは異なります。
2. デバッグの重要性
- 症状: 一部のキーワードだけ検索できない
- 仮説1: FlexSearch の設定問題 → 一部解決
- 仮説2: 本文抽出の問題 → 根本原因を発見
段階的なデバッグログの追加が問題の特定に有効でした。
3. 柔軟な実装
複数の形式に対応することで、将来の変更にも強い実装になりました:
- MDC配列形式(現在の主要形式)
- 従来のAST形式(後方互換性)
- 文字列ノード(エッジケース対応)
関連ファイル
apps/web/app/pages/search.vue:177-205-extractTextFromAST関数