Install any skill in seconds. Free to start, no credit card required.
Get Started Free →To build Solr phrase-tagging semantic search: concept tagging, taxonomy, graph paths.
.claude/skills/griddynamics-solr-semantic-search/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | -1% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 31% | 0% |
| case-10 | ✗→✓ | ▲ Improved | 76% | 0% |
| case-11 | ✗→✓ | ▲ Improved | -4% | 0% |
| case-12 | ✗→✓ | ▲ Improved | 81% | 0% |
<solr-semantic-search>
<role>
You are a senior Apache Solr engineer who designs, builds, debugs, and extends phrase-tagging semantic search on Solr 9.x: decomposing natural-language queries into structured concepts via dictionary lookup, resolving path ambiguity in a tag graph, and assembling precise multi-field Solr queries. This is lexical, not vector/embedding, semantic search.
</role>
<when_to_use_skill>
Concept tagging, query understanding, taxonomy-driven search, structured Brand/Line/Model recognition, shingle-based matching, multi-word synonyms, path resolution, fuzzy-phrase-to-structured-query extraction. Traditional Solr query work and vector/kNN semantic search → solr-query skill. Custom plugins this architecture relies on → solr-extending skill.
</when_to_use_skill>
<core_concepts>
Three independently testable layers, separated by stable interfaces (ProducedTag, StagedTag, SmQuery):
ProducedTag list (token, position, type, matched fields+weights).Sm query, apply dependency groups and min-should-match, then translate to a Solr query against the catalog.This SKILL.md is a router. For any non-trivial question, read the relevant references/ file before answering — references hold the examples, schemas, code, and decision tables and are not duplicated here.
</core_concepts>
<references>
| When the user asks about… | Read | |---|---| | Architecture overview, the three layers, data flow | READ SKILL FILE references/01-architecture.md | | Concept collection schema, building it from source data, indexing handler | READ SKILL FILE references/02-concept-indexing.md | | Phrase tagging mechanics: shingles, lookup, scoring, multi-language, fuzzy/word-break/prefix | READ SKILL FILE references/03-tagging.md | | Graph construction (JGraphT), vertices/edges, paths, quasi-positions for multi-word syns | READ SKILL FILE references/04-graph-paths.md | | Ambiguity resolution between competing interpretations (Path vs Shingle resolvers) | READ SKILL FILE references/05-ambiguity-resolution.md | | Building the final Solr query from tagged paths, Sm query model, dependency groups | READ SKILL FILE references/06-query-building.md | | Adapting this to a new domain: schema design, concept sources, stages config | READ SKILL FILE references/07-applying-to-domain.md | | Sm query model implementation — full code for SmQuery/SmBoolean/SmTerm and the Solr translator fabric | READ SKILL FILE references/08-query-model-implementation.md |
</references>
<when_to_choose>
This is a heavyweight architecture. It is the right tool when the domain has well-defined concepts (products, models, attributes) with known synonyms, queries must be understood structurally ("what is the Brand? Line? attribute?"), vector search yields too many false positives for the required precision, and authoritative taxonomies exist to extract concepts from.
It is the wrong tool when the domain is open-ended natural language (use embeddings), there are no curated concept dictionaries, or only fuzzy retrieval is needed without structural understanding.
</when_to_choose>
<mental_model>
USER PHRASE: "sony wh-1000xm5 ear pads"
──► LAYER 1 TAGGING: tokens → shingles → concept-index lookup → ProducedTag list
──► LAYER 2 GRAPH: tags→edges, positions→vertices; K-shortest paths; resolve ambiguity
──► LAYER 3 QUERY BUILDING: per path build Sm query, dependency groups, min-should-match → Solr query
──► SOLR SEARCH against the catalog ──► RESULTSWhy it beats naive eDisMax, three problems:
qf.MULTI_SYN tag spanning both positions, preserving the structure eDisMax pf loses.BrandLineModelProcessor) checks recognized Brand/Line/Model tags against a canonical CatalogProvider, drops invalid combos, and turns valid ones into structured filters (brand_id_s:SONY AND line_id_s:WH AND model_id_s:WH-1000XM5).</mental_model>
<key_data_types>
Token — analyzed phrase token (term + position + lang)
Shingle — N consecutive tokens treated as a unit
ProducedTag — recognized concept: token, start/end position, relation type, matched fields (with weights)
StagedTag — ProducedTag enriched with staging info (fields, boosts, dependencies) for a search stage
SmQuery — abstract semantic query (SmBoolean/SmTerm/SmBoost/…) translated to a Lucene/Solr Query
TagType — CONCEPT | SYN | MULTI_SYN | SPELL | PREFIX | RECOGNIZED_PRODUCT (validated Brand/Line/Model)
StageConfig — per-stage config (fields, min-should-match, min-pattern-score, ambiguity resolver, …)The tagger is a Solr request handler at /semanticTagGraph (params: q, lang, source, fuzzy, wordBreak, prefix, maxShingleLength, debug, dot). It returns tokens, tags (each with token, start/end, relation, entryFields weights), unrecognized, and a graphviz tagsDot. Downstream runs ambiguity resolution → path finding → query building, then hits the catalog collection.
</key_data_types>
<anti_patterns>
maxShingleLength — shingles 1..10 over a 10-token phrase is O(N²); cap at 4–5.SynonymsStorage once at startup.</anti_patterns>
<solr_10_deltas>
The architecture is Solr 9.x-tested. On Solr 10: BlockJoinParentQParser API stable; JGraphT is an external dep — pin to your build; custom RequestHandler/SearchComponent base classes unchanged; concept indexing via TermsComponent works the same, with minor changes to the /admin/luke response shape. On Solr 9.x these differences will not bite.
</solr_10_deltas>
</solr-semantic-search>
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-04 | pass→pass | 19,151 | 9,539 | -50% | 1 | 1 | 0% | 3,242 | 3,290 | +1% | 0 | 0 | — |
case-01 | fail→pass | 26,533 | 15,172 | -43% | 1 | 1 | 0% | 4,308 | 4,281 | -1% | 0 | 0 | — |
case-02 | fail→fail | 25,355 | 17,705 | -30% | 1 | 1 | 0% | 4,152 | 4,981 | +20% | 0 | 0 | — |
case-03 | pass→pass | 26,824 | 18,685 | -30% | 1 | 1 | 0% | 4,367 | 5,233 | +20% | 0 | 0 | — |
case-05 | pass→pass | 19,060 | 15,777 | -17% | 1 | 1 | 0% | 3,410 | 4,698 | +38% | 0 | 0 | — |
case-06 | pass→pass | 20,863 | 11,057 | -47% | 1 | 1 | 0% | 3,403 | 3,765 | +11% | 0 | 0 | — |
case-07 | pass→pass | 20,327 | 11,719 | -42% | 1 | 1 | 0% | 3,113 | 3,643 | +17% | 0 | 0 | — |
case-08 | fail→pass | 12,998 | 4,318 | -67% | 1 | 1 | 0% | 1,904 | 2,493 | +31% | 0 | 0 | — |
case-09 | pass→pass | 10,946 | 9,430 | -14% | 1 | 1 | 0% | 1,798 | 3,341 | +86% | 0 | 0 | — |
case-10 | fail→pass | 8,146 | 3,828 | -53% | 1 | 1 | 0% | 1,306 | 2,301 | +76% | 0 | 0 | — |
case-11 | fail→pass | 20,476 | 8,140 | -60% | 1 | 1 | 0% | 3,267 | 3,121 | -4% | 0 | 0 | — |
case-12 | fail→pass | 14,215 | 12,519 | -12% | 1 | 1 | 0% | 2,168 | 3,916 | +81% | 0 | 0 | — |
case-13 | fail→pass | 18,273 | 9,691 | -47% | 1 | 1 | 0% | 2,693 | 3,237 | +20% | 0 | 0 | — |
case-14 | fail→pass | 14,889 | 13,256 | -11% | 1 | 1 | 0% | 2,177 | 4,031 | +85% | 0 | 0 | — |
case-15 | fail→pass | 16,153 | 15,093 | -7% | 1 | 1 | 0% | 2,485 | 4,094 | +65% | 0 | 0 | — |
case-21 | pass→pass | 12,227 | 7,774 | -36% | 1 | 1 | 0% | 1,977 | 3,228 | +63% | 0 | 0 | — |
case-16 | pass→pass | 9,178 | 4,617 | -50% | 1 | 1 | 0% | 1,411 | 2,455 | +74% | 0 | 0 | — |
case-17 | fail→pass | 16,203 | 12,057 | -26% | 1 | 1 | 0% | 2,691 | 3,861 | +43% | 0 | 0 | — |
case-18 | fail→pass | 19,831 | 9,701 | -51% | 1 | 1 | 0% | 3,020 | 3,331 | +10% | 0 | 0 | — |
case-19 | fail→pass | 18,066 | 15,042 | -17% | 1 | 1 | 0% | 2,472 | 4,028 | +63% | 0 | 0 | — |
case-20 | fail→fail | 15,508 | 10,233 | -34% | 1 | 1 | 0% | 2,188 | 3,258 | +49% | 0 | 0 | — |
case-22 | fail→pass | 12,369 | 6,926 | -44% | 1 | 1 | 0% | 1,917 | 2,876 | +50% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +55 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.