根据键类型解析地图和过滤的向量

发布于 2025-02-12 11:59:05 字数 1523 浏览 3 评论 0原文

我有一个地图向量,我正在尝试将这些地图解析为一个将在RAW SQL查询中使用的字符串。

(def params [{:fieldName "salary" :comparator "greater" :inputValue "10000" :inputType "number"}
             {:fieldName "name" :comparator "include" :inputValue "Ros" :inputType "text"}
             {:fieldName "start_date" :comparator "between" :inputValue "2022-01-01" :maxInputValue "2022-06-01" :inputType "date"}])

我需要从每张映射中提取的内容是基于的类型:比较器 and :fieldName我创建一个strinng,一旦我对其进行解析,请放置所有字符串在一个字符串中。每个fieldName将在向量中仅重复一次。我在下面写了一些东西,但我试图做得更好,而且它仅适用于一种类型,这将使​​所有比较器 and field> fieldname

(defn convert
  [params]
  (cond-> nil
    (= "equal" (->> params
                    (filter #(= (:fieldName %) "salary"))
                    (first)
                    (:comparator))) (conj " AND salary= ?")
    (= "not-equal" (->> params
                      (filter #(= (:fieldName %) "salary"))
                      (first)
                      (:comparator))) (conj " AND salary!= ?")
    (= "greater" (->> params
                      (filter #(= (:fieldName %) "salary"))
                      (first)
                      (:comparator))) (conj " AND salary > ?")))

我想这样做

"AND salary > ? 
 AND name LIKE 'Ros%'
 AND start BETWEEN between '2022-01-01' AND '2022-06-01' "

I have a vector of maps, and I am trying to parse this maps into a string which will be used in a raw SQL query.

(def params [{:fieldName "salary" :comparator "greater" :inputValue "10000" :inputType "number"}
             {:fieldName "name" :comparator "include" :inputValue "Ros" :inputType "text"}
             {:fieldName "start_date" :comparator "between" :inputValue "2022-01-01" :maxInputValue "2022-06-01" :inputType "date"}])

What I need to extract from each map is that based on the type of :comparator and :fieldName I create a strinng and once I have parsed them all, put all the strings in one string. Each fieldName will be repeated only once in the vector. I have written something below but I am trying to do it better, plus it is only for one type which will make it longer and more difficult to read for all the comparator and fieldName

(defn convert
  [params]
  (cond-> nil
    (= "equal" (->> params
                    (filter #(= (:fieldName %) "salary"))
                    (first)
                    (:comparator))) (conj " AND salary= ?")
    (= "not-equal" (->> params
                      (filter #(= (:fieldName %) "salary"))
                      (first)
                      (:comparator))) (conj " AND salary!= ?")
    (= "greater" (->> params
                      (filter #(= (:fieldName %) "salary"))
                      (first)
                      (:comparator))) (conj " AND salary > ?")))

I want to have somthing like this

"AND salary > ? 
 AND name LIKE 'Ros%'
 AND start BETWEEN between '2022-01-01' AND '2022-06-01' "

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

情未る 2025-02-19 11:59:05

我不确定您想在这里拥有的所有选项,所以这是我的第一次尝试(请注意,我将名称更改为kebab-case):

(def params [{:field-name "salary" :comparator "greater" :input-value "10000" :input-type "number"}
             {:field-name "name" :comparator "include" :input-value "Ros" :input-type "text"}
             {:field-name "start-date" :comparator "between" :input-value "2022-01-01" :max-input-value "2022-06-01" :input-type "date"}])

(defn convert [params]
  (->> (for [{:keys [field-name comparator input-value max-input-value input-type]} params]
         (->> (condp = input-type
                "number" [field-name ({"greater" ">" "less" "<" "equal" "=" "not-equal" "!="} comparator) input-value]
                "text" [field-name ({"include" "LIKE"} comparator) (str "'%" input-value "%'")]
                "date" [field-name ({"between" "BETWEEN"} comparator) (str "'" input-value "'") "AND" (str "'" max-input-value "'")])
              (str/join " ")))
       (str/join " AND ")))

(convert params)
=> "salary > 10000 AND name LIKE '%Ros%' AND start-date BETWEEN '2022-01-01' AND '2022-06-01'"

I'm not sure about all options you want to have here, so this is my first try (note that I changed names to kebab-case):

(def params [{:field-name "salary" :comparator "greater" :input-value "10000" :input-type "number"}
             {:field-name "name" :comparator "include" :input-value "Ros" :input-type "text"}
             {:field-name "start-date" :comparator "between" :input-value "2022-01-01" :max-input-value "2022-06-01" :input-type "date"}])

(defn convert [params]
  (->> (for [{:keys [field-name comparator input-value max-input-value input-type]} params]
         (->> (condp = input-type
                "number" [field-name ({"greater" ">" "less" "<" "equal" "=" "not-equal" "!="} comparator) input-value]
                "text" [field-name ({"include" "LIKE"} comparator) (str "'%" input-value "%'")]
                "date" [field-name ({"between" "BETWEEN"} comparator) (str "'" input-value "'") "AND" (str "'" max-input-value "'")])
              (str/join " ")))
       (str/join " AND ")))

(convert params)
=> "salary > 10000 AND name LIKE '%Ros%' AND start-date BETWEEN '2022-01-01' AND '2022-06-01'"
℉絮湮 2025-02-19 11:59:05

这当然是一个不错的用例

对于多途径可能是这样实现的,

(def sql-hierarchy
  (-> (make-hierarchy)
      (derive :bounded :any)
      (derive :text :any)
      (derive :number :bounded)
      (derive :date :bounded)))

(defmulti to-sql
  (fn [{:keys [input-type comparator]}]
    (mapv keyword [input-type comparator]))
  :hierarchy #'sql-hierarchy)

(defmethod to-sql [:text :include]
  [{:keys [field-name input-value]}]
  {:q (format "%s like '%%%s%%'" field-name input-value)  :p []})

(defmethod to-sql [:bounded :less]
  [{:keys [field-name input-value]}]
  {:q (format "%s < ?" field-name) :p [input-value]})

(defmethod to-sql [:bounded :greater]
  [{:keys [field-name input-value]}]
  {:q (format "%s > ?" field-name) :p [input-value]})

(defmethod to-sql [:bounded :between]
  [{:keys [field-name input-value max-input-value]}]
   {:q (format "%s between ? and ?" field-name) :p [input-value max-input-value]})

。像这样:

(def params [{:field-name "salary" :comparator "greater" :input-value "10000" :input-type "number"}
             {:field-name "name" :comparator "include" :input-value "Ros" :input-type "text"}
             {:field-name "start_date" :comparator "between" :input-value "2022-01-01" :max-input-value "2022-06-01" :input-type "date"}])

(let [data (map to-sql params)]
  {:query (clojure.string/join " and " (map :q data))
   :params (into [] (mapcat :p) data)})

;;=>{:query "salary > ? and name like '%Ros%' and start_date between ? and ?",
;;   :params ["10000" "2022-01-01" "2022-06-01"]}

其中最好的部分是,您始终可以添加新的类型 /选择器,而无需触摸现有的(大概是经过良好测试)代码。例如,您想添加in-list操作员:

(defmethod to-sql [:any :in-list]
  [{:keys [field-name input-value]}]
   {:q (format "%s in (%s)"
               field-name
               (clojure.string/join ", " (repeat (count input-value) "?")))
    :p input-value})

(def params [{:field-name "salary" :comparator "greater" :input-value "10000" :input-type "number"}
             {:field-name "name" :comparator "include" :input-value "Ros" :input-type "text"}
             {:field-name "rank" :comparator "in-list" :input-value ["middle" "junor"] :input-type "text"}
             {:field-name "start_date" :comparator "between" :input-value "2022-01-01" :max-input-value "2022-06-01" :input-type "date"}])

(let [data (map to-sql params)]
  {:query (clojure.string/join " and " (map :q data))
   :params (into [] (mapcat :p) data)})

;;=> {:query "salary > ? and name like '%Ros%' and rank in (?, ?) and start_date between ? and ?",
;;    :params ["10000" "middle" "junor" "2022-01-01" "2022-06-01"]}

This one certainly looks like a nice use case for the multimethods

Could be possibly implemented like this:

(def sql-hierarchy
  (-> (make-hierarchy)
      (derive :bounded :any)
      (derive :text :any)
      (derive :number :bounded)
      (derive :date :bounded)))

(defmulti to-sql
  (fn [{:keys [input-type comparator]}]
    (mapv keyword [input-type comparator]))
  :hierarchy #'sql-hierarchy)

(defmethod to-sql [:text :include]
  [{:keys [field-name input-value]}]
  {:q (format "%s like '%%%s%%'" field-name input-value)  :p []})

(defmethod to-sql [:bounded :less]
  [{:keys [field-name input-value]}]
  {:q (format "%s < ?" field-name) :p [input-value]})

(defmethod to-sql [:bounded :greater]
  [{:keys [field-name input-value]}]
  {:q (format "%s > ?" field-name) :p [input-value]})

(defmethod to-sql [:bounded :between]
  [{:keys [field-name input-value max-input-value]}]
   {:q (format "%s between ? and ?" field-name) :p [input-value max-input-value]})

and use it somehow like this:

(def params [{:field-name "salary" :comparator "greater" :input-value "10000" :input-type "number"}
             {:field-name "name" :comparator "include" :input-value "Ros" :input-type "text"}
             {:field-name "start_date" :comparator "between" :input-value "2022-01-01" :max-input-value "2022-06-01" :input-type "date"}])

(let [data (map to-sql params)]
  {:query (clojure.string/join " and " (map :q data))
   :params (into [] (mapcat :p) data)})

;;=>{:query "salary > ? and name like '%Ros%' and start_date between ? and ?",
;;   :params ["10000" "2022-01-01" "2022-06-01"]}

the best part of it, is that you can always add new types / selectors without touching the existing (and presumably well tested) code at all. Like for example you want to add the in-list operator:

(defmethod to-sql [:any :in-list]
  [{:keys [field-name input-value]}]
   {:q (format "%s in (%s)"
               field-name
               (clojure.string/join ", " (repeat (count input-value) "?")))
    :p input-value})

(def params [{:field-name "salary" :comparator "greater" :input-value "10000" :input-type "number"}
             {:field-name "name" :comparator "include" :input-value "Ros" :input-type "text"}
             {:field-name "rank" :comparator "in-list" :input-value ["middle" "junor"] :input-type "text"}
             {:field-name "start_date" :comparator "between" :input-value "2022-01-01" :max-input-value "2022-06-01" :input-type "date"}])

(let [data (map to-sql params)]
  {:query (clojure.string/join " and " (map :q data))
   :params (into [] (mapcat :p) data)})

;;=> {:query "salary > ? and name like '%Ros%' and rank in (?, ?) and start_date between ? and ?",
;;    :params ["10000" "middle" "junor" "2022-01-01" "2022-06-01"]}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文