From 955fb20acc75db1822a634209e180c2e05d3fa2e Mon Sep 17 00:00:00 2001
From: Simon Brooke <simon@journeyman.cc>
Date: Sat, 17 Mar 2018 23:59:09 +0000
Subject: [PATCH] Added a first stab at a general text search query

---
 src/squirrel_parse/to_hugsql_queries.clj | 70 ++++++++++++++++++------
 src/squirrel_parse/to_json_routes.clj    | 57 +++++++++++--------
 2 files changed, 89 insertions(+), 38 deletions(-)

diff --git a/src/squirrel_parse/to_hugsql_queries.clj b/src/squirrel_parse/to_hugsql_queries.clj
index cd67655..cfb8c87 100644
--- a/src/squirrel_parse/to_hugsql_queries.clj
+++ b/src/squirrel_parse/to_hugsql_queries.clj
@@ -110,6 +110,41 @@
     {}))
 
 
+(defn search-query [entity-map]
+  (let [entity-name (:name (:attrs entity-map))
+        pretty-name (singularise entity-name)
+        query-name (str "search-strings-" pretty-name)
+        signature ":? :1"
+        string-fields (filter
+                       #(= (-> % :attrs :type) "string")
+                       (-> entity-map :content :properties vals))]
+    (if
+      (empty? string-fields)
+      {}
+      (hash-map
+       (keyword query-name)
+       {:name query-name
+        :signature signature
+        :entity entity-map
+        :type :text-search
+        :query
+        (str "-- :name " query-name " " signature "\n"
+             "-- :doc selects existing " entity-name " records having any string field matching `:pattern` by substring match\n"
+             "SELECT * FROM " entity-name "\n"
+             "WHERE "
+             (s/join
+              "\n\tOR "
+              (map
+               #(str (-> % :attrs :name) " LIKE '%:pattern%'")
+               string-fields))
+             "\n"
+             (order-by-clause entity-map)
+             "\n"
+            "--~ (if (:offset params) \"OFFSET :offset \") \n"
+            "--~ (if (:limit params) \"LIMIT :limit\" \"LIMIT 100\")"
+             "\n\n")}))))
+
+
 (defn select-query [entity-map]
   (if
     (has-primary-key? entity-map)
@@ -272,6 +307,7 @@
       (merge
         (select-query entity-map)
         (list-query entity-map)
+        (search-query entity-map)
         (foreign-queries entity-map entities-map)))))
 
 
@@ -282,21 +318,23 @@
    (let
      [adl-struct (migrations-to-xml migrations-path "Ignored")
       file-content (apply
-                     str
-                     (cons
-                       (str "-- "
-                            output
-                            " autogenerated by \n-- [squirrel-parse](https://github.com/simon-brooke/squirrel-parse)\n-- at "
-                            (f/unparse (f/formatters :basic-date-time) (t/now))
-                            "\n\n")
-                       (doall
-                         (map
-                              #(:query %)
-                              (vals
-                                (apply
-                                  merge
-                                  (map
-                                    #(queries % adl-struct)
-                                    (vals adl-struct))))))))]
+                    str
+                    (cons
+                     (str "-- "
+                          output
+                          " autogenerated by \n-- [squirrel-parse](https://github.com/simon-brooke/squirrel-parse)\n-- at "
+                          (f/unparse (f/formatters :basic-date-time) (t/now))
+                          "\n\n")
+                     (doall
+                      (map
+                       #(:query %)
+                       (sort
+                        #(compare (:name %1) (:name %2))
+                        (vals
+                         (apply
+                          merge
+                          (map
+                           #(queries % adl-struct)
+                           (vals adl-struct)))))))))]
      (spit output file-content)
      file-content)))
