search-integration
Skill8 starsChanged 9 days agoMITView on GitHub
Adds and tunes full-text search - choosing database full-text search vs a search engine, Elasticsearch/OpenSearch mappings and analyzers, keeping the index in sync with the database (outbox, CDC, bulk indexing), zero-downtime reindexing with aliases, bool queries with filters, deep pagination with search_after, aggregations and facets, measured relevance tuning, security and monitoring. Use when building search, autocomplete or facets, search results are wrong or slow, changing an index mapping, or the index drifts from the database. Not for vector-only retrieval for LLMs (see rag-systems).
1 other project uses a version of this.
Adopted by
Most recently taken up by
- kangarooking/system-prompt-skillstheir own version, first seen May 4, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- kangarooking/system-prompt-skills★ 178same name
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 repository60 other files it gives its agents.
- api-designSkill
- auth-implementationSkill
- background-jobsSkill
- building-agentsSkill
- caching-strategiesSkill
- ci-cd-pipelinesSkill
- cloud-cost-optimizationSkill
- codebase-onboardingSkill
- code-review-changesSkill
- concurrency-patternsSkill
- database-migrationsSkill
- database-schema-designSkill
- database-testingSkill
- data-pipelinesSkill
- data-privacy-retentionSkill
- dependency-upgradesSkill
- design-docsSkill
- design-systemsSkill
- dockerfilesSkill
- domain-driven-designSkill
- event-driven-architectureSkill
- file-storage-uploadsSkill
- frontend-securitySkill
- git-workflowSkill
- graphql-apisSkill
- grpc-servicesSkill
- incident-responseSkill
- infrastructure-as-codeSkill
- internationalizationSkill
- kubernetes-deploymentsSkill
- large-scale-migrationsSkill
- legacy-refactoringSkill
- llm-evalsSkill
- llm-integrationSkill
- mcp-serversSkill
- micro-frontendsSkill
- ml-experimentsSkill
- monorepo-managementSkill
- nosql-designSkill
- observabilitySkill
- orm-patternsSkill
- performance-profilingSkill
- postgres-operationsSkill
- rag-systemsSkill
- react-patternsSkill
- realtime-apisSkill
- resilience-patternsSkill
- root-cause-debuggingSkill
- security-auditSkill
- seo-and-ssrSkill
- sql-optimizationSkill
- system-designSkill
- test-strategySkill
- threat-modelingSkill
- typescript-patternsSkill
- unit-testingSkill
- verify-before-doneSkill
- web-accessibilitySkill
- web-performanceSkill
- writing-skillsSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
16 rules. About 0 are common, 10 are distinctive, and 6 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈10
Distinctive · estimate
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 14
Search results are irrelevant, missing recent changes, or slow.
- Distinctive: line 18
Not for: embedding-based retrieval for LLM features (see the rag-systems skill, which covers hybrid search for RAG), or SQL query tuning …
- Distinctive: line 32
Default: start with database full-text search when the corpus is modest and needs are simple.
- Distinctive: line 44
Monitor: search latency, indexing errors, sync lag and cluster health.
- Very specific: line 62
Use the _analyze API to see exactly which tokens an analyzer produces before choosing it.
- Very specific: line 75
Use the database primary key as the document _id, so re-indexing the same row overwrites instead of duplicating.
- Very specific: line 78
Use the bulk API, start with a few MB per request and tune.
- Distinctive: line 80
Keep a full rebuild job that recreates the index from the database.
- Very specific: line 86
Put exact constraints in bool.filter: tenant, permissions, status, price ranges.
- Very specific: line 87
Put user text in bool.must / should: multi_match across fields with boosts (title^3, description), fuzziness: "AUTO" for typo tolerance, …
- Very specific: line 89
Avoid leading wildcards (*shoe) and regex queries on large fields.
- Distinctive: line 110
Watch online metrics: after release: click-through on top results, zero-result rate, query reformulation rate, and conversions where …
- Distinctive: line 116
Never expose the cluster to the internet or to browsers directly.
- Distinctive: line 134
Querying the concrete index name: instead of an alias: every mapping change becomes downtime.
- Distinctive: line 135
Tenant filter added in the UI layer: only: data leaks through other code paths.
- Distinctive: line 145
Every query enforces tenant and permission filters on the server.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Search Integration
The database is the source of truth; the search index is a derived copy you must be able to rebuild at any time. Design the mapping on purpose, keep the index in sync through a reliable pipeline, and tune relevance against measured judgments, not impressions from a few test queries.
When to use
- Adding search, autocomplete, filtering or facets to a product.
- Search results are irrelevant, missing recent changes, or slow.
- Changing an index mapping or analyzer, or upgrading the engine.
- The search index and the database disagree.
Not for: embedding-based retrieval for LLM features (see the rag-systems skill, which covers hybrid search for RAG), or SQL query tuning (see sql-optimization).
Database full-text search or a search engine?
| Need | Database full-text (e.g. PostgreSQL tsvector + GIN, pg_trgm; MySQL FULLTEXT) | Search engine (Elasticsearch, OpenSearch, Solr, Meilisearch, Typesense) |
| Simple keyword search on one or two tables | Good | Overkill |
| Strong consistency with writes | Native (same transaction) | Eventually consistent (refresh delay plus sync lag) |
| Typo tolerance, synonyms, per-language analysis | Limited | Built in |
| Relevance tuning (boosts, function scoring) | Limited | Rich |
| Facets and aggregations over large result sets | Slow at scale | Built for it |
| Isolate heavy search load from the primary database | No | Yes |
| Operational cost | None extra | A cluster to run, secure, monitor and keep in sync |
Default: start with database full-text search when the corpus is modest and needs are simple. Move to an engine when you need relevance tuning, typo tolerance, facets at scale, or load isolation.
Engines in brief: Elasticsearch / OpenSearch (Lucene-based, most flexible, most to operate), Solr (Lucene-based, mature), Meilisearch / Typesense (simpler, typo-tolerant instant search for smaller datasets). Managed offerings exist for most of them. The rest of this skill uses Elasticsearch/OpenSearch terms; the principles carry over.
Workflow
- Write down the search requirements: which fields are searched, which are filtered, sorted and faceted, languages, expected corpus size and query rate, and how fresh results must be (seconds vs minutes).
- Design an explicit mapping (below). Set
dynamictostrict(reject unknown fields) orfalse(store but don't index them). - Build the sync pipeline from the database to the index, plus a full rebuild path.
- Write queries with filters for exact constraints and scoring clauses for text.
- Create a judgment list and measure relevance before and after every tuning change.
- Put the index behind an alias from day one, so mapping changes are zero-downtime reindexes.
- Monitor search latency, indexing errors, sync lag and cluster health.
Concrete mapping JSON, query examples and a step-by-step reindex runbook are in references/mapping-and-reindex.md. Read it when creating an index, writing queries, or changing a mapping.
Mapping rules
| Field use | Mapping |
| Full-text search (titles, descriptions) | text with a language analyzer (e.g. english) |
| Exact match, filters, sorting, aggregations (status, tenant ID, tags, SKUs) | keyword (add a lowercase normalizer for case-insensitive matching) |
| Both search and exact/sort | Multi-field: text plus a .keyword sub-field |
| Numbers, dates, booleans | Their native types (integer, scaled_float, date, boolean) so range filters work |
| Arrays of objects matched per object ("color red AND size M on the same variant") | nested (more expensive), otherwise plain object |
| Stored but never searched | "index": false, or leave it out of the index and load from the database |
| Autocomplete | search_as_you_type, edge n-grams, or a completion suggester |
- You cannot change an existing field's type or analyzer in place. It requires a reindex into a new index.
- Mapping explosion: dynamic fields from user-defined keys (e.g. arbitrary JSON attributes) create thousands of fields and hit the total fields limit. Model them as
nestedkey/value pairs, or use a flattened-object field type (flattenedin Elasticsearch,flat_objectin OpenSearch; check your version's docs) instead. - Use the
_analyzeAPI to see exactly which tokens an analyzer produces before choosing it.
Keeping the index in sync
| Approach | Use when | Risk |
| Outbox table written in the same transaction, consumed by an indexer | Default for application-owned data | Indexer lag; needs monitoring |
| CDC from the database log (e.g. Debezium) to an indexer | Many writers, legacy code you can't change | More infrastructure; schema changes affect the pipeline |
Periodic batch reindex of changed rows (updated_at watermark) | Minutes of staleness is fine | Misses hard deletes unless tracked |
| Dual write from application code (DB then index) | Avoid | Index silently drifts when the second write fails |
Rules:
- Use the database primary key as the document
_id, so re-indexing the same row overwrites instead of duplicating. - Prevent out-of-order overwrites with external versioning (
version+version_type=external, using a row version or update timestamp), so a delayed old event can't replace a newer document. - Handle deletes explicitly (tombstones in the outbox, or soft-delete flags).
- Use the bulk API, start with a few MB per request and tune. A bulk response can be HTTP 200 while individual items failed: check the
errorsflag and each item's status, and retry or dead-letter failed items. - Changes become searchable after a refresh (by default about every second, configurable via
refresh_interval). Don't forcerefresh=trueon every write in production. Userefresh=wait_foronly where a user must see their own write immediately. - Keep a full rebuild job that recreates the index from the database. Run it periodically or compare document counts and checksums to detect drift.
The indexer is a background job: see the background-jobs skill for retries, idempotency and metrics.
Querying
- Put exact constraints in
bool.filter: tenant, permissions, status, price ranges. Filters don't affect scoring and are cacheable. - Put user text in
bool.must/should:multi_matchacross fields with boosts (title^3,description),fuzziness: "AUTO"for typo tolerance, and amatch_phraseinshouldto reward exact phrases. - For user-typed queries, use
match/multi_matchorsimple_query_string. Never pass raw user input toquery_string: it exposes the full query syntax, and malformed input causes errors or expensive queries. - Avoid leading wildcards (
*shoe) and regex queries on large fields. Use n-gram fields if you need infix matching. - Build queries with the client library's builders, never by concatenating strings into JSON.
Pagination
from+sizegets slower with depth and is capped byindex.max_result_window(10,000 by default). Fine for the first pages of results.- For deep pagination or "load more", use
search_afterwith a point in time (PIT) and a sort that ends in a unique tiebreaker field. - For exporting everything, use PIT +
search_after(or scroll on older versions), never deepfrom.
Aggregations and facets
- Aggregate on
keywordand numeric fields, never ontext. termsaggregations return the top N buckets and counts can be approximate across shards. Setsizedeliberately and say "approximately" in the UI if counts must be exact elsewhere.- Facet counts usually apply all filters except the facet's own (post filters or separate aggregations), so users see other options.
Relevance tuning
- Build a judgment list: 50-200 real queries from search logs (frequent, long-tail, zero-result), each with documents graded for relevance (e.g. 0-3), labeled by people who know the domain.
- Pick offline metrics: NDCG@10 for graded relevance, precision@k, MRR for "first good result", and the zero-result rate. Elasticsearch has a ranking evaluation API (
_rank_eval); otherwise script it. - Change one thing at a time: field boosts, analyzers, synonyms, phrase boosts, function score (recency, popularity), and re-measure the whole list. Keep changes that raise the metric without hurting important queries.
- Debug single results with
explain: trueor the_explainAPI to see why a document scored as it did. - Watch online metrics after release: click-through on top results, zero-result rate, query reformulation rate, and conversions where relevant.
Synonyms: start with a small, curated list from search logs. Broad synonym lists often hurt precision.
Security
- Never expose the cluster to the internet or to browsers directly. Clients talk to your API, which builds queries.
- Enable authentication and TLS on the cluster, and give the application a role limited to its indices.
- Enforce tenant and permission filters in every query on the server, or use the engine's document-level security. A missing filter leaks other tenants' data.
- Index only fields the search needs. PII in the index needs the same retention, deletion and access rules as in the database (see the data-privacy-retention skill).
Operations
- Shard sizing: too many small shards waste memory; huge shards slow recovery. Follow the vendor's current sizing guidance and start with few primary shards for small indices.
- Monitor: cluster status (green/yellow/red), search latency p95/p99, indexing rate and bulk failures, rejected requests, JVM heap, disk usage against watermarks (at the flood-stage watermark, indices become read-only for writes), and sync lag (age of the oldest unindexed change).
- Snapshot indices if a rebuild from the database would take too long for your recovery objectives.
- Deleting indices,
_delete_by_query, closing indices and alias swaps in production change what users see or destroy data. Stop and get explicit confirmation from the user first.
Anti-patterns
- Dynamic mapping in production: wrong types (
textwhere you neededkeyword), and mapping explosion. - Index as source of truth: the data can't be rebuilt after a bad mapping or a lost cluster.
- Dual writes from the app: silent drift.
- Ignoring bulk item errors because the HTTP status was 200.
- Querying the concrete index name instead of an alias: every mapping change becomes downtime.
- Tenant filter added in the UI layer only: data leaks through other code paths.
- Tuning by eyeballing three queries: fixes one query, breaks fifty others.
- Deep
frompagination: slow queries and the 10,000-result wall. refresh=trueon every write: indexing throughput collapses.
Done when
- The mapping is explicit, with
dynamicset tostrictorfalse, and each field's type matches how it is queried. - Queries go through an alias; a reindex runbook exists and has been rehearsed.
- The database-to-index pipeline is reliable (outbox or CDC), idempotent, versioned against out-of-order updates, and handles deletes; a full rebuild path exists.
- Every query enforces tenant and permission filters on the server.
- A judgment list and an offline metric exist, and tuning changes are measured against them.
- Latency, bulk errors, sync lag and cluster health are monitored with alerts.
References
- references/mapping-and-reindex.md — read when creating an index, writing queries or deep pagination, or running a zero-downtime reindex.
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.

