Claude Code से Search Functionality बनाना: Postgres, Meilisearch और Algolia
Claude Code के साथ production search बनाएं: requirements, index, sync job, filters, debounce UI, tests और rollout.
Search सिर्फ input box नहीं है
Search functionality वह अनुभव है जिसमें user की query से matching candidates मिलते हैं, फिर filters, sorting और highlight के साथ result वापस आता है। सिर्फLIKE '%term%' चलाना internal table के लिए ठीक हो सकता है, लेकिन blog, docs, course catalog या SaaS product में यह PV growth और monetization के लिए काफी नहीं है।
Masa का व्यावहारिक अनुभव यह है कि UI पहले बनाने से rework बढ़ता है। पहले decide करें कि कौन से fields index होंगे, draft और private content कैसे हटेंगे, locale URLs कैसे बनेंगे, ranking कैसे होगी, sync कब चलेगा और zero-result queries को content improvement में कैसे बदला जाएगा। Claude Code तेज़ है, लेकिन उसे precise requirements चाहिए।
Related guides: Claude Code Algolia search, Claude Code API development, और Claude Code performance optimization।
Use cases पहले साफ करें
| Use case | Examples | Important point | Suggested backend |
|---|---|---|---|
| Content search | blog, FAQ, docs | title weight, summary, tags, locale | Postgres full-text या Meilisearch |
| Catalog search | products, courses, templates | facets, sorting, synonyms, analytics | Meilisearch या Algolia |
| Admin search | customers, invoices, logs | permissions, exact filters, audit | Postgres first |
| Multilingual search | Hindi, English, Japanese pages | locale separation, local SEO keywords | Meilisearch या Algolia |
Small site में data अगर Postgres में है, तो full-text search से शुरुआत practical है। Typo tolerance, facets और better relevance चाहिए तो Meilisearch अच्छा step है। Search अगर revenue, lead या course sale से directly जुड़ा है, तो Algolia useful होता है।
Claude Code को देने वाला requirements prompt
You are implementing production search in an existing Next.js app.
Goal:
- Search published articles and increase content discovery.
- Support query, locale, category, and tag filters.
- Search title, summary, tags, and body, with title ranked highest.
- Return fields needed for result cards and highlighting.
Constraints:
- Never return drafts, private records, emails, internal notes, or restricted content.
- Do not expose admin or write API keys to the browser.
- Use 300 ms debounce and AbortController in the UI.
- Log zero-result queries, slow searches, and clicked results.
Deliverables:
- Decision note comparing Postgres full-text, Meilisearch, and Algolia.
- Index schema.
- Sync job.
- /api/search route.
- React search UI.
- Tests and rollout checklist.
Claude Code से पहले existing schema, MDX frontmatter, auth rules और URL structure पढ़वाएं। Search में सबसे बड़ा risk गलत ranking नहीं, बल्कि private data का searchable हो जाना है।
Backend कैसे चुनें
PostgreSQL की official Full Text Search docstsvector, tsquery और ranking समझाती हैं। Meilisearch का quick start और filtering, sorting, faceting content search के लिए आसान है। Algolia के InstantSearch.js और React InstantSearch widgets conversion-focused search UI में मजबूत हैं।
Postgres index schema
CREATE TABLE IF NOT EXISTS articles (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
slug text NOT NULL UNIQUE,
locale text NOT NULL,
status text NOT NULL CHECK (status IN ('draft', 'published', 'private')),
title text NOT NULL,
summary text NOT NULL,
body text NOT NULL,
category text NOT NULL,
tags text[] NOT NULL DEFAULT '{}',
popularity integer NOT NULL DEFAULT 0,
updated_at timestamptz NOT NULL DEFAULT now(),
search_vector tsvector GENERATED ALWAYS AS (
setweight(to_tsvector('simple', coalesce(title, '')), 'A') ||
setweight(to_tsvector('simple', coalesce(summary, '')), 'B') ||
setweight(to_tsvector('simple', coalesce(array_to_string(tags, ' '), '')), 'B') ||
setweight(to_tsvector('simple', coalesce(body, '')), 'C')
) STORED
);
CREATE INDEX IF NOT EXISTS articles_search_vector_idx
ON articles USING GIN (search_vector);
CREATE INDEX IF NOT EXISTS articles_locale_status_idx
ON articles (locale, status, updated_at DESC);
यहाँ title को सबसे ज्यादा weight मिलता है, फिर summary और tags, फिर body। Hindi content के लिए better tokenization की जरूरत हो सकती है, लेकिन early stage में title, summary और tags पर अच्छा काम करने से उपयोगी परिणाम मिल सकते हैं।
Meilisearch sync job
Database या CMS source of truth रहे। Search index में सिर्फ public fields भेजें।
// scripts/sync-meilisearch.ts
import "dotenv/config";
import { MeiliSearch } from "meilisearch";
type ArticleRecord = {
id: string;
title: string;
summary: string;
body: string;
locale: string;
status: "published";
category: string;
tags: string[];
url: string;
popularity: number;
updatedAtTimestamp: number;
};
const client = new MeiliSearch({
host: process.env.MEILISEARCH_HOST ?? "http://127.0.0.1:7700",
apiKey: process.env.MEILISEARCH_ADMIN_KEY
});
const index = client.index<ArticleRecord>("articles");
await index.updateSettings({
searchableAttributes: ["title", "summary", "body", "tags"],
filterableAttributes: ["locale", "status", "category", "tags"],
sortableAttributes: ["updatedAtTimestamp", "popularity"],
displayedAttributes: ["id", "title", "summary", "locale", "category", "tags", "url"]
});
const task = await index.addDocuments(
[
{
id: "hi_claude-code-search-functionality",
title: "Claude Code से Search Functionality बनाना",
summary: "Backend choice, index, UI, tests और rollout पर practical guide.",
body: "MDX या CMS से निकाला गया public article text.",
locale: "hi",
status: "published",
category: "use-cases",
tags: ["Claude Code", "search functionality", "full-text search"],
url: "/hi/blog/claude-code-search-functionality",
popularity: 18,
updatedAtTimestamp: 1780272000
}
],
{ primaryKey: "id" }
);
console.log(`Queued Meilisearch task ${task.taskUid}`);
Facets कम रखें। Content search के लिएcategory, tags, औरlocale काफी होते हैं। Permission filters को server-side या restricted search key में रखें।
Debounce वाला React UI
// components/ArticleSearchBox.tsx
"use client";
import { useEffect, useMemo, useState } from "react";
type SearchHit = {
id: string;
title: string;
summary: string;
url: string;
category: string;
};
function useDebounce<T>(value: T, delayMs: number) {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const timer = window.setTimeout(() => setDebounced(value), delayMs);
return () => window.clearTimeout(timer);
}, [value, delayMs]);
return debounced;
}
export function ArticleSearchBox({ locale = "hi" }: { locale?: string }) {
const [query, setQuery] = useState("");
const [category, setCategory] = useState("");
const [hits, setHits] = useState<SearchHit[]>([]);
const [loading, setLoading] = useState(false);
const debouncedQuery = useDebounce(query, 300);
const params = useMemo(() => {
const next = new URLSearchParams({ q: debouncedQuery, locale });
if (category) next.set("category", category);
return next;
}, [category, debouncedQuery, locale]);
useEffect(() => {
if (debouncedQuery.trim().length < 2) {
setHits([]);
return;
}
const controller = new AbortController();
setLoading(true);
fetch(`/api/search?${params.toString()}`, { signal: controller.signal })
.then((response) => {
if (!response.ok) throw new Error("Search request failed");
return response.json();
})
.then((data: { hits: SearchHit[] }) => setHits(data.hits))
.catch((error) => {
if (error.name !== "AbortError") console.error(error);
})
.finally(() => setLoading(false));
return () => controller.abort();
}, [debouncedQuery, params]);
return (
<section aria-label="Article search">
<input
aria-label="Search keywords"
type="search"
value={query}
onChange={(event) => setQuery(event.target.value)}
placeholder="Search Claude Code articles"
/>
<select aria-label="Category" value={category} onChange={(event) => setCategory(event.target.value)}>
<option value="">All</option>
<option value="use-cases">Use cases</option>
<option value="advanced">Advanced</option>
</select>
{loading && <p>Searching...</p>}
<ul>
{hits.map((hit) => (
<li key={hit.id}>
<a href={hit.url}>{hit.title}</a>
<p>{hit.summary}</p>
</li>
))}
</ul>
</section>
);
}
Tests, rollout और pitfalls
Common pitfalls: drafts को index करना, admin key browser में रखना, private fields search provider को भेजना, बहुत ज्यादा synonyms जोड़ना, हर DB column को facet बनाना, और zero-result queries को review न करना।
// tests/search-query.test.ts
import { describe, expect, it } from "vitest";
function shouldSearch(query: string) {
return query.trim().length >= 2 && query.length <= 80;
}
describe("search request rules", () => {
it("rejects empty and one-character queries", () => {
expect(shouldSearch("")).toBe(false);
expect(shouldSearch("a")).toBe(false);
expect(shouldSearch("api")).toBe(true);
});
});
Rollout से पहले public-only index, zero-result UI, p95 latency, query length limit, logs में personal data न होना और mobile layout check करें। Launch के बाद हर week zero-result और low-click queries को Claude Code को देकर title, synonyms, internal links और new article topics सुधारें।
ClaudeCodeLab search design, Claude Code training और implementation review में मदद करता है। Structured help चाहिए तो training and consultation page देखें।
Summary
Search implementation का सही क्रम है: requirements, backend choice, index schema, sync job, filters/facets, debounce UI, tests और rollout। Practical result यह रहा कि indexed fields और returned fields को जल्दी सीमित करने से later fixes कम हुए, और zero-result logs ने PV growth के लिए सबसे उपयोगी content ideas दिए।
मुफ़्त PDF: Claude Code cheatsheet
Email डालें और commands, review habits तथा safe workflow वाली एक-page PDF पाएँ.
हम आपका data सुरक्षित रखते हैं और spam नहीं भेजते.
लेखक के बारे में
Masa
Claude Code workflow और team adoption पर काम करने वाला engineer.
संबंधित लेख
Claude Code Obsidian to CLAUDE.md workflow: context बार-बार न समझाएं
Obsidian notes को CLAUDE.md operating notes में बदलकर Claude Code sessions को resume करना आसान बनाएं.
Claude Code Revenue CTA Routing: article से PDF, Gumroad और consultation तक
Reader intent के आधार पर free PDF, Gumroad products और consultation तक CTA route करने वाला workflow.
Claude Code टीम हैंडऑफ नियम: review proof, permissions, rollback और revenue path
Claude Code टीम काम के लिए evidence, permission rules, rollback, free PDF, Gumroad और consultation path वाला handoff.