diff --git a/src/squirrel_parse/to_json_routes.clj b/src/squirrel_parse/to_json_routes.clj
index 2f9efe4..ed3d9ad 100644
--- a/src/squirrel_parse/to_json_routes.clj
+++ b/src/squirrel_parse/to_json_routes.clj
@@ -42,7 +42,7 @@
 (defn file-header [parent-name this-name]
   (list
     'ns
-    (symbol (str parent-name "." this-name))
+    (symbol (str parent-name ".routes." this-name))
     (str "JSON routes for " parent-name
          " auto-generated by [squirrel-parse](https://github.com/simon-brooke/squirrel-parse) at "
          (f/unparse (f/formatters :basic-date-time) (t/now)))
@@ -102,27 +102,27 @@
           :delete-1
           (generate-handler-src
             handler-name query :post
-            (str "delete one record from the "
+            (str "delete one record from the `"
                  (-> query :entity :attrs :name)
-                 " table. Expects the following key(s) to be present in `params`: "
+                 "` table. Expects the following key(s) to be present in `params`: `"
                  (doall (-> query :entity :content :key :content keys))
-                 "."))
+                 "`."))
           :insert-1
           (generate-handler-src
             handler-name query :post
-            (str "insert one record to the "
+            (str "insert one record to the `"
                  (-> query :entity :attrs :name)
-                 " table. Expects the following key(s) to be present in `params`: "
+                 "` table. Expects the following key(s) to be present in `params`: `"
                  (pr-str (-> query :entity :content :properties keys))
-                 ". Returns a map containing the keys "
+                 "`. Returns a map containing the keys `"
                  (pr-str (-> query :entity :content :key :content keys))
-                 " identifying the record created."))
+                 "` identifying the record created."))
           :update-1
           (generate-handler-src
             handler-name query :post
-            (str "update one record in the "
+            (str "update one record in the `"
                  (-> query :entity :attrs :name)
-                 " table. Expects the following key(s) to be present in `params`: "
+                 "` table. Expects the following key(s) to be present in `params`: `"
                  (pr-str
                    (distinct
                      (sort
@@ -130,15 +130,15 @@
                          (cons
                            (-> query :entity :content :properties keys)
                            (-> query :entity :content :key :content keys))))))
-                 "."))
+                 "`."))
           :select-1
           (generate-handler-src
             handler-name query :post
-            (str "select one record from the "
+            (str "select one record from the `"
                  (-> query :entity :attrs :name)
-                 " table. Expects the following key(s) to be present in `params`: "
+                 "` table. Expects the following key(s) to be present in `params`: `"
                  (pr-str (-> query :entity :content :key :content keys))
-                 ". Returns a map containing the following keys: "
+                 "`. Returns a map containing the following keys: `"
                  (pr-str
                    (distinct
                      (sort
@@ -146,13 +146,13 @@
                          (cons
                            (-> query :entity :content :properties keys)
                            (-> query :entity :content :key :content keys))))))
-                 "."))
-            :select-many
+                 "`."))
+          :select-many
           (generate-handler-src
             handler-name query :get
-            (str "select all records from the "
+            (str "select all records from the `"
                  (-> query :entity :attrs :name)
-                 " table. If the keys (:limit :offset) are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: "
+                 "` table. If the keys `(:limit :offset)` are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: `"
                  (pr-str
                    (distinct
                      (sort
@@ -160,17 +160,30 @@
                          (cons
                            (-> query :entity :content :properties keys)
                            (-> query :entity :content :key :content keys))))))
-                 "."))
-
+                 "`."))
+          :text-search
+          (generate-handler-src
+            handler-name query :get
+            (str "select all records from the `"
+                 (-> query :entity :attrs :name)
+                 "` table with any text field matching the value of the key `:pattern` which should be in the request. If the keys `(:limit :offset)` are present in the request then they will be used to page through the data. Returns a sequence of maps each containing the following keys: `"
+                 (pr-str
+                   (distinct
+                     (sort
+                       (flatten
+                         (cons
+                           (-> query :entity :content :properties keys)
+                           (-> query :entity :content :key :content keys))))))
+                 "`."))
           (:select-many-to-many
-            :select-one-to-many)
+           :select-one-to-many)
           (hash-map :method :get
                     :src (list 'defn handler-name [{:keys ['params]}]
                                (list 'do (list (symbol (str "db/" (:name query))) 'params))))
           ;; default
           (hash-map
             :src
-            (str ";; don't know what to do with query " :key " of type " (:type query))))))))
+            (str ";; don't know what to do with query `" :key "` of type `" (:type query) "`.")))))))
 
 
 (defn defroutes [handlers-map]