Clojure HoneySQL - How to aggregate a string value after a join into a single row?

421 views Asked by At

I'm performing the following query that joins across 3 tables to pull back workouts and the tags associated with them.

(db/query {:select [:workouts.id :workouts.name :tag.tag_name]
             :from   [:workouts]
             :left-join [[:workout_tags :workout_tag] [:= :workout_tag.workout_id :workouts.id]
                         [:tags :tag] [:= :tag.tag_id :workout_tag.tag_id]]
             :where  [:= :workouts.id 1]}))

This returns the following:

({:id 1, :name "Short", :tag_name "cardio"} 
 {:id 1, :name "Short", :tag_name "No weights"})

Ideally, I'd like to return to the end user a single result with the tag_name being combined into a single field. Something like:

{:id 1, :name "Short", :tag_name ["cardio" "No weights"]} 

It would see that I could fairly easily do this after the fact, but I wanted to see if there's a built in MySQL function that can do what I'm looking to accomplish. It seems maybe GROUP_CONACT might do what I'm looking for, but I can't seem to get it to work in the context of HoneySQL.

2

There are 2 answers

0
Alan Thompson On

I would do it after the fact:

  (let [data    [{:id 1, :name "Short", :tag_name "cardio"}
                 {:id 1, :name "Short", :tag_name "No weights"}
                 {:id 2, :name "Long", :tag_name "Tall"}
                 {:id 2, :name "Long", :tag_name "Hills"}]
        grouped (group-by :id data)
        id-tags (vec (for [[id data-maps] grouped]
                       (let [tags (mapv :tag_name data-maps)]
                         {:id id :tags tags})))]
    (is= id-tags
      [{:id 1, :tags ["cardio" "No weights"]}
       {:id 2, :tags ["Tall" "Hills"]}]))

The intermediate result grouped looks like

grouped => 
{1
 [{:id 1, :name "Short", :tag_name "cardio"}
  {:id 1, :name "Short", :tag_name "No weights"}],
 2
 [{:id 2, :name "Long", :tag_name "Tall"}
  {:id 2, :name "Long", :tag_name "Hills"}]}

See my favorite template project for full config details.

0
Sean Corfield On

The following should be close to what you need:

  (require '[honeysql.core :as hc])
  (hc/format {:select [:workouts.id :workouts.name [(hc/call :group_concat :tag.tag_name) :tag_name]]
              :from   [:workouts]
              :left-join [[:workout_tags :workout_tag] [:= :workout_tag.workout_id :workouts.id]
                          [:tags :tag] [:= :tag.tag_id :workout_tag.tag_id]]
              :where  [:= :workouts.id 1]
              :group-by [:tag.tag_name]})

That produces the following SQL:

SELECT workouts.id, workouts.name, group_concat(tag.tag_name) AS tag_name
FROM workouts
LEFT JOIN workout_tags workout_tag ON workout_tag.workout_id = workouts.id
LEFT JOIN tags tag ON tag.tag_id = workout_tag.tag_id
WHERE workouts.id = ?
GROUP BY tag.tag_name

You didn't show what you had tried or what failed, which would definitely be helpful for us in ascertaining what we should suggest as an answer.