Skip to content

Instantly share code, notes, and snippets.

@vvvvalvalval
Last active August 29, 2015 14:15
Show Gist options
  • Save vvvvalvalval/c3d9bae9a003852a1acc to your computer and use it in GitHub Desktop.
Save vvvvalvalval/c3d9bae9a003852a1acc to your computer and use it in GitHub Desktop.
'populate foreign keys' functionality with Monger, emulates a subset of Mongoose's populate
(require '[monger.collection :as mc])
(require '[monger.operators :as mop])
(defn populate "Populates the given docs sequence by looking up the 'foreign key' as an :_id in `foreign-coll`.
`foreign-path` can be either a single key or a sequence of keys (as in get-in)
Assumes the foreign keys are ObjectIds or coercable to objectIds.
Returns a seq of the docs where the foreign keys have been updated to be the foreign documents, in the same order.
"
[foreign-coll foreign-path docs]
(let [foreign-path (if (sequential? foreign-path) foreign-path [foreign-path]) ;; convert path to a seq if it's not one
foreign-keys (->> docs (map #(get-in % foreign-path)) (filter some?) set) ;; the set of foreign keys to pre-fetch
foreign-docs (->> (mc/find-maps foreign-coll {:_id {mop/$in foreign-keys}}) ;; fetch the foreign keys and keep a map foreign-key -> foreign-doc
(reduce (fn [m {:keys [_id] :as fd}] (assoc m _id fd)) {}))]
(->> docs (map #(update-in % foreign-path foreign-docs))) ;; replace foreign key with foreign doc in each document
))
(comment "Example"
(def movies-coll "movies")
(def people-coll "people")
;; without populate.
;; Documents in the "movies" collection have a 'foreign key' to documents in the "people" collection.
(mc/find-maps movies-coll)
=> ({:_id "54d34ffbd4c6b26ee3db8a28",
:title "2001: A Space Odyssey",
:staff {:director "546f2ac50e73c602001b97ee"}},
{:_id "54d34ffbd4c6b26ee3db8a2c",
:title "Gladiator",
:staff {:director "5475b212cad31102007a562e"}}
{:_id "54d34ffbd4c6b26ee3db8a2e",
:title "The Lord of the Rings",
:staff {:director "548b28c6df8b39030021e36c"}})
;; with populate.
(->> (mc/find-maps movies-coll)
(populate people-coll [:staff :director]))
=> ({:_id "54d34ffbd4c6b26ee3db8a28",
:title "2001: A Space Odyssey",
:staff {:director {:_id "546f2ac50e73c602001b97ee",
:firstName "Stanley", :lastName "Kubrick"}}},
{:_id "54d34ffbd4c6b26ee3db8a2c",
:title "Gladiator",
:staff {:director {:_id "5475b212cad31102007a562e"
:firstName "Ridley", :lastName "Scott"}}}
{:_id "54d34ffbd4c6b26ee3db8a2e",
:title "The Lord of the Rings",
:staff {:director {:_id "548b28c6df8b39030021e36c"
:firstName "Peter", :lastName "Jackson"}}})
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment