search
Skill0 starsChanged 33 days agoMITView on GitHub
Implementing search that works at scale: chooses between PostgreSQL full-text search and Elasticsearch based on requirements, builds tsvector indexes with the right language config, combines full-text with filter predicates, ranks results with ts_rank, adds fuzzy/trigram matching for typo tolerance, designs an Elasticsearch index with correct mappings and analyzers, and keeps the search index in sync with the source of truth. Use when you want to add search, implement full-text search, use PostgreSQL FTS, set up Elasticsearch, add fuzzy search, combine search with filters, rank search results, or keep a search index in sync.
54 other projects use a version of this.
Adopted by
Most recently taken up by
- beCyborg/jadlis-searchtheir own version, first seen Nov 8, 2026
- hydra-db/hydradb-claude-codetheir own version, first seen Nov 8, 2026
- Mahashwetha/Job-search-email-system-claudetheir own version, first seen Nov 5, 2026
- denolfe/dotfilestheir own version, first seen Nov 2, 2026
- Arcadia-1/virtuoso-bridge-litetheir own version, first seen Nov 1, 2026
- jav-ed/skill_Managtheir own version, first seen Nov 1, 2026
Used with
Projects with it also use fetch, integrate and 8 more.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
jtfasulo7Skillfetch
Use this skill when the user wants to retrieve a URL without a full browser session: fetch HTML or JSON from static pages, inspect status codes or headers, follow redirects, or get page source for simple scraping. Prefer it over a browser when JavaScript rendering and page interaction are not needed. Supports proxies and redirect control.
Also in 3 projects that use this
modeled-information-formatSkillintegrate
This skill should be used when the user asks to "integrate mnemonic", "wire plugin", "add memory to plugin", "enable memory capture in plugin", "integrate memory operations", "add mnemonic protocol", "remove mnemonic integration", "rollback plugin integration", or "migrate legacy memory sections". It wires mnemonic memory capture and recall workflows into other Claude Code plugins using sentinel markers.
Also in 3 projects that use this
soilmassSkillexport
Animation export configurations including format selection, resolution, framerate, quality, and optimization settings. Use when rendering animations for web, mobile, video, or social media. Provides tokens for all export parameters across Lottie, GIF, video, and sprite sheet formats.
Also in 4 projects that use this
DockerSkillwrite
Write or edit reader-facing technical prose for immediate comprehension. Use for documentation, PR titles and descriptions, release notes, design documents, user-facing explanations, and substantive comments. Also use when asked to make writing clearer, more natural, less AI-generated, or easier to read. For documentation fixes, handles edits, formatting, self-review, and commits after research identifies what to change. Do not use for code-only tasks with no prose deliverable.
Also in 3 projects that use this
JaimeGBASkillingest
Digest an external source (article, paper, transcript, URL, book chapter) and integrate its content into the knowledge base
Also in 4 projects that use this
ryu111Skillbrowser
agent-browser CLI 瀏覽器自動化指南。E2E 測試、UI 調試、設計驗證。適用於需要實際操作瀏覽器的場景。
Also in 3 projects that use this
Other versions54 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 54
- anthropics/knowledge-work-plugins★ 35ksame name
- exa-labs/exa-mcp-server★ 5.2ksame name
- browserbase/agent-browse★ 3.7ksame name
- spotify/portal-ai-plugins★ 2.8ksame name
- Muuuun/luxas★ 1.2ksame name
- Arcadia-1/virtuoso-bridge-lite★ 1ksame name
- ZimoLiao/scholaraio★ 585same name
- BlockRunAI/blockrun-mcp★ 389same name
- project-numina/numina-lean-agent★ 279same name
- brightdata/skills★ 270same name
44 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository68 other files it gives its agents.
- AGENTS.mdAGENTS.md
- .claude/CLAUDE.mdCLAUDE.md
- accessibilitySkill
- api-designSkill
- api-versioningSkill
- authSkill
- background-jobsSkill
- cachingSkill
- capacity-planningSkill
- changelogSkill
- chaos-engineeringSkill
- ci-cdSkill
- circuit-breakerSkill
- claude-best-practicesSkill
- code-commentsSkill
- code-hygieneSkill
- code-reviewSkill
- code-structureSkill
- commit-styleSkill
- concurrency-and-lockingSkill
- database-transactionsSkill
- data-consistencySkill
- debug-infra-startupSkill
- debug-sessionSkill
- demo-video-creatorSkill
- demo-video-script-creatorSkill
- dependency-injectionSkill
- dependency-updateSkill
- disk-cleanupSkill
- distributed-tracingSkill
- dockerSkill
- env-configSkill
- env-setupSkill
- error-handlingSkill
- event-driven-architectureSkill
- feature-flagsSkill
- file-storageSkill
- graceful-shutdownSkill
- idempotencySkill
- input-validationSkill
- jars-engineering-blogsmithSkill
- jars-workshop-builderSkill
- licensingSkill
- loggingSkill
- memory-and-allocationsSkill
- migrate-dbSkill
- monitoringSkill
- multi-tenancySkill
- notificationsSkill
- openapiSkill
- paginationSkill
- perf-profileSkill
- pr-descriptionSkill
- query-optimizationSkill
- rate-limitingSkill
- refactorSkill
- releaseSkill
- runbook-writerSkill
- secrets-auditSkill
- simplifySkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
5 rules. 0 are common, 3 are distinctive, and 2 are very specific to this project.
0
Common
Most of the 47 edited versions we read keep these.
3
Distinctive
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 23
Default to PostgreSQL FTS: when your data is already in Postgres and you do not need aggregations or advanced relevance tuning.
- Very specific: line 338
Never use LIKE '%term%' — it cannot use a btree index and scans every row.
- Very specific: line 339
Use plainto_tsquery for user input in PostgreSQL — it handles arbitrary text safely without injection.
- Distinctive: line 342
Keep the search index eventually consistent via the outbox pattern — never sync in the request path.
- Distinctive: line 343
Test search quality with real queries from users, not synthetic ones.
“Common” is measured: kept by at least 6 in 10 of the 47 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
search
Pick the right backend first. Add the right index. Never implement search with LIKE '%term%'.
1. Choose the backend
| Requirement | PostgreSQL FTS | Elasticsearch |
| Data already in Postgres | ✅ | Needs sync pipeline |
| Simple keyword search | ✅ | Overkill |
| Multi-language stemming | ✅ | ✅ Better control |
| Typo tolerance (fuzzy) | ✅ pg_trgm | ✅ Built-in |
| Relevance tuning per field | Limited | ✅ |
| Faceted search / aggregations | Hard | ✅ |
| Billions of documents | ❌ | ✅ |
| Real-time autocomplete | Possible | ✅ Better |
Default to PostgreSQL FTS when your data is already in Postgres and you do not need aggregations or advanced relevance tuning. Add Elasticsearch only when Postgres cannot meet the requirements — it is significant operational overhead.
2. PostgreSQL full-text search — the right way
-- Never do this — scans every row, no index, slow at scale
SELECT * FROM products WHERE name LIKE '%wireless headphones%';
-- Full-text search with GIN index — fast, stemmed, rankedStep 1 — add a tsvector column and index:
-- Add a generated tsvector column (PostgreSQL 12+)
ALTER TABLE products
ADD COLUMN search_vector tsvector
GENERATED ALWAYS AS (
setweight(to_tsvector('english', coalesce(name, '')), 'A') ||
setweight(to_tsvector('english', coalesce(description, '')), 'B') ||
setweight(to_tsvector('english', coalesce(brand, '')), 'A')
) STORED;
-- GIN index on the tsvector column
CREATE INDEX idx_products_search ON products USING gin(search_vector);Step 2 — query with tsquery and rank:
-- Basic search
SELECT id, name, ts_rank(search_vector, query) AS rank
FROM products, to_tsquery('english', 'wireless & headphones') query
WHERE search_vector @@ query
ORDER BY rank DESC
LIMIT 20;
-- Phrase search
SELECT id, name
FROM products
WHERE search_vector @@ phraseto_tsquery('english', 'wireless headphones');
-- Prefix search (for autocomplete)
SELECT id, name
FROM products
WHERE search_vector @@ to_tsquery('english', 'wireless:* & head:*');# repositories/product_repository.py
"""
repositories/product_repository.py
------------------------------------
Product search using PostgreSQL full-text search.
Depends on: lib/db.py, lib/logger.py
Used by: services/search_service.py
"""
from lib.logger import get_logger
logger = get_logger(__name__)
async def search_products(
query: str,
filters: dict | None = None,
limit: int = 20,
cursor: str | None = None,
) -> list[dict]:
if not query.strip():
return []
# Sanitise query — convert free text to tsquery safely
# plainto_tsquery handles arbitrary user input without injection risk
params: dict = {"query": query, "limit": limit}
conditions = ["search_vector @@ plainto_tsquery('english', :query)"]
# Combine FTS with structured filters
if filters:
if filters.get("category"):
conditions.append("category = :category")
params["category"] = filters["category"]
if filters.get("max_price"):
conditions.append("price_cents <= :max_price")
params["max_price"] = filters["max_price"]
where = " AND ".join(conditions)
rows = await db.fetch_all(
f"""
SELECT
id, name, price_cents, category,
ts_rank(search_vector, plainto_tsquery('english', :query)) AS rank
FROM products
WHERE {where}
ORDER BY rank DESC, id DESC
LIMIT :limit
""",
params,
)
logger.info("Product search executed. query=%s results=%d", query[:50], len(rows))
return [dict(r) for r in rows]3. Fuzzy / trigram search — typo tolerance
-- Enable pg_trgm extension
CREATE EXTENSION IF NOT EXISTS pg_trgm;
-- GIN trigram index on name (for similarity and ILIKE acceleration)
CREATE INDEX idx_products_name_trgm ON products USING gin(name gin_trgm_ops);
-- Fuzzy match — returns results even with typos
SELECT id, name, similarity(name, 'wireles headphnes') AS sim
FROM products
WHERE similarity(name, 'wireles headphnes') > 0.3
ORDER BY sim DESC
LIMIT 20;Combine FTS + trigram for best results:
-- Exact FTS match boosted, trigram fuzzy as fallback
SELECT id, name,
CASE
WHEN search_vector @@ plainto_tsquery('english', 'wireless headphones')
THEN ts_rank(search_vector, plainto_tsquery('english', 'wireless headphones')) + 1.0
ELSE similarity(name, 'wireless headphones')
END AS combined_rank
FROM products
WHERE
search_vector @@ plainto_tsquery('english', 'wireless headphones')
OR similarity(name, 'wireless headphones') > 0.3
ORDER BY combined_rank DESC
LIMIT 20;4. Elasticsearch — index design and sync
Index mapping — define before indexing any documents:
# lib/es_client.py
"""
lib/es_client.py
-----------------
Elasticsearch index management and search helpers.
Depends on: elasticsearch-py, lib/logger.py
Used by: services/search_service.py, workers/search_sync_worker.py
"""
from elasticsearch import AsyncElasticsearch
from lib.logger import get_logger
logger = get_logger(__name__)
es = AsyncElasticsearch([settings.elasticsearch_url])
PRODUCTS_INDEX = "products_v1"
PRODUCTS_MAPPING = {
"settings": {
"number_of_shards": 1,
"number_of_replicas": 1,
"analysis": {
"analyzer": {
"product_analyzer": {
"type": "custom",
"tokenizer": "standard",
"filter": ["lowercase", "asciifolding", "english_stemmer"],
},
},
"filter": {
"english_stemmer": {"type": "stemmer", "language": "english"},
},
},
},
"mappings": {
"properties": {
"name": {"type": "text", "analyzer": "product_analyzer", "boost": 3},
"brand": {"type": "text", "analyzer": "product_analyzer", "boost": 2},
"description": {"type": "text", "analyzer": "product_analyzer"},
"category": {"type": "keyword"}, # exact match / facet
"price_cents": {"type": "integer"},
"tenant_id": {"type": "keyword"},
"updated_at": {"type": "date"},
}
},
}
async def ensure_index() -> None:
exists = await es.indices.exists(index=PRODUCTS_INDEX)
if not exists:
await es.indices.create(index=PRODUCTS_INDEX, body=PRODUCTS_MAPPING)
logger.info("Elasticsearch index created. index=%s", PRODUCTS_INDEX)
async def search_products(
query: str,
tenant_id: str,
category: str | None = None,
max_price: int | None = None,
page: int = 1,
size: int = 20,
) -> dict:
must_filters = [{"term": {"tenant_id": tenant_id}}]
if category:
must_filters.append({"term": {"category": category}})
if max_price:
must_filters.append({"range": {"price_cents": {"lte": max_price}}})
body = {
"query": {
"bool": {
"must": {
"multi_match": {
"query": query,
"fields": ["name^3", "brand^2", "description"],
"fuzziness": "AUTO", # typo tolerance built-in
"type": "best_fields",
}
},
"filter": must_filters,
}
},
"from": (page - 1) * size,
"size": size,
}
response = await es.search(index=PRODUCTS_INDEX, body=body)
hits = response["hits"]["hits"]
total = response["hits"]["total"]["value"]
logger.info("ES search completed. query=%s total=%d", query[:50], total)
return {
"items": [{"id": h["_id"], **h["_source"], "score": h["_score"]} for h in hits],
"total": total,
"page": page,
"size": size,
}5. Keep the search index in sync
# workers/search_sync_worker.py
"""
workers/search_sync_worker.py
------------------------------
Syncs product changes from PostgreSQL to Elasticsearch.
Triggered by the outbox pattern — listens for product.updated events.
Depends on: lib/es_client.py, repositories/product_repository.py
Used by: queue consumer
"""
from lib.es_client import es, PRODUCTS_INDEX
from lib.logger import get_logger
logger = get_logger(__name__)
async def sync_product(product_id: int) -> None:
product = await product_repo.find_by_id(product_id)
if not product:
# Product deleted — remove from index
await es.delete(index=PRODUCTS_INDEX, id=str(product_id), ignore=[404])
logger.info("Product removed from search index. product_id=%s", product_id)
return
await es.index(
index=PRODUCTS_INDEX,
id=str(product_id),
document={
"name": product["name"],
"brand": product["brand"],
"description": product["description"],
"category": product["category"],
"price_cents": product["price_cents"],
"tenant_id": product["tenant_id"],
"updated_at": product["updated_at"].isoformat(),
},
)
logger.info("Product synced to search index. product_id=%s", product_id)6. Search anti-patterns to avoid
| Anti-pattern | Why it hurts | Fix |
LIKE '%term%' on large tables | Full table scan, no index | PostgreSQL FTS with GIN index |
ILIKE '%term%' | Same — case-insensitive but still full scan | pg_trgm GIN index |
| Indexing everything in Elasticsearch | Index bloat, slow writes | Map only searchable fields; use keyword for filters |
| Syncing ES synchronously in the request | Latency spike on every write | Async via outbox/queue |
| Rebuilding the entire index on every deploy | Downtime for large corpora | Blue-green index alias swap |
wildcard query on unanalysed fields in ES | Extremely slow | Use match with fuzziness instead |
Rules
- Never use
LIKE '%term%'— it cannot use a btree index and scans every row. - Use
plainto_tsqueryfor user input in PostgreSQL — it handles arbitrary text safely without injection. - Weight fields in
tsvectorwithsetweight— name and brand should outrank description. - In Elasticsearch, map filter fields as
keywordand search fields astext— never mix them. - Keep the search index eventually consistent via the outbox pattern — never sync in the request path.
- Test search quality with real queries from users, not synthetic ones. Relevance tuning without real data is guesswork.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

