honeysql

namenu 2023-11-21T15:45:10.479569Z

I have this kind of where clauses.

(h/where 
  (when (:id filter-options)
    [:= :id (:id filter-options)])
  (when (seq (:ids filter-options))
    [:in :id (:ids filter-options)])
  (when (:user-id filter-options)
    [:= :user-id (:user-id filter-options)])
  (when (seq (:user-ids filter-options))
    [:in :user-id (:user-ids filter-options)])
  (when (:crop-id filter-options)
    [:= :crop-id (:crop-id filter-options)])
  (when (seq (:crop-ids filter-options))
    [:in :crop-id (:crop-ids filter-options)])
  ; ... and many more filters ...
  )
I felt like "https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators#null-conditional-operators--and-" was what I needed. The attempt I made was to (register-fn! :?= ...) but it seems that I cannot return nil from formatter.

namenu 2023-11-21T15:46:57.293949Z

(sql/register-fn! :?=
                  (fn [_op [l r]]
                    (when (some? l)
                      [:= l r])))
                    
(sql/format [:and [:?= nil :a]])
; => [""]
is it possible to make it ["TRUE"] or so?

p-himik 2023-11-21T15:48:37.974549Z

I think you can replace (when (some? r) ...) with (if (nil? r) true ...).

👍 1
namenu 2023-11-21T15:49:43.027769Z

oh my

namenu 2023-11-21T15:53:30.896589Z

returning [true] works, but ["WHERE true AND true AND true"] 😅

p-himik 2023-11-21T15:57:28.826319Z

Yeah, no way around that without either overriding handling of the :where clause or filtering the conditions themselves. This is how I'd write something like that (I don't use helper functions generally):

(let [conds (filter peek [[:= :id (:id filter-options) ...])
      sqlmap {:select :id :from :t}]
  (cond-> sqlmap
    (seq conds)
    (assoc :where (into [:and] conds))))

namenu 2023-11-21T16:12:11.459229Z

yes indeed, yet it is prevailing pattern for our code base. so I was looking for syntax extension.

seancorfield 2023-11-21T17:47:40.125589Z

I would use cond-> for this and just thread through multiple h/where calls:

(cond-> sql
    (:id filter-options)
    (h/where [:= :id (:id filter-options)])
    ..)
(that should do some simplification of the DSL to produce less redundant SQL)