Who this is for. The developer asked to make search results "better", and the client who has to agree what better means. Afterwards you can say which levers exist, which do not, and tune against a fixed set of real queries rather than the one in your head.

The levers you have

Relevance is the order results come back in, and it is tuned, not fixed. Every lever below is a view setting (Search views) except the last, which is the mapping; this is what each is for, in the order to reach for them:

LeverWhat it changesReach for it when
Field weights, such as name^3,fields.summary^2,fields.contentHow much a match in each field countsA title hit should beat a passing mention. The first thing to set, and often enough
Minimum should matchHow many of the query's words a result must containMulti-word queries return too much: "red wool jumper" returns everything red. Set 2 or 75%
Fuzzy searchWhether near-misses matchOn by default; switch it off only for codes and part numbers, where a near match is a wrong match
Tie breakerHow much matches in other fields add to the best field's scoreA record that matches in title and body should edge out one that matches in the title alone; a value such as 0.3
What you mapWhich words are in which fieldA term that is never found is usually an unmapped area, not a ranking problem

Change one thing at a time and run the same set of real queries after each change. Relevance tuning is a search for the setting that helps the most queries without hurting others, and "without hurting others" cannot be seen one query at a time.

Boosting is field weights, and nothing else

The score is Elasticsearch's own, multiplied by your field weights. There is no per-record boost, no way to pin a result to the top, no custom scoring function and no decay by date. Two things follow:

  • To promote a kind of record, give more weight to a field that kind fills, or map a marker into a searched field. You cannot add a fixed bonus to one record.
  • To favour recent content, offer a sort. orderBy=published:desc replaces relevance, so offer it as a choice, not as the default; orderBy=_score,published:desc keeps relevance and puts the newer of two equal matches first. For most content search, an older record that matches better outranking a newer one is correct.

Synonyms, stop words and language: the limits

This is where the built-in index stops, and it is better to know it before a deadline than during one. The way text is broken into words is fixed for every site (How text is broken into words):

  • There is no synonym list. A search for "sofa" does not find a product called "couch", and "trousers" does not find "pants". Stems are matched, so "running" finds "run", but meanings are not.
  • Stop words are a fixed English list that cannot be edited: the, and, of, to, it and the rest are dropped from content and query alike. A query made only of them finds nothing, and a page titled "IT" is hard to find by its name.
  • The analysis is English for every site. Stems and stop words are English whatever the site's language, so a French or German site gets little from either, and no other language's analysis can be chosen.

Add synonym coverage by putting the other words into the content. Give the layouts a plain-text area such as searchKeywords, map it to a Text search field named keywords, add that field to the view's full text search fields with a modest weight, and let authors type the alternatives a visitor might use: "couch, settee" on the sofa. The word the visitor types is then in the document. It is a list maintained by hand, record by record; when synonyms are central to the experience, that is a reason to look at an external engine (When to use an external search engine).

Tuning against real queries

The queries you imagine are not the queries people type, so collect the real ones first. The platform has no search analytics of its own, but every search is a page view of /search/<slug>?q=…, so the query is in the URL, and the site-search report of whatever analytics the site runs lists the most common queries without any code. Send the result count to analytics as well, from the page, so that the queries returning nothing can be listed: they are the most valuable, because each is a synonym you need, content you are missing, or a word your mapping does not cover.

Then the loop: take the twenty most common queries and every zero-result query, run them, change one weight or one mapping, and run them again. Write down the set and what each returned, so that a later change can be checked against the same baseline. Stop when the common queries are good; chasing the long tail beyond that costs more than it returns. A query that still finds nothing after tuning needs content or a synonym added, not a ranking change: add it to the content and the mapping, not the weights.