{"id":575,"date":"2017-08-30T10:38:04","date_gmt":"2017-08-30T17:38:04","guid":{"rendered":"https:\/\/www.kenwalger.com\/blog\/?p=575"},"modified":"2017-10-16T04:40:48","modified_gmt":"2017-10-16T11:40:48","slug":"performing-joins-mongodb-lookup","status":"publish","type":"post","link":"https:\/\/www.kenwalger.com\/blog\/nosql\/mongodb\/performing-joins-mongodb-lookup\/","title":{"rendered":"Performing joins in MongoDB with $lookup"},"content":{"rendered":"<p>Eventually, it seems, looking up data in multiple <a href=\"https:\/\/www.mongodb.com\">MongoDB<\/a> collections at the same time becomes necessary. Okay,\u00a0<em>necessary<\/em>, might be strongly phrased. &#8220;Very helpful&#8221; may be better. Until version 3.2 of MongoDB doing joins wasn&#8217;t possible, but with that version came the <code>$lookup<\/code> <a href=\"https:\/\/docs.mongodb.com\/manual\/reference\/operator\/aggregation\/\">aggregation stage operator<\/a>.\u00a0The introduction of <code>$lookup<\/code> allows for left outer joins to be performed on collections in the same database which\u00a0are\u00a0not\u00a0<a href=\"https:\/\/www.kenwalger.com\/blog\/nosql\/mongodb-horizontal-scaling-sharding\/\">sharded<\/a>.<\/p>\n<p>For our data for this post let&#8217;s utilize the concept of recipes. I&#8217;ll lay out some basic traditional SQL database tables and walk through the SQL syntax for a join.<\/p>\n<p><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" data-attachment-id=\"581\" data-permalink=\"https:\/\/www.kenwalger.com\/blog\/nosql\/mongodb\/performing-joins-mongodb-lookup\/attachment\/recipe_tables\/\" data-orig-file=\"https:\/\/i0.wp.com\/www.kenwalger.com\/blog\/wp-content\/uploads\/2017\/08\/recipe_tables.png?fit=1199%2C674&amp;ssl=1\" data-orig-size=\"1199,674\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"recipe_tables\" data-image-description=\"\" data-image-caption=\"\" data-medium-file=\"https:\/\/i0.wp.com\/www.kenwalger.com\/blog\/wp-content\/uploads\/2017\/08\/recipe_tables.png?fit=300%2C169&amp;ssl=1\" data-large-file=\"https:\/\/i0.wp.com\/www.kenwalger.com\/blog\/wp-content\/uploads\/2017\/08\/recipe_tables.png?fit=840%2C473&amp;ssl=1\" class=\"aligncenter size-full wp-image-581\" src=\"https:\/\/i0.wp.com\/www.kenwalger.com\/blog\/wp-content\/uploads\/2017\/08\/recipe_tables.png?resize=840%2C472&#038;ssl=1\" alt=\"Recipe table layout\" width=\"840\" height=\"472\" srcset=\"https:\/\/i0.wp.com\/www.kenwalger.com\/blog\/wp-content\/uploads\/2017\/08\/recipe_tables.png?w=1199&amp;ssl=1 1199w, https:\/\/i0.wp.com\/www.kenwalger.com\/blog\/wp-content\/uploads\/2017\/08\/recipe_tables.png?resize=300%2C169&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.kenwalger.com\/blog\/wp-content\/uploads\/2017\/08\/recipe_tables.png?resize=768%2C432&amp;ssl=1 768w, https:\/\/i0.wp.com\/www.kenwalger.com\/blog\/wp-content\/uploads\/2017\/08\/recipe_tables.png?resize=1024%2C576&amp;ssl=1 1024w\" sizes=\"auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><\/p>\n<h3>SQL Syntax<\/h3>\n<p>Let&#8217;s have a quick look at what a left outer join looks like in SQL with a couple of different syntaxes in SQL.<\/p>\n<h6>Standard SQL<\/h6>\n<pre>SELECT recipe_type.decription, recipes.title\nFROM recipe_type\nLEFT OUTER JOIN recipes\nON recipe_type.id = recipes.recipe_type_id\n<\/pre>\n<h6>Oracle<\/h6>\n<pre>SELECT recipe_type.description, recipes.title\nFROM recipe_type, recipes\nWHERE recipe_type.id = recipes.recipe_type_id(+)\n<\/pre>\n<p>The results of a left outer join for these two tables will contain <em>all<\/em> of the rows from our &#8220;left&#8221; table (recipe_type).<\/p>\n<h3>$lookup for Joins<\/h3>\n<figure id=\"attachment_584\" aria-describedby=\"caption-attachment-584\" style=\"width: 1199px\" class=\"wp-caption aligncenter\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" data-attachment-id=\"584\" data-permalink=\"https:\/\/www.kenwalger.com\/blog\/nosql\/mongodb\/performing-joins-mongodb-lookup\/attachment\/recipe_join_venn_diagram\/\" data-orig-file=\"https:\/\/i0.wp.com\/www.kenwalger.com\/blog\/wp-content\/uploads\/2017\/08\/recipe_join_venn_diagram.png?fit=1199%2C674&amp;ssl=1\" data-orig-size=\"1199,674\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"recipe_join_venn_diagram\" data-image-description=\"\" data-image-caption=\"&lt;p&gt;Concept of a Left Outer Join&lt;\/p&gt;\n\" data-medium-file=\"https:\/\/i0.wp.com\/www.kenwalger.com\/blog\/wp-content\/uploads\/2017\/08\/recipe_join_venn_diagram.png?fit=300%2C169&amp;ssl=1\" data-large-file=\"https:\/\/i0.wp.com\/www.kenwalger.com\/blog\/wp-content\/uploads\/2017\/08\/recipe_join_venn_diagram.png?fit=840%2C473&amp;ssl=1\" class=\"size-full wp-image-584\" src=\"https:\/\/i0.wp.com\/www.kenwalger.com\/blog\/wp-content\/uploads\/2017\/08\/recipe_join_venn_diagram.png?resize=840%2C472&#038;ssl=1\" alt=\"Concept of a Left Outer Join\" width=\"840\" height=\"472\" srcset=\"https:\/\/i0.wp.com\/www.kenwalger.com\/blog\/wp-content\/uploads\/2017\/08\/recipe_join_venn_diagram.png?w=1199&amp;ssl=1 1199w, https:\/\/i0.wp.com\/www.kenwalger.com\/blog\/wp-content\/uploads\/2017\/08\/recipe_join_venn_diagram.png?resize=300%2C169&amp;ssl=1 300w, https:\/\/i0.wp.com\/www.kenwalger.com\/blog\/wp-content\/uploads\/2017\/08\/recipe_join_venn_diagram.png?resize=768%2C432&amp;ssl=1 768w, https:\/\/i0.wp.com\/www.kenwalger.com\/blog\/wp-content\/uploads\/2017\/08\/recipe_join_venn_diagram.png?resize=1024%2C576&amp;ssl=1 1024w\" sizes=\"auto, (max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><figcaption id=\"caption-attachment-584\" class=\"wp-caption-text\">Concept of a Left Outer Join<\/figcaption><\/figure>\n<p>Our join from <code>$lookup<\/code> then should be the same from a conceptual standpoint as our SQL cousin. Assuming we have a collection of recipe types and one of recipes\u00a0our <code>$lookup<\/code> stage would look like the following:<\/p>\n<pre>{\n  $lookup:\n    {\n      from: \"recipes\",\n      localField: \"id\",\n      foreignField: \"recipe_type_id\",\n      as: \"recipe_categories\"\n    }\n}\n<\/pre>\n<p>Our <code>$lookup<\/code> stage then is doing an equality match between the two documents based on the <code>id<\/code> and <code>recipe_type_id<\/code> fields. It will add a new array field to the document in our aggregation pipeline document and passes it to the next stage of the pipeline.<\/p>\n<p>Now, given our knowledge of <a href=\"https:\/\/www.kenwalger.com\/blog\/nosql\/mongodb\/schema-design-considerations-mongodb\/\">schema design<\/a> and <a href=\"https:\/\/www.kenwalger.com\/blog\/nosql\/document-model\/\">document models<\/a> in MongoDB we may not have a need for this exact join as these two collections of data\u00a0<em>might<\/em> be embedded in one or the other collection. However, depending on your data access patterns and application needs, the collections may be utilizing references.<\/p>\n<h3>Wrap Up<\/h3>\n<p>I hope this provided a useful, and quick, introduction to the <code>$lookup<\/code> aggregation stage and how it is possible to do joins in MongoDB. While you may not find yourself using it all of the time, it is a great tool to have at the ready when it is needed.<\/p>\n<p>There are several MongoDB specific terms in this post. I created a <a href=\"https:\/\/www.echoskillstore.com\/MongoDB-Dictionary\/45103\">MongoDB Dictionary<\/a> skill for the <a href=\"https:\/\/www.amazon.com\/gp\/product\/B01DFKC2SO\/ref=as_li_tl?ie=UTF8&amp;camp=1789&amp;creative=9325&amp;creativeASIN=B01DFKC2SO&amp;linkCode=as2&amp;tag=kenwalgersite-20&amp;linkId=f9e513223de2525a72b95cf9561db55b\" rel=\"noopener noreferrer\">Amazon Echo<\/a>\u00a0line of products. Check it out and you can say &#8220;Alexa, ask MongoDB what is a document?&#8221; and get a helpful response.<\/p>\n<hr \/>\n<p><em>Follow me on Twitter <a href=\"https:\/\/www.twitter.com\/kenwalger\">@kenwalger<\/a> to get the latest updates on my postings.<\/em><\/p>\n<a class=\"synved-social-button synved-social-button-share synved-social-size-48 synved-social-resolution-single synved-social-provider-facebook nolightbox\" data-provider=\"facebook\" target=\"_blank\" rel=\"nofollow\" title=\"Share on Facebook\" href=\"https:\/\/www.facebook.com\/sharer.php?u=https%3A%2F%2Fwww.kenwalger.com%2Fblog%2Fwp-json%2Fwp%2Fv2%2Fposts%2F575&#038;t=Performing%20joins%20in%20MongoDB%20with%20%24lookup&#038;s=100&#038;p&#091;url&#093;=https%3A%2F%2Fwww.kenwalger.com%2Fblog%2Fwp-json%2Fwp%2Fv2%2Fposts%2F575&#038;p&#091;images&#093;&#091;0&#093;=https%3A%2F%2Fi0.wp.com%2Fwww.kenwalger.com%2Fblog%2Fwp-content%2Fuploads%2F2017%2F08%2Ffeature-2-e1504105105445.png%3Ffit%3D125%252C125%26ssl%3D1&#038;p&#091;title&#093;=Performing%20joins%20in%20MongoDB%20with%20%24lookup\" style=\"font-size: 0px;width:48px;height:48px;margin:0;margin-bottom:5px;margin-right:5px\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" alt=\"Facebook\" title=\"Share on Facebook\" class=\"synved-share-image synved-social-image synved-social-image-share\" width=\"48\" height=\"48\" style=\"display: inline;width:48px;height:48px;margin: 0;padding: 0;border: none;box-shadow: none\" src=\"https:\/\/i0.wp.com\/www.kenwalger.com\/blog\/wp-content\/plugins\/social-media-feather\/synved-social\/image\/social\/regular\/96x96\/facebook.png?resize=48%2C48&#038;ssl=1\" \/><\/a><a class=\"synved-social-button synved-social-button-share synved-social-size-48 synved-social-resolution-single synved-social-provider-twitter nolightbox\" data-provider=\"twitter\" target=\"_blank\" rel=\"nofollow\" title=\"Share on Twitter\" href=\"https:\/\/twitter.com\/intent\/tweet?url=https%3A%2F%2Fwww.kenwalger.com%2Fblog%2Fwp-json%2Fwp%2Fv2%2Fposts%2F575&#038;text=Hey%20check%20this%20out\" style=\"font-size: 0px;width:48px;height:48px;margin:0;margin-bottom:5px;margin-right:5px\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" alt=\"twitter\" title=\"Share on Twitter\" class=\"synved-share-image synved-social-image synved-social-image-share\" width=\"48\" height=\"48\" style=\"display: inline;width:48px;height:48px;margin: 0;padding: 0;border: none;box-shadow: none\" src=\"https:\/\/i0.wp.com\/www.kenwalger.com\/blog\/wp-content\/plugins\/social-media-feather\/synved-social\/image\/social\/regular\/96x96\/twitter.png?resize=48%2C48&#038;ssl=1\" \/><\/a><a class=\"synved-social-button synved-social-button-share synved-social-size-48 synved-social-resolution-single synved-social-provider-reddit nolightbox\" data-provider=\"reddit\" target=\"_blank\" rel=\"nofollow\" title=\"Share on Reddit\" href=\"https:\/\/www.reddit.com\/submit?url=https%3A%2F%2Fwww.kenwalger.com%2Fblog%2Fwp-json%2Fwp%2Fv2%2Fposts%2F575&#038;title=Performing%20joins%20in%20MongoDB%20with%20%24lookup\" style=\"font-size: 0px;width:48px;height:48px;margin:0;margin-bottom:5px;margin-right:5px\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" alt=\"reddit\" title=\"Share on Reddit\" class=\"synved-share-image synved-social-image synved-social-image-share\" width=\"48\" height=\"48\" style=\"display: inline;width:48px;height:48px;margin: 0;padding: 0;border: none;box-shadow: none\" src=\"https:\/\/i0.wp.com\/www.kenwalger.com\/blog\/wp-content\/plugins\/social-media-feather\/synved-social\/image\/social\/regular\/96x96\/reddit.png?resize=48%2C48&#038;ssl=1\" \/><\/a><a class=\"synved-social-button synved-social-button-share synved-social-size-48 synved-social-resolution-single synved-social-provider-linkedin nolightbox\" data-provider=\"linkedin\" target=\"_blank\" rel=\"nofollow\" title=\"Share on Linkedin\" href=\"https:\/\/www.linkedin.com\/shareArticle?mini=true&#038;url=https%3A%2F%2Fwww.kenwalger.com%2Fblog%2Fwp-json%2Fwp%2Fv2%2Fposts%2F575&#038;title=Performing%20joins%20in%20MongoDB%20with%20%24lookup\" style=\"font-size: 0px;width:48px;height:48px;margin:0;margin-bottom:5px;margin-right:5px\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" alt=\"linkedin\" title=\"Share on Linkedin\" class=\"synved-share-image synved-social-image synved-social-image-share\" width=\"48\" height=\"48\" style=\"display: inline;width:48px;height:48px;margin: 0;padding: 0;border: none;box-shadow: none\" src=\"https:\/\/i0.wp.com\/www.kenwalger.com\/blog\/wp-content\/plugins\/social-media-feather\/synved-social\/image\/social\/regular\/96x96\/linkedin.png?resize=48%2C48&#038;ssl=1\" \/><\/a><a class=\"synved-social-button synved-social-button-share synved-social-size-48 synved-social-resolution-single synved-social-provider-mail nolightbox\" data-provider=\"mail\" rel=\"nofollow\" title=\"Share by email\" href=\"mailto:?subject=Performing%20joins%20in%20MongoDB%20with%20%24lookup&#038;body=Hey%20check%20this%20out:%20https%3A%2F%2Fwww.kenwalger.com%2Fblog%2Fwp-json%2Fwp%2Fv2%2Fposts%2F575\" style=\"font-size: 0px;width:48px;height:48px;margin:0;margin-bottom:5px\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" alt=\"mail\" title=\"Share by email\" class=\"synved-share-image synved-social-image synved-social-image-share\" width=\"48\" height=\"48\" style=\"display: inline;width:48px;height:48px;margin: 0;padding: 0;border: none;box-shadow: none\" src=\"https:\/\/i0.wp.com\/www.kenwalger.com\/blog\/wp-content\/plugins\/social-media-feather\/synved-social\/image\/social\/regular\/96x96\/mail.png?resize=48%2C48&#038;ssl=1\" \/><\/a>","protected":false},"excerpt":{"rendered":"<p>Eventually, it seems, looking up data in multiple MongoDB collections at the same time becomes necessary. Okay,\u00a0necessary, might be strongly phrased. &#8220;Very helpful&#8221; may be better. Until version 3.2 of MongoDB doing joins wasn&#8217;t possible, but with that version came the $lookup aggregation stage operator.\u00a0The introduction of $lookup allows for left outer joins to be &hellip; <a href=\"https:\/\/www.kenwalger.com\/blog\/nosql\/mongodb\/performing-joins-mongodb-lookup\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Performing joins in MongoDB with $lookup&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":578,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"pmpro_default_level":"","_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[4],"tags":[1023,1022,1024],"yst_prominent_words":[891,873,898,897,893,894,298,882,878,879,884,87,880,885,889,890,883,875,876,892],"class_list":["post-575","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-mongodb","tag-lookup","tag-joins","tag-v3-2","pmpro-has-access"],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/www.kenwalger.com\/blog\/wp-content\/uploads\/2017\/08\/feature-2-e1504105105445.png?fit=125%2C125&ssl=1","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p8lx70-9h","jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/www.kenwalger.com\/blog\/wp-json\/wp\/v2\/posts\/575","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.kenwalger.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.kenwalger.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.kenwalger.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.kenwalger.com\/blog\/wp-json\/wp\/v2\/comments?post=575"}],"version-history":[{"count":3,"href":"https:\/\/www.kenwalger.com\/blog\/wp-json\/wp\/v2\/posts\/575\/revisions"}],"predecessor-version":[{"id":586,"href":"https:\/\/www.kenwalger.com\/blog\/wp-json\/wp\/v2\/posts\/575\/revisions\/586"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.kenwalger.com\/blog\/wp-json\/wp\/v2\/media\/578"}],"wp:attachment":[{"href":"https:\/\/www.kenwalger.com\/blog\/wp-json\/wp\/v2\/media?parent=575"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.kenwalger.com\/blog\/wp-json\/wp\/v2\/categories?post=575"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.kenwalger.com\/blog\/wp-json\/wp\/v2\/tags?post=575"},{"taxonomy":"yst_prominent_words","embeddable":true,"href":"https:\/\/www.kenwalger.com\/blog\/wp-json\/wp\/v2\/yst_prominent_words?post=575"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}