2 votes

Clojure HoneySQL - Comment agréger une chaîne de caractères en une seule ligne après une jointure ?

J'exécute la requête suivante qui relie 3 tables pour récupérer les entraînements et les balises qui leur sont associées.

(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]}))

Le résultat est le suivant :

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

Idéalement, j'aimerais renvoyer à l'utilisateur final un résultat unique avec la balise tag_name en un seul champ. Quelque chose comme :

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

Il semblerait que je puisse assez facilement le faire après coup, mais je voulais voir s'il existait une fonction MySQL intégrée qui puisse faire ce que je cherche à accomplir. Il semble que GROUP_CONACT pourrait faire ce que je cherche, mais je n'arrive pas à le faire fonctionner dans le contexte de HoneySQL.

2voto

Sean Corfield Points 1580

La liste suivante devrait être proche de ce dont vous avez besoin :

  (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]})

Cela produit le code SQL suivant :

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

Vous n'avez pas indiqué ce que vous avez essayé ou ce qui a échoué, ce qui nous aiderait certainement à déterminer ce que nous devrions suggérer comme réponse.

1voto

Alan Thompson Points 325

Je le ferais après coup :

  (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"]}]))

Le résultat intermédiaire grouped ressemble à

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"}]}

Voir mon projet de gabarit préféré pour obtenir tous les détails de la configuration.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X