Liking cljdoc? Tell your friends :D

search

Full-text search library providing document indexing, full-text search, and an admin UI. Uses PostgreSQL FTS in production with LIKE fallback for H2/SQLite.

Module layout

libs/search/src/wagoe/search/
├── schema.clj         — Malli schemas: SearchDocument, SearchResult, SearchResponse
├── ports.clj          — ISearchStore (persistence), ISearchEngine (orchestration),
│                        ISearchDefinitions
├── core/
│   ├── index.clj      — defsearch, build-document*
│   ├── query.clj      — SQL builders (PostgreSQL FTS + H2/SQLite LIKE fallback)
│   ├── ranking.clj, highlighting.clj
│   └── ui.clj         — Hiccup: search results, admin index page
└── shell/
    ├── persistence.clj    — SearchStore (next.jdbc, HoneySQL ON CONFLICT upsert)
    ├── service.clj        — SearchService (orchestration, pagination, reindex)
    ├── http.clj           — Routes: API + admin web UI
    └── module_wiring.clj  — the :wagoe/search component

Defining a search index

(require '[wagoe.search.core.index :as search])

(search/defsearch product-search
  {:entity-type :product
   :language    :english
   :fields      [{:name :title  :weight :a}
                 {:name :body   :weight :b}
                 {:name :tags   :weight :c}]
   :filters     [:tenant-id :category-id]})  ; optional: filterable dimensions

Weight levels: :a (highest) → :d (lowest).

defsearch only binds the var. Hand definitions, keyed by index id, to :wagoe/search: as your module’s :search part, or as data in config.

(defn ig-config [_settings _ctx]
  {:search {:product-search search/product-search}})
:wagoe/search
{:indexes {:product-search {:entity-type :product
                            :fields      [{:name :title :weight :a}]}}}

Each system holds its own definitions. The older wagoe.search.shell.registry/defsearch still works, with a deprecation warning.

The search API

POST /api/v1/search/:index-id and /suggest, and the admin search pages, serve an index only when its definition opts in:

  • :api? true serves it to any signed-in user.

  • :api {:authorize (fn [request definition] …​)} serves it when the hook returns truthy.

Any other index answers 404, the same as an unknown one. An index that backs your own page, with its own visibility rules, should not opt in. The framework ships no indexes.

Indexing documents

(require '[wagoe.search.ports :as ports])

(ports/index-document! search-engine :product-search product-id
  {:title "Wagoe Framework"
   :body  "A Clojure web framework..."
   :tags  "clojure framework"}
  {:filter-values {:tenant-id tenant-id}})

;; Bulk reindex: a seq of [entity-id field-values] pairs
(ports/reindex! search-engine :product-search documents)
;=> {:indexed 120}

Searching

(ports/search search-engine :product-search "clojure framework"
  {:filters {:tenant-id tenant-id}
   :limit   25
   :offset  0})
;=> {:results [{:entity-type :product :entity-id #uuid "..." :rank 0.9 :snippet "..."}]
;    :total 42
;    :query "clojure framework"
;    :took-ms 3}

Testing

clojure -M:test :search

Can you improve this documentation? These fine people already did:
Thijs Creemers & thijscreemers
Edit on GitHub

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
←Move to previous article
→Move to next article
Ctrl+/Jump to the search field
× close