Use Cases

How to Implement Search Functionality with Claude Code

Learn how to implement search functionality using Claude Code. Includes practical code examples and step-by-step guidance.

検索機能の実装をClaude Codeで加速する

検索機能はユーザー体験に直結する重要な機能です。デバウンス処理、ハイライト表示、フィルタリングなど、考慮すべき点が多くあります。Claude Codeならこれらをまとめて実装できます。

インクリメンタルサーチの実装

リアルタイムで検索結果が更新されるインクリメンタルサーチを作ります。

> デバウンス付きのインクリメンタルサーチコンポーネントをReactで作って。
> 検索結果のキーワードハイライトもお願い。
import { useState, useEffect, useMemo } from "react";

function useDebounce<T>(value: T, delay: number): T {
  const [debounced, setDebounced] = useState(value);
  useEffect(() => {
    const timer = setTimeout(() => setDebounced(value), delay);
    return () => clearTimeout(timer);
  }, [value, delay]);
  return debounced;
}

function highlightText(text: string, query: string) {
  if (!query) return text;
  const regex = new RegExp(`(${query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")})`, "gi");
  const parts = text.split(regex);
  return parts.map((part, i) =>
    regex.test(part) ? <mark key={i} className="bg-yellow-200">{part}</mark> : part
  );
}

interface SearchResult {
  id: string;
  title: string;
  description: string;
}

export function SearchBox() {
  const [query, setQuery] = useState("");
  const [results, setResults] = useState<SearchResult[]>([]);
  const [loading, setLoading] = useState(false);
  const debouncedQuery = useDebounce(query, 300);

  useEffect(() => {
    if (!debouncedQuery) {
      setResults([]);
      return;
    }

    const controller = new AbortController();
    setLoading(true);

    fetch(`/api/search?q=${encodeURIComponent(debouncedQuery)}`, {
      signal: controller.signal,
    })
      .then((res) => res.json())
      .then((data) => {
        setResults(data.results);
        setLoading(false);
      })
      .catch((err) => {
        if (err.name !== "AbortError") setLoading(false);
      });

    return () => controller.abort();
  }, [debouncedQuery]);

  return (
    <div className="relative w-full max-w-lg">
      <input
        type="search"
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="Search by keyword..."
        className="w-full p-3 border rounded-lg"
      />

      {loading && <div className="absolute right-3 top-3">検索中...</div>}

      {results.length > 0 && (
        <ul className="absolute w-full mt-1 bg-white border rounded-lg shadow-lg max-h-96 overflow-y-auto">
          {results.map((item) => (
            <li key={item.id} className="p-3 hover:bg-gray-50 border-b last:border-0">
              <div className="font-semibold">
                {highlightText(item.title, debouncedQuery)}
              </div>
              <div className="text-sm text-gray-600">
                {highlightText(item.description, debouncedQuery)}
              </div>
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

サーバーサイド:SQLによる検索API

バックエンドの検索APIもClaude Codeに依頼できます。

// Next.js App Router APIルート
import { db } from "@/lib/database";

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const q = searchParams.get("q") || "";
  const category = searchParams.get("category");
  const page = parseInt(searchParams.get("page") || "1");
  const limit = 20;

  if (q.length < 2) {
    return Response.json({ results: [], total: 0 });
  }

  const where: any = {
    OR: [
      { title: { contains: q, mode: "insensitive" } },
      { content: { contains: q, mode: "insensitive" } },
    ],
  };

  if (category) {
    where.category = category;
  }

  const [results, total] = await Promise.all([
    db.article.findMany({
      where,
      skip: (page - 1) * limit,
      take: limit,
      orderBy: { createdAt: "desc" },
      select: { id: true, title: true, description: true, category: true },
    }),
    db.article.count({ where }),
  ]);

  return Response.json({
    results,
    total,
    totalPages: Math.ceil(total / limit),
  });
}

静的サイトでのクライアント検索

ブログや文書サイトなら、ビルド時に検索インデックスを生成する方法が有効です。

// 検索インデックス生成スクリプト
import MiniSearch from "minisearch";
import fs from "fs";
import path from "path";

const posts = getAllPosts(); // 記事データの取得

const miniSearch = new MiniSearch({
  fields: ["title", "content", "tags"],
  storeFields: ["title", "slug", "description"],
  searchOptions: {
    boost: { title: 3, tags: 2 },
    fuzzy: 0.2,
    prefix: true,
  },
});

miniSearch.addAll(posts);

fs.writeFileSync(
  path.join("public", "search-index.json"),
  JSON.stringify(miniSearch.toJSON())
);

正規表現を活用した高度なパターンマッチングについてはClaude Codeで正規表現を作成・デバッグする方法も参考にしてください。効率的なプロンプトの書き方は生産性を3倍にする10のTipsで紹介しています。

Zusammenfassung

Claude Codeを使えば、デバウンス、ハイライト、ファセット検索といった検索機能の複雑な要件も、自然言語で伝えるだけで実装できます。フロントエンドからバックエンドまで一貫した設計を短時間で得られます。

詳細はClaude Code公式ドキュメントを参照してください。

#Claude Code #search #全文検索 #React #API