NOT the API namespace for the search module!! See [[metabase.search]] instead.

(ns metabase.search.core
  (:require
   [metabase.analytics.core :as analytics]
   [metabase.search.appdb.core :as search.engines.appdb]
   [metabase.search.config :as search.config]
   [metabase.search.engine :as search.engine]
   [metabase.search.impl :as search.impl]
   [metabase.search.in-place.legacy :as search.legacy]
   [metabase.search.ingestion :as search.ingestion]
   [metabase.search.spec :as search.spec]
   [metabase.search.util :as search.util]
   [metabase.util.log :as log]
   [potemkin :as p]))
(comment
  ;; Make sure to import all the engine implementations. In future this can happen automatically, as per drivers.
  ;;
  ;; TODO -- maybe engine loading should be moved to [[metabase.search.init]] instead
  search.engine/keep-me
  search.engines.appdb/keep-me
  search.legacy/keep-me

  search.config/keep-me
  search.impl/keep-me)
(p/import-vars
 [search.config
  SearchableModel]

 [search.engine
  model-set]

 [search.impl
  search
  ;; We could avoid exposing this by wrapping `query-model-set` and `search` with it.
  search-context]

 [search.ingestion
  bulk-ingest!
  get-next-batch!]

 [search.spec
  define-spec])
(defmethod analytics/known-labels :metabase-search/index
  [_]
  (for [model (keys (search.spec/specifications))]
    {:model model}))
(defmethod analytics/known-labels :metabase-search/engine-default
  [_]
  (analytics/known-labels :metabase-search/engine-active))
(defmethod analytics/known-labels :metabase-search/engine-active
  [_]
  (for [e (search.engine/known-engines)]
    {:engine (name e)}))
(defmethod analytics/initial-value :metabase-search/engine-default
  [_ {:keys [engine]}]
  (if (= engine (name (search.impl/default-engine))) 1 0))
(defmethod analytics/initial-value :metabase-search/engine-active
  [_ {:keys [engine]}]
  (if (search.engine/supported-engine? (keyword "search.engine" engine)) 1 0))

Does this instance support a search index, of any sort?

(defn supports-index?
  []
  (seq (search.engine/active-engines)))

Ensure there is an index ready to be populated.

(defn init-index!
  [& {:as opts}]
  ;; If there are multiple indexes, return the peak inserted for each type. In practice, they should all be the same.
  (reduce (partial merge-with max)
          nil
          (for [e (search.engine/active-engines)]
            (search.engine/init! e opts))))

Populate a new index, and make it active. Simultaneously updates the current index.

(defn reindex!
  [& {:as opts}]
  ;; If there are multiple indexes, return the peak inserted for each type. In practice, they should all be the same.
  (try
    (reduce (partial merge-with max)
            nil
            (for [e (search.engine/active-engines)]
              (search.engine/reindex! e opts)))
    (catch Throwable e
      (log/fatal e "Error reindexing search indexes."))))

Stop tracking the current indexes. Used when resetting the appdb.

(defn reset-tracking!
  []
  (doseq [e (search.engine/active-engines)]
    (search.engine/reset-tracking! e)))

Given a new or updated instance, put all the corresponding search entries if needed in the queue.

(defn update!
  [instance & [always?]]
  (when (supports-index?)
    (when-let [updates (->> (search.spec/search-models-to-update instance always?)
                            (remove (comp search.util/impossible-condition? second))
                            seq)]
      ;; We need to delay execution to handle deletes, which alert us *before* updating the database.
      (search.ingestion/ingest-maybe-async! updates))))

Given a model and a list of model's ids, remove corresponding search entries.

(defn delete!
  [model ids]
  (doseq [e            (search.engine/active-engines)
          search-model (->> (vals (search.spec/specifications))
                            (filter (comp #{model} :model))
                            (map :name))]
    (search.engine/delete! e search-model ids)))