An example is helpful to illustrate what you can do with Querqy.
These are the three most common name spaces you will need when creating your rewriter.
;; The core querqy namespace
(require '[com.nytimes.querqy :as querqy])
;; The replace rewriter
(require '[com.nytimes.querqy.replace :as r])
;; The common rules rewriter
(require '[com.nytimes.querqy.commonrules :as c])
A common use case for the replace rewriter is to fix typos, consolidate input into a common form, and delete unhelpful query terms. In the below example we do all three:
- We fix the typo
ihpone
and replace it withiphone
. - We fix
ombiles
and replace it withmobile
. We also replace the pluralmobiles
with the singularmobile
This would also be a good place to capturecell phone
,cell phones
, etc. - We delete
cheap
from the input because, in this hypothetical, it hurts search relevance.
(def typos-rewriter
(r/replace-rewriter
(r/replace "ihpone" (r/with "iphone"))
(r/replace (or "mobiles" "ombiles")
(r/with "mobile"))
(r/delete "cheap")))
The common rules rewriter is where we will write most of the domain-specific query rewriting we want to do. In our example, we have three rules:
- When we see a query with
apple phone
, we inject a synonymiphone
into the query. - When a query contains
iphone
but notcase
, we boost all matching documents tagged with theiphone
category. - When a query contains
iphone
andcase
, we boost all matching documents tagged with theiphoe-accessories
category.
(def rules-rewriter
(c/rules-rewriter
(c/match "apple phone"
(c/synonym "iphone"))
(c/match (and "iphone" (not "case"))
(c/boost 100 {:term {:category "iphone"}}))
(c/match (and "iphone" "case")
(c/boost 100 {:term {:category "iphone-accessories"}}))))
We can compose multiple rewriters into a single rewriter chain. The rewriters run in order, so this chain will cause our typo rewriter to run before our rules rewriter.
(def chain (querqy/chain-of [typos-rewriter rules-rewriter]))
Below we use our rewriter chain to rewrite the query cheap ihpone
. We emit an
Elasticsearch query from this which we can issue against our search cluster.
(def emit-opts
{:match/fields ["title", "body"]
:dis_max/tie_breaker 0.5})
(def query "cheap ihpone")
(def query' (querqy/rewrite chain query))
(querqy/emit query' emit-opts)
;; =>
{:function_score
{:query
{:bool
{:must [],
:should [{:dis_max
{:queries [{:match {"title" {:query "iphone"}}}
{:match {"body" {:query "iphone"}}}],
:tie_breaker 0.5}}],
:must_not [],
:filter []}},
:functions [{:filter {:term {:category "mobiles"}},
:weight 100.0}]}}