Clojure宏用于使用类似SQL的语法在地图列表中进行搜索

时间:2017-12-08 21:11:48

标签: clojure macros

示例输入

=> (def persons '({:id 1 :name "olle"} {:id 2 :name "anna"} {:id 3 :name
"isak"} {:id 4 :name "beatrice"}))

=> (select [:id :name] from persons where [:id > 2] orderby :name)

示例输出

({:id 4 :name "beatrice"} {:id 3 :name "isak"})

查询格式应为:

(select [columns]
from #{table}
where [:column op value]
orderby :column)

其中'op'等于'=''<' '>'或'<>'

尝试解决方案

(defmacro select
    [vara _ coll _ wherearg _ orderarg]
    '(map [~vara]
        (filter [~wherearg]
            (sort-by ~orderarg) ~coll)
        )
    ) 

我的解决方案还需要什么? 我不明白这些错误是什么意思,所以我甚至不知道应该寻找什么样的解决方案。

IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Symbol  clojure.lang.RT.seqFrom (RT.java:542)

2 个答案:

答案 0 :(得分:1)

我会解决这个问题,坚持使用常规的Clojure语法,而不是尝试重新发明SQL。像这样:

jsonData

结果

(def persons '( {:id 1 :name "olle"}
                {:id 2 :name "anna"}
                {:id 3 :name "isak"}
                {:id 4 :name "beatrice"} ))

(defn id-over-2?
  [person]
  (< 2 (:id person)))

(def persons-2 (filter id-over-2? persons) )

(def persons-3 (sort-by :name persons-2))

答案 1 :(得分:1)

使用Clojure方式,使用常规函数组合和宏语法糖:

您的具体示例可以解决如下:

(->> persons
     (filter #(>= (:id %) 2))
     (sort-by :name)
     (map #(select-keys % [:id :name]))

但我们尝试创建一种更通用的解决方案。

(defn select
  "Takes a set of keys as input, returns a transducer
   which will select only the given keys for each item"
  [& keys]
  (map #(select-keys % keys)))

(defn filter-by [field predicate]
  (filter #(-> % field predicate)))

(defn filter-by-name [user-name]
  "Filters by users with the given name"
  (filter-by :name #(= % user-name)))

(defn filter-by-age [user-age]
  "Filters by users with the given age"
  (filter-by :age #(= % user-age))

(defn filter-by-age-over [user-age]
  "Filters by users with the age strictly above user-age"
  (filter-by :age #(> % user-age))

(defn filter-by-age-below [user-age]
  "Filters by users with the age strictly below user-age"
  (filter-by :age #(< % user-age))

(defn filter-by-id-over [id-val]
  (filter-by :id #(> % id-val))

(defn filter-by-gender [gender]
  (filter-by :gender #(= % gender))

我们现在可以使用以下这些功能

(def xf-charly-age-5 
  (comp (filter-by-name "Charly") 
        (filter-by-age 5) 
        (select :name :id)))

(defn xf-adults
  (comp (filter-by-age-over 18) 
        (select :name :id)))

(defn xf-male-adults
  (comp (filter-by-age-over 18) 
        (filter-by-gender :male)
        (select :name :id)))

(defn search-users [xf users]
  (->> (into [] xf users)
       (sort-by

您现在可以使用以下内容:

(search-users xf-male-adults all-users)
(search-users xf-adults all-users)
(search-users xf-charly-age-5 all-users

有关排序的说明:

不幸的是,Clojure没有配备分类传感器。但是,您可以从https://github.com/cgrand/xforms项目中获取一个项目,或使用sort-by函数自行实施排序。