diff --git a/deps.edn b/deps.edn index 0f438c2..414bfb7 100644 --- a/deps.edn +++ b/deps.edn @@ -13,9 +13,9 @@ hato/hato {:mvn/version "1.0.0"} instaparse/instaparse {:mvn/version "1.5.0"} io.netty/netty-buffer {:mvn/version "4.1.114.Final"} - org.apache.lucene/lucene-core {:mvn/version "9.12.0"} - org.apache.lucene/lucene-queries {:mvn/version "9.12.0"} - org.apache.lucene/lucene-backward-codecs {:mvn/version "9.12.0"} + org.apache.lucene/lucene-core ^:antq/exclude {:mvn/version "9.12.0"} + org.apache.lucene/lucene-queries ^:antq/exclude {:mvn/version "9.12.0"} + org.apache.lucene/lucene-backward-codecs ^:antq/exclude {:mvn/version "9.12.0"} org.lmdbjava/lmdbjava {:mvn/version "0.9.0"}} :aliases @@ -48,6 +48,11 @@ com.wsscode/pathom-viz-connector {:mvn/version "2022.02.14"} io.github.nubank/morse {:git/tag "v2023.10.06.02" :git/sha "88b5ff7"}}} + :lucene10 ;; can be used when on Java 21 and above + {:override-deps {org.apache.lucene/lucene-core {:mvn/version "10.0.0"} + org.apache.lucene/lucene-queries {:mvn/version "10.0.0"} + org.apache.lucene/lucene-backward-codecs {:mvn/version "10.0.0"}}} + :test {:extra-paths ["cmd" "test" "test/resources"] :extra-deps {org.clojure/test.check {:mvn/version "1.1.1"} @@ -127,3 +132,5 @@ :exec-fn codox.main/generate-docs :exec-args {:source-paths ["src"] :metadata {:doc/format :markdown}}}}} + + diff --git a/src/com/eldrix/hermes/impl/lucene.clj b/src/com/eldrix/hermes/impl/lucene.clj index 9b03cb2..c6e65e5 100644 --- a/src/com/eldrix/hermes/impl/lucene.clj +++ b/src/com/eldrix/hermes/impl/lucene.clj @@ -9,9 +9,66 @@ (ns ^:no-doc com.eldrix.hermes.impl.lucene (:require [clojure.core.async :as a]) (:import (java.util List ArrayList) + (org.apache.lucene.util Version) (org.apache.lucene.search CollectionTerminatedException CollectorManager IndexSearcher BooleanClause$Occur BooleanQuery$Builder Query MatchAllDocsQuery BooleanQuery BooleanClause Collector LeafCollector Scorable ScoreMode))) +(defmacro when-v + "Evaluate body depending on Lucene major version. + Supported operators: = <= < > >= + For example + ``` + (lucene/when-v > 8 ...) + ```" + [op version & body] + (let [latest (.major Version/LATEST)] + (list 'when (list 'cond + (list = '= op) `(= ~version ~latest) + (list = '>= op) `(>= ~latest ~version) + (list = '> op) `(> ~latest ~version) + (list = '< op) `(< ~latest ~version) + (list = '<= op) `(<= ~latest ~version) + :else `(throw (ex-info "Invalid operand" {:op ~op}))) + (cons 'do body)))) + +;; +;; Lucene 10 breaks backwards compatibility in a trivial rename of an accessor +;; which, frankly, is pretty indefensible. Here we manage the incompatibility +;; dynamically by looking at the Lucene version at compile time and choosing +;; the right accessor based on version. This means we can support Lucene 10 +;; when running on Java 21 and above, and yet still run on Java 11 and above +;; using Lucene 9. +;; + +(when-v >= 10 + + (defn query-for-boolean-clause + [^BooleanClause clause] + (.query clause)) + (defn occur-for-boolean-clause + [^BooleanClause clause] + (.occur clause))) + +(when-v < 10 + + (defn query-for-boolean-clause + [^BooleanClause clause] + (.getQuery clause)) + (defn occur-for-boolean-clause + [^BooleanClause clause] + (.getOccur clause))) + +(comment + Version/LUCENE_CURRENT + (println Version/LUCENE_CURRENT) + (println Version/MIN_SUPPORTED_MAJOR) + (.MIN_SUPPORTED_MAJOR Version/LATEST)) + +;; +;; +;; +;; + (set! *warn-on-reflection* true) ;; A Lucene results collector that collects *all* results into the mutable @@ -93,14 +150,14 @@ [^Query q] (and (instance? BooleanQuery q) (= (count (.clauses ^BooleanQuery q)) 1) - (= BooleanClause$Occur/MUST_NOT (.getOccur ^BooleanClause (first (.clauses ^BooleanQuery q)))))) + (= BooleanClause$Occur/MUST_NOT (occur-for-boolean-clause (first (.clauses ^BooleanQuery q)))))) (defn- rewrite-single-must-not "Rewrite a single 'must-not' query." [^BooleanQuery q] (-> (BooleanQuery$Builder.) (.add (MatchAllDocsQuery.) BooleanClause$Occur/SHOULD) - (.add (.getQuery ^BooleanClause (first (.clauses q))) BooleanClause$Occur/MUST_NOT) + (.add (query-for-boolean-clause (first (.clauses q))) BooleanClause$Occur/MUST_NOT) (.build))) (defn q-or @@ -131,7 +188,7 @@ (let [builder (BooleanQuery$Builder.)] (doseq [query queries] (if (single-must-not-clause? query) - (.add builder ^Query (.getQuery ^BooleanClause (first (.clauses ^BooleanQuery query))) BooleanClause$Occur/MUST_NOT) + (.add builder ^Query (query-for-boolean-clause (first (.clauses ^BooleanQuery query))) BooleanClause$Occur/MUST_NOT) (.add builder ^Query query BooleanClause$Occur/MUST))) (.build builder)))) diff --git a/src/com/eldrix/hermes/impl/search.clj b/src/com/eldrix/hermes/impl/search.clj index 9217e74..f024260 100644 --- a/src/com/eldrix/hermes/impl/search.clj +++ b/src/com/eldrix/hermes/impl/search.clj @@ -733,8 +733,8 @@ items." (if-not (instance? BooleanQuery query) (vector query nil) (let [clauses (.clauses ^BooleanQuery query) - incl (seq (filter #(not= (.getOccur ^BooleanClause %) BooleanClause$Occur/MUST_NOT) clauses)) - excl (seq (filter #(= (.getOccur ^BooleanClause %) BooleanClause$Occur/MUST_NOT) clauses))] + incl (seq (filter #(not= (lucene/occur-for-boolean-clause %) BooleanClause$Occur/MUST_NOT) clauses)) + excl (seq (filter #(= (lucene/occur-for-boolean-clause %) BooleanClause$Occur/MUST_NOT) clauses))] (vector ;; build the inclusive clauses directly into a new query (when incl @@ -746,7 +746,7 @@ items." (when excl (let [builder (BooleanQuery$Builder.)] (doseq [^BooleanClause clause excl] - (.add builder (.getQuery clause) BooleanClause$Occur/MUST)) + (.add builder (lucene/query-for-boolean-clause clause) BooleanClause$Occur/MUST)) (.build builder))))))) (defn test-query [store ^IndexSearcher searcher ^Query q ^long max-hits]