sova
sova

Reputation: 5650

merging certain attributes in datomic query results

So I have some posts that get tagged, and I'd like users to be able to add tags, and then I'd like to be able to do a query and get an aggregated list of tags for particular posts. (Currently the behavior just returns a list of entities, each with a different :tags attribute.)

({:title "Straight edges ...", 
  :content "If you ever ... ", 
  :tags "folding",              <<< +
  :eid 1759} 
 {:title "Straight edges ...", 
  :content "If you ever ...", 
  :tags "art",                  <<< +
  :eid 1759} 
 {:title "Straight edges ...", 
  :content "If you ever ... ", 
  :tags "scissor-less edges",   <<< +
  :eid 1759} 
 {:title "Straight edges ...", 
  :content "If you ever ...  ", 
  :tags "snips",                <<< +
  :eid 1759} 
 {:title "Straight edges ...", 
  :content "If you ever ...  ", 
  :tags "scissor-less edges",   <<< ^  How to combine?
  :eid 1759})

My query looks like

(defn get-post-by-eid [eid]
  (->> (d/q '[:find ?title ?content ?tags ?eid
              :in $ ?eid
              :where
             [?eid post/title ?title]
             [?eid post/content ?content]
             [?eid post/tag ?tags]] (d/db conn) eid)
       (map (fn [[title content tags eid]]
             {:title title
              :content content
              :tags tags
              :eid eid}))
       (sort-by :eid)))

The desired result is something like

({:title "Straight edges ...", 
  :content "If you ever ... ", 
  :tags "folding, art, scissor-less edges, snips", ;;combination
  :eid 1759} 

Any tips on how I can query for this, or how I can mash all the query results together? Thanks in advance

Upvotes: 2

Views: 253

Answers (1)

Arthur Ulfeldt
Arthur Ulfeldt

Reputation: 91554

I'm personally fond of combining sort-by partition-by and merge-with for things like this, though in this case since everything is the same between entries except the tags you can skip the merge step and just stick the correct :tags value into any arbitrary list entry (I chose the first)

user> (->>
       '({:title "Straight edges ...", 
          :content "If you ever ... ", 
          :tags "folding",              
          :eid 1759} 
         {:title "Straight edges ...", 
          :content "If you ever ...", 
          :tags "art",                  
          :eid 1759} 
         {:title "Straight edges ...", 
          :content "If you ever ... ", 
          :tags "scissor-less edges",   
          :eid 1759} 
         {:title "Straight edges ...", 
          :content "If you ever ...  ", 
          :tags "snips",                
          :eid 1759}
         {:title "other post edges ...", 
          :content "If you ever ...  ", 
          :tags "example",                
          :eid 9999}
         {:title "Straight edges ...", 
          :content "If you ever ...  ", 
          :tags "scissor-less edges",   
          :eid 1759})
       (sort-by :eid)
       (partition-by :eid)
       (map #(assoc (first %) 
                    :tags (clojure.string/join ", " (map :tags %))))
       pprint)

Which produces a sequence of posts with rolled up tags:

({:title
  "Straight edges ...",
  :content "If you ever ... ", 
  :tags "folding, art, scissor-less edges, snips, scissor-less edges",
  :eid 1759}
 {:title "other post edges ...",
  :content "If you ever ...  ",
  :tags "example",
  :eid 9999})

Upvotes: 3

Related Questions