{"id":5470,"date":"2018-07-11T23:07:33","date_gmt":"2018-07-12T06:07:33","guid":{"rendered":"http:\/\/www.couchbase.com\/blog\/?p=5470"},"modified":"2025-06-13T20:20:03","modified_gmt":"2025-06-14T03:20:03","slug":"joining-json-comparing-couchbase-n1ql-mongodb","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/joining-json-comparing-couchbase-n1ql-mongodb\/","title":{"rendered":"How to Join JSON: Couchbase N1QL vs. MongoDB Query"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-5483\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2018\/07\/Screen-Shot-2018-07-13-at-1.24.12-PM-300x157.png\" alt=\"\" width=\"567\" height=\"297\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/07\/Screen-Shot-2018-07-13-at-1.24.12-PM-300x157.png 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/07\/Screen-Shot-2018-07-13-at-1.24.12-PM-20x10.png 20w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/07\/Screen-Shot-2018-07-13-at-1.24.12-PM.png 655w\" sizes=\"auto, (max-width: 567px) 100vw, 567px\" \/><\/p>\n<p><span style=\"font-weight: 400;\">As NoSQL databases evolved, each added higher level APIs or languages to help programmers to complex things easily. SQL, having done that for relational data, showed the way. In SQL, developers say WHAT needs to be done and the database engine figures out the HOW. \u00a0HOW is the efficient procedure\/algorithm to execute the statement. <\/span><span style=\"font-weight: 400;\">Select, join and project are the basic operations SQL processing. Even in NoSQL systems when you model data without much normalization, you still need to join a collection of objects. \u00a0Customers with orders, orders with inventory, inventory with suppliers, suppliers with credits and so forth. Hence, Couchbase N1QL has supported join operations since its first release. \u00a0Following that, MongoDB, in version 3.2, added $lookup operator to the aggregation framework to perform the join operations. <\/span><\/p>\n<p><span style=\"font-weight: 400;\">Without an expressive and high-performance query feature, application developers have to do it within the application or export the data to a system that does it. \u00a0Both expensive propositions. <\/span><\/p>\n<p><span style=\"font-weight: 400;\">In this article, we will be comparing Couchbase vs. MongoDB and their differing approaches to join JSON documents. Specifically, we do a comparative study of using joins for MongoDB collections versus how we can execute them in Couchbase. <span style=\"color: #0000ff;\">Joins are unsupported in Cassandra CQL and DynamoDB natively.\u00a0 Developers must do the work themselves or use other layers like Spark or Amazon EMR to achieve the same result. So, we won\u2019t cover them in this article<\/span>.<\/span><\/p>\n<p><span style=\"color: #ff0000;\">Joins in Couchbase<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Couchbase introduced INNER and LEFT OUTER joins starting with Couchbase 4.0 (2015). This supported joins of in a child-to-parent relationship. \u00a0Children documents (e.g. Orders) can be joined with parent documents (e.g. customer). In 4.5 (2016), Couchbase introduced <\/span><a href=\"https:\/\/dzone.com\/articles\/join-faster-with-couchbase-index-joins\"><span style=\"font-weight: 400;\">index joins<\/span><\/a><span style=\"font-weight: 400;\"> to query from parent to child joins. \u00a0In both cases, there was an implied attribute-value to document-key equality predicate, specified by the ON KEY clause. \u00a0<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Couchbase 5.5 has ANSI standard SQL extended for JSON. \u00a0It supports INNER JOIN, LEFT OUTER JOIN and limited RIGHT OUTER join.\u00a0 We&#8217;ll be using examples based on Couchbase 5.5.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Couchbase joins documentation: <\/span><a href=\"https:\/\/developer.couchbase.com\/documentation\/server\/5.5\/n1ql\/n1ql-language-reference\/from.html\"><span style=\"font-weight: 400;\">https:\/\/developer.couchbase.com\/documentation\/server\/5.5\/n1ql\/n1ql-language-reference\/from.html<\/span><\/a><\/p>\n<p><span style=\"color: #008000;\">MongoDB Joins and Collections:<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Joins are supported via the $lookup operator within the aggregation framework. <\/span><\/p>\n<p><span style=\"font-weight: 400;\">Following are excerpts from the MongoDB documentation.<\/span><\/p>\n<p><i><span style=\"font-weight: 400;\">New in version 3.2.<\/span><\/i><\/p>\n<p><span style=\"font-weight: 400;\">Performs a left outer join to an unsharded collection in the <\/span><i><span style=\"font-weight: 400;\">same<\/span><\/i><span style=\"font-weight: 400;\"> database to filter in documents from the \u201cjoined\u201d collection for processing. To each input document, the <\/span><a href=\"https:\/\/docs.mongodb.com\/manual\/reference\/operator\/aggregation\/lookup\/#pipe._S_lookup\"><span style=\"font-weight: 400;\">$lookup<\/span><\/a><span style=\"font-weight: 400;\"> stage adds a new array field whose elements are the matching documents from the \u201cjoined\u201d collection. The <\/span><a href=\"https:\/\/docs.mongodb.com\/manual\/reference\/operator\/aggregation\/lookup\/#pipe._S_lookup\"><span style=\"font-weight: 400;\">$lookup<\/span><\/a><span style=\"font-weight: 400;\"> stage passes these reshaped documents to the next stage.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Eliot Horowitz, MongoDB CTO, <\/span><a href=\"https:\/\/www.youtube.com\/watch?v=MPPwn1XmhzQ\"><span style=\"font-weight: 400;\">said<\/span><\/a><span style=\"font-weight: 400;\">: &#8220;MongoDB aggregation is similar to Unix pipeline. The output of one stage goes into another\u2026.[it\u2019s] very procedural. Lets you think about in a very procedural way.&#8221;<\/span><\/p>\n<p><span style=\"font-weight: 400;\">MongoDB $lookup : <\/span><a href=\"https:\/\/docs.mongodb.com\/manual\/reference\/operator\/aggregation\/lookup\/\"><span style=\"font-weight: 400;\">https:\/\/docs.mongodb.com\/manual\/reference\/operator\/aggregation\/lookup\/<\/span><\/a><\/p>\n<hr \/>\n<p style=\"text-align: center;\"><strong>Interested in learning more on JOINs? \u00a0Read the article by Lukas Eder. <a href=\"https:\/\/dzone.com\/articles\/a-probably-incomplete-comprehensive-guide-to-the-many-different-ways-to-join-tables-in-sql\">https:\/\/dzone.com\/articles\/a-probably-incomplete-comprehensive-guide-to-the-many-different-ways-to-join-tables-in-sql<\/a><\/strong><\/p>\n<hr \/>\n<p><span style=\"font-weight: 400; color: #0000ff;\">High Level Comparison between Couchbase N1Ql and MongoDB.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Couchbase N1QL: Supports INNER JOIN, LEFT OUTER JOIN and limited RIGHT OUTER JOIN.\u00a0 The query language, like SQL, is declarative. Developers write, tools generate the query to the N1QL syntax.\u00a0 The engine figures out the plan and executes the query.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">MongoDB: Supports LEFT OUTER JOIN for scalar values only.\u00a0 The design of the joins into the MongoDB query language is done help write the query and process data in a procedural way.\u00a0<\/span><\/p>\n<p><span style=\"color: #0000ff;\">Implication<\/span><span style=\"font-weight: 400;\"><span style=\"color: #0000ff;\">:<\/span> <\/span><\/p>\n<ol>\n<li><span style=\"font-weight: 400;\">Left outer join resultset is a superset of inner join resultset. \u00a0It\u2019s possible to add additional predicates to eliminate the non-matching (null-projected or missing subservient side of the join) documents after the left outer join is performed. \u00a0That\u2019s like going from San Francisco to Chicago via London. You can do it, but it&#8217;s expensive. For the query execution, it takes time, memory, cpu resources affecting the overall performance of the system.<\/span><\/li>\n<li>N1QL support for joins is declarative.\u00a0 MongoDB language is somewhat procedural.\u00a0 You&#8217;ve to separate the predicates, think about the join order between collections, think about when to\u00a0group, sort, etc.\u00a0 Writing queries with MongoDB aggregation is like writing query plans, step by step.<\/li>\n<\/ol>\n<p><span style=\"font-weight: 400; color: #0000ff;\"><strong>Examples:<\/strong> <\/span><\/p>\n<p><span style=\"font-weight: 400;\">We use the simple travel-sample model and data. \u00a0Here are the details of the model data. <\/span><a href=\"https:\/\/developer.couchbase.com\/documentation\/server\/4.5\/travel-app\/travel-app-data-model.html\"><span style=\"font-weight: 400;\">https:\/\/developer.couchbase.com\/documentation\/server\/4.5\/travel-app\/travel-app-data-model.html<\/span><\/a><\/p>\n<p><span style=\"font-weight: 400;\">We simply exported the data from Couchbase and imported it to a mongo database called travel-sample. \u00a0In MongoDB, the 5 different types of the document (landmark, route, airline, airport, hotel) are stored in 5 collections with respective names.<\/span><\/p>\n<p><span style=\"color: #0000ff;\"><span style=\"font-weight: 400;\">Example 1<\/span><span style=\"font-weight: 400;\">: LEFT OUTER JOIN with ON clause on scalar values.<\/span><\/span><\/p>\n<p><span style=\"color: #ff0000;\">Couchbase N1QL<\/span><\/p>\n<pre class=\"theme:github font:courier-new wrap:true whitespace-before:1 whitespace-after:1 lang:mysql decode:true\" title=\"Query in Couchbase N1QL\">SELECT count(*)\r\nFROM `travel-sample` route\r\n         LEFT OUTER JOIN `travel-sample` airline\r\n         ON (route.airlineid = META(airline).id)\r\nWHERE route.type = 'route';\r\n<\/pre>\n<p><span style=\"color: #008000;\">MongoDB Query<\/span><\/p>\n<pre class=\"theme:github font:courier-new wrap:true whitespace-before:1 whitespace-after:1 lang:js decode:true\" title=\"Converted query in MongoDB\">db.route.aggregate([\r\n   {\r\n     $lookup:\r\n       {\r\n         from:\"airline\",\r\n         localField: \"airlineid\",\r\n         foreignField: \"_id\",\r\n         as: \"airline_docs\"\r\n       }\r\n   },\r\n   { $group: { _id: null, myCount: { $sum: 1 } } },\r\n   { $project: { _id: 0 } }\r\n ]);\r\n<\/pre>\n<p><span style=\"color: #0000ff;\">Observations:<\/span><\/p>\n<p>This is a fairly simple left outer join query joining two collections and then simply counting the total number of documents produced.\u00a0 Notice, unlike N1QL (and SQL), in MongoDB, you&#8217;d still have to group the resultset to get the count, even if you have a single group.<\/p>\n<p><span style=\"font-weight: 400; color: #0000ff;\">Example 2: List the airports and landmarks in the same city, ordered by the airports.<\/span><\/p>\n<p><span style=\"color: #ff0000;\">Couchbase N1QL:<\/span><\/p>\n<pre class=\"theme:github font:courier-new wrap:true whitespace-before:1 whitespace-after:1 lang:mysql decode:true\" title=\"Query in Couchbase N1QL\">SELECT landmark.name AS Landmark_Name,\r\n       MIN(airport.airportname) AS Airport_Name,\r\n       MIN(airport.tz) AS Landmark_Time\r\nFROM `travel-sample` airport INNER JOIN `travel-sample` landmark\r\n      ON airport.city = landmark.city\r\nWHERE landmark.country = \"United States\"\r\n      AND airport.type = \"airport\"\r\n      AND landmark.type = \"landmark\"\r\nGROUP BY landmark.name\r\nORDER BY Airport_Name\r\n<\/pre>\n<p><span style=\"color: #008000;\">MongoDB:<\/span><\/p>\n<pre class=\"theme:github font:courier-new wrap:true whitespace-before:1 whitespace-after:1 lang:js decode:true\" title=\"Converted query in MongoDB\">db.airport.aggregate([\r\n   {\r\n     $lookup:\r\n       {\r\n         from:\"landmark\",\r\n         localField: \"city\",\r\n         foreignField: \"city\",\r\n         as: \"aplm_docs\"\r\n       }\r\n   },\r\n   {\r\n      $match: {\"airline_docs\": {$ne: []}}\r\n   },\r\n   {  $unwind: { path: \"$aplm_docs\", preserveNullAndEmptyArrays: true }},\r\n   {  $group: {\r\n      _id: \"$aplm_docs.name\",\r\n      Airport_Name: { $min: \"$airportname\" } ,\r\n      Landmark_Time: { $min: \"$tz\"}\r\n     }\r\n   },\r\n   { $sort : { Airport_Name: 1 } },\r\n   { $project: { _id: 1, Airport_Name:1, Landmark_Time:1  } }\r\n ]);\r\n<\/pre>\n<p><span style=\"color: #0000ff;\">Observations:<\/span><\/p>\n<ol>\n<li>This query uses INNER JOIN which MongoDB does not have.\u00a0 So, in MongoDB, you first do the lookup join to get the LEFT OUTER JOIN, and then eliminate non matching, but projected documents (because of the left outer) using the match stage (code:\u00a0$match: {&#8220;airline_docs&#8221;: {$ne: []}}).<\/li>\n<li>Then, you&#8217;ve to remember the matched documents are in an array data structure, unwind them before you group them by the landmark.name.\u00a0 \u00a0Then do the sort and final projection.<\/li>\n<\/ol>\n<p>As expected, the MongoDB join query is procedural and you&#8217;ve to understand the execution plan and write code for each stage.<\/p>\n<p><span style=\"color: #0000ff;\">Example 3: Starting from San Francisco, find all the destination airports (those have routes from SFO).<\/span><\/p>\n<p><span style=\"color: #ff0000;\">Couchbase N1QL<\/span><\/p>\n<pre class=\"theme:github font:courier-new wrap:true whitespace-before:1 whitespace-after:1 lang:mysql decode:true\" title=\"N1QL Query\">SELECT DISTINCT route.destinationairport\r\nFROM `travel-sample` airport JOIN `travel-sample` route\r\n     ON (airport.faa = route.sourceairport AND route.type = \"route\")\r\nWHERE airport.type = \"airport\"\r\n  AND airport.city = \"San Francisco\"\r\n  AND airport.country = \"United States\"\r\nORDER BY route.destinationairport\r\n<\/pre>\n<p><span style=\"color: #008000;\">MongoDB:<\/span><\/p>\n<pre class=\"theme:github font:courier-new wrap:true whitespace-before:1 whitespace-after:1 lang:js decode:true\" title=\"Converted to MongoDB query.\">db.airport.aggregate([\r\n   {\r\n      $match: {\r\n          $and: [\r\n            {\"type\": \"airport\"},\r\n            { city: \"San Francisco\"},\r\n            { \"country\": \"United States\"}\r\n          ]\r\n      }\r\n   },\r\n   {\r\n     $lookup:\r\n       {\r\n         from:\"route\",\r\n         let: { rfaa : \"$faa\"},\r\n         pipeline: [\r\n           { $match:\r\n               { $expr:\r\n                 { $and:\r\n                   [\r\n                     { $eq: [\"$sourceairport\", \"$$rfaa\"]} ,\r\n                     { $eq: [\"$type\", \"route\"] }\r\n                   ]\r\n\r\n                }\r\n              }\r\n           }\r\n         ],\r\n         as: \"airline_docs\"\r\n       }\r\n   },\r\n   { $match: {\"airline_docs\": {$ne: []}} },\r\n   {  $unwind: { path: \"$airline_docs\", preserveNullAndEmptyArrays: true }},\r\n   { $project: { _id:0,  \"airline_docs.destinationairport\" : 1 }},\r\n   { $group: {\r\n      _id : \"$airline_docs.destinationairport\"\r\n     }\r\n   },\r\n   { $sort: { _id : 1 }},\r\n ]);\r\n<\/pre>\n<p><span style=\"color: #0000ff;\">Observations:<\/span><\/p>\n<ol>\n<li>The join clause for this query is a bit more complex, with two predicates\u00a0(airport.faa = (route.sourceairport AND route.type = &#8220;route&#8221;).\u00a0 This requires a cumbersome pipeline syntax on the MongoDB query.<\/li>\n<li>And because you need to differentiate between the two collections, you need another let stage to create the local variables for airport attributes.<\/li>\n<li>Like before, it requires an additional match clause to eliminate non-matching (empty) airline docs, followed by grouping and sorting.<\/li>\n<li>As you can see visually, the MongoDB query is getting larger and larger to do the same job as Couchbase N1QL.<\/li>\n<\/ol>\n<p><span style=\"color: #0000ff;\">Example 4: Find all the hotels and landmarks in Yosemite.\u00a0 Hotels should have <\/span>altleast<span style=\"color: #0000ff;\"> 5 likes.<\/span><\/p>\n<p><span style=\"color: #ff0000;\">Couchbase N1QL<\/span><\/p>\n<pre class=\"theme:github font:courier-new wrap:true whitespace-before:1 whitespace-after:1 lang:mysql decode:true\" title=\"Couchbase N1QL Query\">SELECT hotel.name hotel_name, landmark.name landmark_name, landmark.activity\r\nFROM `travel-sample` hotel INNER JOIN `travel-sample` landmark\r\n    ON (hotel.city = landmark.city \r\n        AND hotel.country = landmark.country \r\n        AND landmark.type = \"landmark\")\r\nWHERE hotel.type = \"hotel\" \r\n  AND hotel.title like \"Yosemite%\" \r\n  AND array_length(hotel.public_likes) &gt; 5;\r\n<\/pre>\n<p><span style=\"color: #008000;\">MongoDB:<\/span><\/p>\n<pre class=\"theme:github font:courier-new wrap:true whitespace-before:1 whitespace-after:1 lang:js decode:true\" title=\"Converted MongoDB Query.\">db.hotel.aggregate([\r\n   { $match:  { title: { $regex: \/^Yosemite\/ } }, },\r\n   {\r\n     $lookup:\r\n       {\r\n         from:\"landmark\",\r\n         let: { hcity : \"$city\", hcountry : \"$country\"},\r\n         pipeline: [\r\n           { $match:\r\n               { $expr:\r\n                 { $and:\r\n                   [\r\n                     { $eq: [\"$city\", \"$$hcity\"]} ,\r\n                     { $eq: [\"$country\", \"$$hcountry\"] }\r\n                   ]\r\n                }\r\n              }\r\n           }\r\n         ],\r\n         as: \"hotel_lm_docs\"\r\n       }\r\n   },\r\n   { $match : {\"hotel_lm_docs\": { $ne: [] }}},\r\n   { $project: {_id:0, hname: \"$name\", public_likes: 1, hotel_lm_docs:1}},\r\n   { $unwind: { path: \"$hotel_lm_docs\", preserveNullAndEmptyArrays: true }},\r\n   { $project: { _id: 1, hname : 1 , \"hotel_lm_docs.name\" : 1, \"hotel_lm_docs.name\" : 1, \"hotel_lm_docs.activity\" : 1, mt5 : {$gt: [ {$size: \"$public_likes\"}, 5]}}},\r\n   { $match: { mt5 : true } },\r\n   { $project: {_$id:0}}\r\n ]);\r\n<\/pre>\n<p><span style=\"color: #0000ff;\">Observation:<\/span><\/p>\n<ol>\n<li>Translating the LIKE predicate into a regular expression was straightforward, but determining if there were atleast five public_likes was not.\u00a0 Needed additional projection and matching phase to calculate the size of the public_likes at the end.<\/li>\n<li>When you have many attributes to match, manipulate and project, you&#8217;d have to rename them properly at appropriate stage otherwise, the query can&#8217;t reference it.\u00a0 For example, hotel.name had to be renamed to hname\u00a0before the unwind.\u00a0 Maybe there&#8217;s a better way to write this stage!<\/li>\n<li>N1QL expressed the query in 370 characters. MongoDB required 956 characters.\u00a0 All this for a two table join.\u00a0 As the complexity increases, the ratio increases as well since the MongoDB query is written in a procedural way.<\/li>\n<\/ol>\n<p><span style=\"color: #0000ff;\">Example 5: Find all the hotels and landmarks in Yosemite.\u00a0 Hotels should have at least 5 likes.<\/span><\/p>\n<p>This is just like Example 4, but do it faster!<\/p>\n<p><span style=\"color: #ff0000;\">Couchbase N1QL<\/span><\/p>\n<pre class=\"theme:github font:courier-new wrap:true whitespace-before:1 whitespace-after:1 lang:mysql decode:true\" title=\"Couchbase N1QL Query\">SELECT hotel.name hotel_name, landmark.name landmark_name, landmark.activity\r\nFROM `travel-sample` hotel INNER JOIN `travel-sample` landmark USE HASH(build)\r\n    ON (hotel.city = landmark.city \r\n        AND hotel.country = landmark.country \r\n        AND landmark.type = \"landmark\")\r\nWHERE hotel.type = \"hotel\"\r\n  AND hotel.title like \"Yosemite%\" \r\n  AND array_length(hotel.public_likes) &gt; 5;\r\n<\/pre>\n<p><span style=\"color: #0000ff;\">Observation:<\/span><\/p>\n<p>The default join method in Couchbase N1QL is nested loop join.\u00a0 This works fine when you have a smaller number of documents involved on each side of the join.\u00a0 When you have a\u00a0larger data set, typically in reporting queries, nested loop join slows down.\u00a0 Couchbase N1QL has hash joins and this speeds up joins significantly.\u00a0 When each side of join has thousands of documents to millions of documents, the speed increase can be 2x to 20x or more.\u00a0See the <a href=\"https:\/\/www.couchbase.com\/blog\/ansi-join-support-n1ql\/\">detailed Couchbase blog<\/a> on ANSI Joins for more information.<\/p>\n<p>From the documentation and explain plan, it&#8217;s unclear what join method MongoDB uses. Some of the blogs indicate that they&#8217;ve used a nested loop join to implement the $lookup operator.<\/p>\n<p><strong>Summary:<\/strong><\/p>\n<p>&nbsp;<\/p>\n<table>\n<tbody>\n<tr>\n<td><\/td>\n<td><span style=\"color: #ff0000;\"><b>Couchbase N1QL<\/b><\/span><\/td>\n<td><span style=\"color: #008000;\"><b>MongoDB<\/b><\/span><\/td>\n<\/tr>\n<tr>\n<td><b>JOIN approach<\/b><\/td>\n<td><span style=\"font-weight: 400;\">Declarative, like SQL.<\/span><\/p>\n<p>Allows joining between any sized and distributed data set.<\/td>\n<td><span style=\"font-weight: 400;\">Procedural with some declarative aspects (e.g. index selection).<\/span><\/p>\n<p>Can only join a sharded collection in an\u00a0<strong>unsharded collection.\u00a0<\/strong>To join two sharded collections, applications will have to write the join algorithm.<\/td>\n<\/tr>\n<tr>\n<td><b>JOINs supported<\/b><\/td>\n<td><span style=\"font-weight: 400;\">LEFT OUTER JOIN<\/span><\/p>\n<p><span style=\"font-weight: 400;\">INNER JOIN<\/span><\/p>\n<p><span style=\"font-weight: 400;\">RIGHT OUTER JOIN<\/span><\/td>\n<td><span style=\"font-weight: 400;\">$lookup implements the LEFT OUTER JOIN on scalar values.<\/span><\/td>\n<\/tr>\n<tr>\n<td><b>ON-clause support<\/b><\/td>\n<td><span style=\"font-weight: 400;\">Full expressions.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Scalars<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Arrays<\/span><\/td>\n<td><span style=\"font-weight: 400;\">Implicit equality<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Pipeline expression<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Arrays should to be $unwind before the $lookup<\/span><\/td>\n<\/tr>\n<tr>\n<td><b>JOIN implementation<\/b><\/td>\n<td><span style=\"font-weight: 400;\">Block Nested Loop<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Hash join with user defined build and probes.<\/span><\/td>\n<td><span style=\"font-weight: 400;\">Nested Loop<\/span><\/td>\n<\/tr>\n<tr>\n<td><b>ON Clause<\/b><\/td>\n<td><span style=\"font-weight: 400;\">ON clause with any expression.<\/span><\/td>\n<td><span style=\"font-weight: 400;\">$pipeline expression<\/span><\/td>\n<\/tr>\n<tr>\n<td><b>Array expressions in ON clause<\/b><\/td>\n<td><span style=\"font-weight: 400;\">Use ANY, IN expressions.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">Supports UNNEST<\/span><\/td>\n<td><span style=\"font-weight: 400;\">Pipeline with $unwind before $match<\/span><\/td>\n<\/tr>\n<tr>\n<td><b>Explain<\/b><\/td>\n<td><span style=\"font-weight: 400;\">Visual explain and <\/span><\/p>\n<p><span style=\"font-weight: 400;\">JSON explain<\/span><\/td>\n<td><span style=\"font-weight: 400;\">Visual explain and<\/span><\/p>\n<p><span style=\"font-weight: 400;\">JSON explain<\/span><\/td>\n<\/tr>\n<tr>\n<td><b>JOIN order<\/b><\/td>\n<td><span style=\"font-weight: 400;\">Left to right, as specified by the user. The optimizer is rule based.<\/span><\/td>\n<td><span style=\"font-weight: 400;\">As specified in the pipeline.<\/span><\/td>\n<\/tr>\n<tr>\n<td><b>Nested JOINs<\/b><\/td>\n<td><span style=\"font-weight: 400;\">Supported via derived tables.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">FROM clause can have subselects which can have joins or subselects in turn.<\/span><\/td>\n<td><span style=\"font-weight: 400;\">No<\/span><\/td>\n<\/tr>\n<tr>\n<td><b>JOIN predicate processing<\/b><\/td>\n<td><span style=\"font-weight: 400;\">Optimizer processes the join predicates, constant predicates and pushes the predicates to the index automatically.<\/span><\/td>\n<td><span style=\"font-weight: 400;\">Manual design of predicates for each collection, careful ordering of pipeline stages without full help from the optimizer.<\/span><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>How about performance?\u00a0 Good question.\u00a0 That&#8217;s for a future blog!<\/p>\n<p><strong><span style=\"color: #000000;\">And now, a quote:<\/span><\/strong><\/p>\n<p><span style=\"font-weight: 400;\">\u201cA sentence should contain no unnecessary words, a paragraph no unnecessary sentences, for the same reason that a drawing should have no unnecessary lines and a machine no unnecessary parts.\u201d<\/span><\/p>\n<p><span style=\"font-weight: 400;\"> &#8212; William Strunk, Jr.\u00a0 <a href=\"https:\/\/www.amazon.com\/Elements-Style-William-Strunk-Jr\/dp\/194564401X\">Elements of Style<\/a>.<\/span><\/p>\n<p><strong>References:<\/strong><\/p>\n<ol>\n<li>Couchbase Documentation: <a href=\"https:\/\/docs.couchbase.com\">https:\/\/docs.couchbase.com<\/a><\/li>\n<li>MongoDB Documentation:\u00a0<a href=\"https:\/\/docs.mongodb.com\/\">https:\/\/docs.mongodb.com\/<\/a><\/li>\n<li>ANSI Joins in Couchbase N1QL:\u00a0<a href=\"https:\/\/www.couchbase.com\/blog\/ansi-join-support-n1ql\/\">https:\/\/www.couchbase.com\/blog\/ansi-join-support-n1ql\/<\/a><\/li>\n<li>N1QL Tutorial:\u00a0<a href=\"https:\/\/query-tutorial.couchbase.com\/tutorial\/#1\">https:\/\/query-tutorial.couchbase.com\/tutorial\/#1<\/a><\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>As NoSQL databases evolved, each added higher level APIs or languages to help programmers to complex things easily. SQL, having done that for relational data, showed the way. In SQL, developers say WHAT needs to be done and the database [&hellip;]<\/p>\n","protected":false},"author":55,"featured_media":5471,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[1814,1821,1816,1819,9417,1812],"tags":[1309],"ppma_author":[8929],"class_list":["post-5470","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-application-design","category-couchbase-architecture","category-couchbase-server","category-data-modeling","category-performance","category-n1ql-query","tag-mongodb"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v27.3 (Yoast SEO v27.3) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>How to Join JSON: Couchbase N1QL vs. MongoDB Query<\/title>\n<meta name=\"description\" content=\"This article discusses how to join JSON documents while comparing Couchbase vs. MongoDB. Follow along as we review several real-world examples.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.couchbase.com\/blog\/joining-json-comparing-couchbase-n1ql-mongodb\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How to Join JSON: Couchbase N1QL vs. MongoDB Query\" \/>\n<meta property=\"og:description\" content=\"This article discusses how to join JSON documents while comparing Couchbase vs. MongoDB. Follow along as we review several real-world examples.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/joining-json-comparing-couchbase-n1ql-mongodb\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:published_time\" content=\"2018-07-12T06:07:33+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-14T03:20:03+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/07\/Screen-Shot-2018-04-22-at-8.52.36-PM-e1531517244183.png\" \/>\n\t<meta property=\"og:image:width\" content=\"700\" \/>\n\t<meta property=\"og:image:height\" content=\"294\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Keshav Murthy\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@rkeshavmurthy\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Keshav Murthy\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/joining-json-comparing-couchbase-n1ql-mongodb\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/joining-json-comparing-couchbase-n1ql-mongodb\\\/\"},\"author\":{\"name\":\"Keshav Murthy\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#\\\/schema\\\/person\\\/c261644262bf98e146372fe647682636\"},\"headline\":\"How to Join JSON: Couchbase N1QL vs. MongoDB Query\",\"datePublished\":\"2018-07-12T06:07:33+00:00\",\"dateModified\":\"2025-06-14T03:20:03+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/joining-json-comparing-couchbase-n1ql-mongodb\\\/\"},\"wordCount\":1656,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/joining-json-comparing-couchbase-n1ql-mongodb\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/1\\\/2018\\\/07\\\/Screen-Shot-2018-04-22-at-8.52.36-PM-e1531517244183.png\",\"keywords\":[\"mongodb\"],\"articleSection\":[\"Application Design\",\"Couchbase Architecture\",\"Couchbase Server\",\"Data Modeling\",\"High Performance\",\"SQL++ \\\/ N1QL Query\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/joining-json-comparing-couchbase-n1ql-mongodb\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/joining-json-comparing-couchbase-n1ql-mongodb\\\/\",\"url\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/joining-json-comparing-couchbase-n1ql-mongodb\\\/\",\"name\":\"How to Join JSON: Couchbase N1QL vs. MongoDB Query\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/joining-json-comparing-couchbase-n1ql-mongodb\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/joining-json-comparing-couchbase-n1ql-mongodb\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/1\\\/2018\\\/07\\\/Screen-Shot-2018-04-22-at-8.52.36-PM-e1531517244183.png\",\"datePublished\":\"2018-07-12T06:07:33+00:00\",\"dateModified\":\"2025-06-14T03:20:03+00:00\",\"description\":\"This article discusses how to join JSON documents while comparing Couchbase vs. MongoDB. Follow along as we review several real-world examples.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/joining-json-comparing-couchbase-n1ql-mongodb\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/joining-json-comparing-couchbase-n1ql-mongodb\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/joining-json-comparing-couchbase-n1ql-mongodb\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/1\\\/2018\\\/07\\\/Screen-Shot-2018-04-22-at-8.52.36-PM-e1531517244183.png\",\"contentUrl\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/1\\\/2018\\\/07\\\/Screen-Shot-2018-04-22-at-8.52.36-PM-e1531517244183.png\",\"width\":700,\"height\":294},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/joining-json-comparing-couchbase-n1ql-mongodb\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"How to Join JSON: Couchbase N1QL vs. MongoDB Query\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/\",\"name\":\"The Couchbase Blog\",\"description\":\"Couchbase, the NoSQL Database\",\"publisher\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/wp-content\\\/uploads\\\/2023\\\/04\\\/admin-logo.png\",\"contentUrl\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/wp-content\\\/uploads\\\/2023\\\/04\\\/admin-logo.png\",\"width\":218,\"height\":34,\"caption\":\"The Couchbase Blog\"},\"image\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#\\\/schema\\\/person\\\/c261644262bf98e146372fe647682636\",\"name\":\"Keshav Murthy\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/af74df754db27152971d0aed2f323ead5a1f9fe5afd0209af91e12e784451224?s=96&d=mm&r=g4e51d72fc07c662aa791316deafffac4\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/af74df754db27152971d0aed2f323ead5a1f9fe5afd0209af91e12e784451224?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/af74df754db27152971d0aed2f323ead5a1f9fe5afd0209af91e12e784451224?s=96&d=mm&r=g\",\"caption\":\"Keshav Murthy\"},\"description\":\"Keshav Murthy is a Vice President at Couchbase R&amp;D. Previously, he was at MapR, IBM, Informix, Sybase, with more than 20 years of experience in database design &amp; development. He lead the SQL and NoSQL R&amp;D team at IBM Informix. He has received two President's Club awards at Couchbase, two Outstanding Technical Achievement Awards at IBM. Keshav has a bachelor's degree in Computer Science and Engineering from the University of Mysore, India, and has received twenty four US patents.\",\"sameAs\":[\"https:\\\/\\\/blog.planetnosql.com\\\/\",\"https:\\\/\\\/x.com\\\/rkeshavmurthy\"],\"url\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/author\\\/keshav-murthy\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"How to Join JSON: Couchbase N1QL vs. MongoDB Query","description":"This article discusses how to join JSON documents while comparing Couchbase vs. MongoDB. Follow along as we review several real-world examples.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.couchbase.com\/blog\/joining-json-comparing-couchbase-n1ql-mongodb\/","og_locale":"en_US","og_type":"article","og_title":"How to Join JSON: Couchbase N1QL vs. MongoDB Query","og_description":"This article discusses how to join JSON documents while comparing Couchbase vs. MongoDB. Follow along as we review several real-world examples.","og_url":"https:\/\/www.couchbase.com\/blog\/joining-json-comparing-couchbase-n1ql-mongodb\/","og_site_name":"The Couchbase Blog","article_published_time":"2018-07-12T06:07:33+00:00","article_modified_time":"2025-06-14T03:20:03+00:00","og_image":[{"width":700,"height":294,"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/07\/Screen-Shot-2018-04-22-at-8.52.36-PM-e1531517244183.png","type":"image\/png"}],"author":"Keshav Murthy","twitter_card":"summary_large_image","twitter_creator":"@rkeshavmurthy","twitter_misc":{"Written by":"Keshav Murthy","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/joining-json-comparing-couchbase-n1ql-mongodb\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/joining-json-comparing-couchbase-n1ql-mongodb\/"},"author":{"name":"Keshav Murthy","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/c261644262bf98e146372fe647682636"},"headline":"How to Join JSON: Couchbase N1QL vs. MongoDB Query","datePublished":"2018-07-12T06:07:33+00:00","dateModified":"2025-06-14T03:20:03+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/joining-json-comparing-couchbase-n1ql-mongodb\/"},"wordCount":1656,"commentCount":0,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/joining-json-comparing-couchbase-n1ql-mongodb\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/07\/Screen-Shot-2018-04-22-at-8.52.36-PM-e1531517244183.png","keywords":["mongodb"],"articleSection":["Application Design","Couchbase Architecture","Couchbase Server","Data Modeling","High Performance","SQL++ \/ N1QL Query"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/joining-json-comparing-couchbase-n1ql-mongodb\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/joining-json-comparing-couchbase-n1ql-mongodb\/","url":"https:\/\/www.couchbase.com\/blog\/joining-json-comparing-couchbase-n1ql-mongodb\/","name":"How to Join JSON: Couchbase N1QL vs. MongoDB Query","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/joining-json-comparing-couchbase-n1ql-mongodb\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/joining-json-comparing-couchbase-n1ql-mongodb\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/07\/Screen-Shot-2018-04-22-at-8.52.36-PM-e1531517244183.png","datePublished":"2018-07-12T06:07:33+00:00","dateModified":"2025-06-14T03:20:03+00:00","description":"This article discusses how to join JSON documents while comparing Couchbase vs. MongoDB. Follow along as we review several real-world examples.","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/joining-json-comparing-couchbase-n1ql-mongodb\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/joining-json-comparing-couchbase-n1ql-mongodb\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/joining-json-comparing-couchbase-n1ql-mongodb\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/07\/Screen-Shot-2018-04-22-at-8.52.36-PM-e1531517244183.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/07\/Screen-Shot-2018-04-22-at-8.52.36-PM-e1531517244183.png","width":700,"height":294},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/joining-json-comparing-couchbase-n1ql-mongodb\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"How to Join JSON: Couchbase N1QL vs. MongoDB Query"}]},{"@type":"WebSite","@id":"https:\/\/www.couchbase.com\/blog\/#website","url":"https:\/\/www.couchbase.com\/blog\/","name":"The Couchbase Blog","description":"Couchbase, the NoSQL Database","publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"The Couchbase Blog","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","width":218,"height":34,"caption":"The Couchbase Blog"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/c261644262bf98e146372fe647682636","name":"Keshav Murthy","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/af74df754db27152971d0aed2f323ead5a1f9fe5afd0209af91e12e784451224?s=96&d=mm&r=g4e51d72fc07c662aa791316deafffac4","url":"https:\/\/secure.gravatar.com\/avatar\/af74df754db27152971d0aed2f323ead5a1f9fe5afd0209af91e12e784451224?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/af74df754db27152971d0aed2f323ead5a1f9fe5afd0209af91e12e784451224?s=96&d=mm&r=g","caption":"Keshav Murthy"},"description":"Keshav Murthy is a Vice President at Couchbase R&amp;D. Previously, he was at MapR, IBM, Informix, Sybase, with more than 20 years of experience in database design &amp; development. He lead the SQL and NoSQL R&amp;D team at IBM Informix. He has received two President's Club awards at Couchbase, two Outstanding Technical Achievement Awards at IBM. Keshav has a bachelor's degree in Computer Science and Engineering from the University of Mysore, India, and has received twenty four US patents.","sameAs":["https:\/\/blog.planetnosql.com\/","https:\/\/x.com\/rkeshavmurthy"],"url":"https:\/\/www.couchbase.com\/blog\/author\/keshav-murthy\/"}]}},"acf":[],"authors":[{"term_id":8929,"user_id":55,"is_guest":0,"slug":"keshav-murthy","display_name":"Keshav Murthy","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/af74df754db27152971d0aed2f323ead5a1f9fe5afd0209af91e12e784451224?s=96&d=mm&r=g","0":null,"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/5470","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/users\/55"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/comments?post=5470"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/5470\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media\/5471"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media?parent=5470"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/categories?post=5470"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/tags?post=5470"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/ppma_author?post=5470"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}