PageRenderTime 43ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/src/pretty/core.clj

http://github.com/dirtyvagabond/pretty-ql
Clojure | 172 lines | 129 code | 35 blank | 8 comment | 2 complexity | 775f9c13a4fe18a0cabef73379a1f601 MD5 | raw file
  1. (ns pretty.core
  2. (:require [funnyplaces.api :as fun]
  3. [clojure.walk :as walk]
  4. [clojure.string :as str]))
  5. ;; --- pretty helpers ---
  6. ;; assumes s always like "term*"
  7. ;; PS: Laura says: iloveyou
  8. (defn like-str->m [s]
  9. {:$bw (apply str (butlast s))})
  10. ;; assumes s always like "term*"
  11. (defn not-like-str->m [s]
  12. {:$nbw (apply str (butlast s))})
  13. (defn pred
  14. "Creates the most common predicate spec. %1 is field name, %2 is val.
  15. Example use in fn lookup table:
  16. '> (pred :$gt)
  17. Example final result when used to interpret (... (> :rank 8.5) ...):
  18. {:rank {:$gt 8.5}}"
  19. [pred-name]
  20. `#(hash-map %1 {~pred-name %2}))
  21. ;; --- pretty fn translation tables ---
  22. (def keywords
  23. {'where 'pretty.core/where
  24. 'fields 'pretty.core/fields
  25. 'order 'pretty.core/order
  26. 'search 'pretty.core/search
  27. 'offset 'pretty.core/offset
  28. 'limit 'pretty.core/limit
  29. 'circle 'pretty.core/circle
  30. 'around 'pretty.core/around})
  31. (def preds {'and 'pretty.core/+and
  32. 'or 'pretty.core/+or
  33. 'search 'pretty.core/search
  34. 'like 'pretty.core/+like
  35. 'not-like 'pretty.core/+not-like
  36. 'blank 'pretty.core/+blank
  37. 'not-blank 'pretty.core/+not-blank
  38. '= (pred :$eq)
  39. 'not= (pred :$neq)
  40. '> (pred :$gt)
  41. '>= (pred :$gte)
  42. '< (pred :$lt)
  43. '<= (pred :$lte)
  44. 'in (pred :$in)
  45. 'not-in (pred :$nin)})
  46. ;; --- pretty fns ---
  47. (defn fields [q & forms]
  48. ;;;(update-in q [:select] into (map name forms))
  49. (assoc q :select (str/join "," (map name forms)))
  50. )
  51. (defn order [q & forms]
  52. (assoc q :sort (str/join "," (map name forms))))
  53. (defn +or [& clauses]
  54. {:$or (vec clauses)})
  55. (defn +and [& clauses]
  56. {:$and (vec clauses)})
  57. (defn search
  58. "Supports 2 variants of full text search:
  59. 1) At top level of query, so FTS across row, like:
  60. (select ... (search \"myterm\"))
  61. arg1 will be the query map, arg2 the search term.
  62. 2) Within the where clause, so for a specific field, like:
  63. (select ... (where ... (search :tel \"myterm\")))
  64. arg1 will be the field name, arg2 the searh term."
  65. [arg1 arg2]
  66. (if (map? arg1)
  67. (assoc arg1 :q arg2)
  68. {arg1 {:$search arg2}}))
  69. (defn +like [field term]
  70. {field (like-str->m term)})
  71. (defn +not-like [field term]
  72. {field (not-like-str->m term)})
  73. (defn +blank [field]
  74. {field {:$blank true}})
  75. (defn +not-blank [field]
  76. {field {:$blank false}})
  77. (defn limit [q limit]
  78. (assoc q :limit limit))
  79. (defn offset [q offset]
  80. (assoc q :offset offset))
  81. (defn circle
  82. "Adds a geo proximity filter to query q.
  83. Expects a hash-map describing a circle, like:
  84. {:center [40.73 -74.01]
  85. :meters 5000}
  86. Supports one of :miles or :meters"
  87. [q circ]
  88. (let [meters (or (:meters circ) (* (:miles circ) 1609.344))
  89. center {:$center (:center circ) :$meters meters}]
  90. (assoc q :geo {:$circle center})))
  91. (defn around
  92. "Adds a geo proximity filter to query q.
  93. Expects a hash-map describing a circle, like:
  94. {:lat 34.06021 :lon -118.4183 :miles 3}
  95. Only supports :miles"
  96. [q {:keys [lat lon miles]}]
  97. (circle q {:center [lat lon] :miles miles}))
  98. ;; --- pretty DSL ---
  99. (defn init!
  100. "Establishes your oauth credentials for Factual. You only need to
  101. do this once for the lifetime of your application."
  102. [key secret]
  103. (fun/factual! key secret))
  104. (defn pretty!
  105. "DEPRECATED: Use 'init!' instead"
  106. [key secret]
  107. {:added "0.0.1"
  108. :deprecated "1.0.4"}
  109. (fun/factual! key secret))
  110. (defn exec [query]
  111. (let [table (:table (meta query))]
  112. (fun/fetch-q table query)))
  113. (defn select* [table]
  114. (with-meta
  115. {}
  116. {:type :select
  117. :table table}))
  118. (defn where*
  119. "Add a where clause to the query. Clause can be either a map or a string, and
  120. will be AND'ed to the other clauses."
  121. [query clause]
  122. (update-in query [:filters] conj clause))
  123. (defmacro where [query & clauses]
  124. (let [xform (walk/postwalk-replace preds clauses)]
  125. `(update-in ~query [:filters] merge ~@xform)))
  126. (defmacro select [table & clauses]
  127. (let [xform (walk/postwalk-replace keywords clauses)]
  128. `(let [query# (-> (select* ~(name table)) ~@xform)]
  129. (exec query#))))
  130. (defmacro schema [table]
  131. `(fun/schema ~(name table)))
  132. (defn resolve
  133. "Direct support for Resolve. values must be a hashmap, where keys
  134. are valid attributes for the schema, and values are the values on
  135. which to match."
  136. [values]
  137. (fun/resolve values))