{"id":4498,"date":"2018-02-12T10:00:54","date_gmt":"2018-02-12T18:00:54","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=4498"},"modified":"2025-06-13T20:09:22","modified_gmt":"2025-06-14T03:09:22","slug":"join-queries-couchbase-mobile","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/","title":{"rendered":"Introducing JOIN Queries in Couchbase Mobile"},"content":{"rendered":"<p>Couchbase Lite 2.0 supports the ability to perform JOINS across your JSON documents . This is part of the new Query interface based on <a href=\"https:\/\/www.couchbase.com\/products\/n1ql\/\">N1QL<\/a>, Couchbase\u2019s declarative query language that extends SQL for JSON. If you are familiar with SQL, you will feel right at home with the semantics of the new API.<\/p>\n<p>JOINS enable you to combine the contents of multiple documents. In this post, we will provide examples to illustrate the types of JOINS possible with Couchbase Lite. For each of the queries, we will provide the equivalent SQL query. This blog assumes you are familiar with the fundamentals of the new query API, so if you haven\u2019t done so already, be sure to review the <a href=\"https:\/\/www.couchbase.com\/blog\/sql-for-json-query-interface-couchbase-mobile\/\">earlier post<\/a> first. If you are interested, links to blogs discussing other features of the Query interface are provided at the end of this post.<\/p>\n<p>You can download the \u00a0latest Couchbase Mobile 2.0 Pre-Release builds from our <a href=\"https:\/\/developer.couchbase.com\/documentation\/mobile\/2.0\/whatsnew.html\">downloads<\/a> page.<\/p>\n<p><!--more--><\/p>\n<h3 id=\"background\">Background<\/h3>\n<p>If you were using 1.x versions of Couchbase Mobile, you are probably familiar with <a href=\"https:\/\/developer.couchbase.com\/documentation\/mobile\/1.4\/training\/develop\/using-the-database\/index.html#query-documents\">Map-Views<\/a> for creating indexes and queries. In 2.0, you no longer have to create views and map functions! Instead, a simple interface allows you to create indexes and you can use a Query Builder interface to construct your queries. The new query interface is simpler to use and much more powerful in comparison. We will discover some of it\u2019s features in this post.<\/p>\n<h3 id=\"sampleproject\">Sample Project<\/h3>\n<p>While the examples discussed here use Swift for iOS, the same query interface is supported on the Android and Windows platforms as well.<\/p>\n<p><strong>So with some minor tweaks, you should be able to reuse the query examples in this post when working with other platforms.<\/strong><\/p>\n<p>Follow instructions below if you are interested in a sample Swift Project<\/p>\n<ul>\n<li>Clone the iOS Swift Playground from GitHub<\/li>\n<\/ul>\n<pre><code>$ git clone https:\/\/github.com\/couchbaselabs\/couchbase-lite-ios-api-playground<\/code><\/pre>\n<ul>\n<li>Follow the installation instructions in the corresponding <a href=\"https:\/\/github.com\/couchbaselabs\/couchbase-lite-ios-api-playground\/blob\/master\/README.md\">README<\/a> file to build and execute the playground.<\/li>\n<\/ul>\n<h3 id=\"sampledatamodel\">Sample Data Model<\/h3>\n<p>We shall use a sample database located <a href=\"https:\/\/github.com\/couchbaselabs\/couchbase-lite-ios-api-playground\/tree\/master\/joindb.cblite2\">here<\/a>. You can embed this pre-built database into your mobile application and start using it for your queries.<\/p>\n<p>The sample data set is a bit contrived but the goal here is to demonstrate some typical use cases of <code>join<\/code> queries.<\/p>\n<ul>\n<li>\u201cemployee\u201d type document<\/li>\n<\/ul>\n<pre class=\"lang:js decode:true \">{\r\n \"type\": \"employee\",\r\n \"firstname\": \"John\",\r\n \"lastname\": \"Smith\",\r\n \"department\": \"1000\",\r\n \"location\": \"101\"\r\n}<\/pre>\n<ul>\n<li>\u201cdepartment\u201d type document<\/li>\n<\/ul>\n<pre class=\"lang:js decode:true\">{\r\n \"type\": \"department\",\r\n \"name\": \"Product Management\",\r\n \"code\": \"2000\",\r\n \"head\": {\r\n \"firstname\": \"Patricia\",\r\n \"lastname\": \"Shoji\"\r\n },\r\n \"location\":[\"101\",\"102\"]\r\n}\r\n<code><\/code><\/pre>\n<ul>\n<li>\u201clocation\u201d type document<\/li>\n<\/ul>\n<pre class=\"lang:js decode:true \">{\r\n \"type\": \"location\",\r\n \"name\": \"HQ\",\r\n \"address\": \"1123 6th St. Melbourne, FL 32904 \",\r\n \"code\": \"101\"\r\n}<\/pre>\n<p>** Refer to the model above for each of the query examples below. **<\/p>\n<h3 id=\"thedatabasehandle\">The Database Handle<\/h3>\n<p>In the queries below, we will use the <a href=\"https:\/\/developer.couchbase.com\/documentation\/mobile\/2.0\/guides\/couchbase-lite\/native-api\/database\/index.html\"><code>Database<\/code><\/a> API to open\/create CouchbaseLite Database.<\/p>\n<pre class=\"wrap:true lang:swift decode:true \"> let options = DatabaseConfiguration()\r\n let db = try Database(name: kDBName, config: options)<\/pre>\n<h3 id=\"indexes\">Indexes<\/h3>\n<p>To speed up read queries, you can create Indexes on properties that you will query on. The performance improvement would be significant on large datasets. Of course, be aware that there will be an increase in storage needs in order to store the indexes and performance of writes can also be impacted. So be cautious of creating too many indexes.<\/p>\n<p>The following example creates a <code>ValueIndex<\/code> on the <code>type<\/code> property of a Document<\/p>\n<pre class=\"wrap:true lang:swift decode:true \"> try db.createIndex(IndexBuilder.valueIndex(items: ValueIndexItem.property(\"type\")),withName: \"typeIndex\")<\/pre>\n<h3 id=\"joinorinnerjoin\">JOIN or Inner JOIN<\/h3>\n<p>You can use a simple JOIN or Inner JOIN Query to fetch properties from participating documents if and only if both documents meet the conditions specified in the <code>ON<\/code> clause.<\/p>\n<p>For example, considering the data model we presented earlier, let\u2019s suppose that you wanted to fetch the <code>firstName<\/code>, <code>lastName<\/code> of an \u201cemployee\u201d and the corresponding <code>name<\/code> of the \u201cdepartment\u201d that the employee belonged to. In this case, <code>firstname<\/code> and <code>lastname<\/code> properties are fetched from document of <code>type<\/code> \u201cemployee\u201d and the department <code>name<\/code> is fetched from the document of <code>type<\/code> \u201cdepartment\u201d if and only if the <code>department<\/code> property of the \u201cemployee\u201d matches the corresponding <code>code<\/code> property in the \u201cdepartment\u201d<\/p>\n<p>This implies that if there are no \u201cdepartment\u201d documents that match the <code>code<\/code> in the \u201cemployee\u201d document then the details of that employee are not included in the output result<\/p>\n<figure><img decoding=\"async\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2018\/01\/inner_join_explain.png\" alt=\"\" \/><\/figure>\n<h4 id=\"request\">Request<\/h4>\n<pre class=\"wrap:true lang:swift decode:true \"> try db.createIndex(IndexBuilder.valueIndex(items: ValueIndexItem.property(\"type\")),withName: \"typeIndex\")\r\n\/\/ set up aliases to represent the data source for &amp;quot;department&amp;quot; type document\r\nlet departmentDS = DataSource.database(db).as(\"departmentDS\")\r\n\r\n\/\/ Property expression for &amp;quot;department&amp;quot; (in employee documents)\r\nlet employeeDeptExpr = Expression.property(\"department\").from(\"employeeDS\")\r\n\r\n\/\/ Property expression for &amp;quot;code&amp;quot; (in department documents)\r\nlet departmentCodeExpr = Expression.property(\"code\").from(\"departmentDS\")\r\n\r\n\/\/ Join clause: Join employee type and department type documents where the\r\n\/\/ \"department\" field of employee documents is equal to the department \"code\";\r\n\/\/ of \"department\" documents\r\nlet joinExpr = employeeDeptExpr.equalTo(departmentCodeExpr)\r\n    .and(Expression.property(\"type\").from(\"employeeDS\").equalTo(Expression.string(\"employee\")))\r\n    .and(Expression.property(\"type\").from(\"departmentDS\").equalTo(Expression.string(\"department\")))\r\n\r\n\r\n\/\/ Construct inner join expression with ON query.\r\nlet join = Join.join(departmentDS).on(joinExpr)\r\n\r\n\/\/ return the \"firstname\", \"lastname\"; and \"department\"; name from the documents that are joined based\r\n\/\/ on the JOIN expression\r\nlet searchQuery = Query.select(\r\n    SelectResult.expression(Expression.property(\"firstname\").from(\"employeeDS\")),\r\n    SelectResult.expression(Expression.property(\"lastname\").from(\"employeeDS\")),\r\n    SelectResult.expression(Expression.property(\"name\").from(\"departmentDS\")))\r\n    .from(employeeDS)\r\n    .join(join)<\/pre>\n<p>&nbsp;<\/p>\n<h4>ANSI SQL<\/h4>\n<p>The equivalent SQL statement for the above query would be<\/p>\n<pre class=\"wrap:true lang:mysql decode:true\">SELECT\r\n  employeeDS.firstname,\r\n  employeeDS.lastname,\r\n  departmentDS.name\r\nFROM\r\n  `travel-sample` employeeDS\r\n  INNER JOIN `travel-sample` departmentDS ON employeeDS.department = departmentDS.code\r\nWHERE\r\n  employeeDS.type = \"employee\"\r\n  AND departmentDS.type = \"department\"<\/pre>\n<p>&nbsp;<\/p>\n<h3 id=\"leftjoinorleftouterjoin\">Left JOIN or Left Outer JOIN<\/h3>\n<p>You can use a left JOIN Query to fetch properties from participating documents if both documents meet the conditions specified in the <code>ON<\/code> clause. However, unlike a regular JOIN, the results will also include unmatched documents to the left-hand-side of the <code>ON<\/code> clause of the JOIN Expression.<\/p>\n<p>For example, considering the data model we presented earlier, let\u2019s suppose that you wanted to fetch the <code>firstName<\/code>, <code>lastName<\/code> of an \u201cemployee\u201d and the corresponding <code>name<\/code> of the \u201cdepartment\u201d that the employee belonged to.<br \/>\nIn addition, let\u2019s suppose that we are also interested in the<code>firstName<\/code> and <code>lastName<\/code> of an \u201cemployee\u201d whose <code>department<\/code> code does <em>not<\/em> correspond to a valid department. This could be the case for instance, if the <code>department<\/code> code for the employee was entered incorrectly.<\/p>\n<p>In this case, <code>firstname<\/code> and <code>lastname<\/code> properties are fetched from document of <code>type<\/code> \u201cemployee\u201d and the department <code>name<\/code> is fetched from the document of <code>type<\/code> \u201cdepartment\u201d if the <code>department<\/code> property of the \u201cemployee\u201d matches the corresponding <code>code<\/code> property in the \u201cdepartment\u201d.<\/p>\n<p>If there is no matching department, then only the <code>firstname<\/code> and <code>lastname<\/code> properties from the \u201cemployee\u201d document are returned.<\/p>\n<figure><img decoding=\"async\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2018\/01\/outer_join_explain.png\" alt=\"\" \/><\/figure>\n<h4 id=\"request\">Request<\/h4>\n<pre class=\"wrap:true lang:swift decode:true\"> \/\/ set up aliases to represent the data source for \"employee\" type document\r\n let employeeDS = DataSource.database(db).as(\"employeeDS\")\r\n\/\/ set up aliases to represent the data source for \"department\" type document\r\nlet departmentDS = DataSource.database(db).as(\"departmentDS\")\r\n\r\n\/\/ Property expression for \"department\" (in employee documents)\r\nlet employeeDeptExpr = Expression.property(\"department\").from(\"employeeDS\")\r\n\r\n\/\/ Property expression for \"code\" (in department documents)\r\nlet departmentCodeExpr = Expression.property(&amp;quot;code&amp;quot;).from(\"departmentDS\")\r\n\r\n\/\/ Join clause: Join department type and employee type documents where the\r\n\/\/ \"department\" field of employee documents is equal to the department \"code\" of\r\n\/\/ \"department\" documents\r\nlet joinExpr = employeeDeptExpr.equalTo(departmentCodeExpr)\r\n    .and(Expression.property(\"type\").from(\"employeeDS\").equalTo(Expression.string(\"employee\")))\r\n    .and(Expression.property(\"type\").from(\"departmentDS\").equalTo(Expression.string(\"department\")))\r\n\r\n\r\n\/\/ join expression with ON query\r\nlet join = Join.leftJoin(departmentDS).on(joinExpr)\r\n\r\n\/\/ return the \"firstname\", \"lastname\" and \"department\" name from the documents that are joined based on the JOIN expression\r\n let searchQuery = Query.select(\r\n     SelectResult.expression(Expression.property(\"firstname\").from(\"employeeDS\")),\r\n     SelectResult.expression(Expression.property(\"lastname\").from(\"employeeDS\")),\r\n     SelectResult.expression(Expression.property(\"name\").from(\"departmentDS\")))\r\n    .from(employeeDS)\r\n    .join(join)<\/pre>\n<h4>ANSI SQL<\/h4>\n<p>The equivalent SQL statement for the above query would be<\/p>\n<pre class=\"wrap:true lang:mysql decode:true\"> SELECT\r\n  employeeDS.firstname,\r\n  employeeDS.lastname,\r\n  departmentDS.name\r\nFROM\r\n  `travel-sample` employeeDS\r\n  LEFT JOIN `travel-sample` departmentDS ON employeeDS.department = departmentDS.code\r\nWHERE\r\n  employeeDS.type = \"employee\"\r\n  AND departmentDS.type = \"department\"<\/pre>\n<p>&nbsp;<\/p>\n<h3 id=\"crossjoin\">Cross JOIN<\/h3>\n<p>You can use a cross JOIN Query to fetch the cartesian product of the properties from participating documents.The documents are typically not related to each other. This is the equivalent of a inner JOIN <em>without<\/em> the <code>ON<\/code> clause of the join expression.<\/p>\n<p>For example, considering the data model we presented earlier, let\u2019s suppose that you wanted to fetch the cartesian product of all documents of <code>type<\/code> \u201clocation\u201d and <code>type<\/code> \u201cdepartment\u201d. In other words, every \u201clocation\u201d <code>type<\/code> document would be combined with each of the \u201cdepartment\u201d <code>type<\/code> documents.<\/p>\n<p>Since there is no <code>on<\/code> clause specified in the cross JOIN expression, you would need to include a <code>where<\/code> clause to filter the subset of documents to be considered based on document <code>type<\/code>.<\/p>\n<figure><img decoding=\"async\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2018\/01\/cross_join_explain.png\" alt=\"\" \/><\/figure>\n<h4 id=\"request\">Request<\/h4>\n<pre class=\"wrap:true lang:swift decode:true \">\/\/ set up aliases to represent the data source for \"department\" type document\r\n let departmentDS = DataSource.database(db).as(\"departmentDS\")\r\n\/\/ set up aliases to represent the data source for \"location\" type document\r\nlet locationDS = DataSource.database(db).as(\"locationDS\")\r\n\r\n\/\/ Property expression for \"department\" (in employee documents)\r\nlet employeeDeptExpr = Expression.property(\"code\").from(\"departmentDS\")\r\n\r\n\/\/ Property expression for \"department\" (in location documents)\r\nlet departmentCodeExpr = Expression.property(\"code\").from(\"locationDS\")\r\n\r\n\/\/ cross join expression\r\nlet join = Join.crossJoin(locationDS)\r\n\r\n\/\/ type property expressions\r\nlet deptTypeExpr = Expression.property(\"type\").from(\"departmentDS\")\r\nlet locationTypeExpr = Expression.property(\"type\").from(\"locationDS\")\r\n\r\n\/\/ The where clauses filters the set of documents to cross-join on\r\n\/\/ We alias the \"code\" properties since it exists in both the department and location docs\r\n\r\n\/\/ NOTE: The where clause is used to filter the documents to be considered as\r\n\/\/ part of the cartesian join\r\nlet searchQuery = Query.select(\r\n    SelectResult.expression(Expression.property(\"name\").from(\"departmentDS\")).as(\"DeptName\"),\r\n     SelectResult.expression(Expression.property(\"name\").from(\"locationDS\")).as(\"LocationName\"),\r\n    SelectResult.expression(Expression.property(\"address\").from(\"locationDS\")))\r\n    .from(departmentDS)\r\n    .join(join)\r\n    .where(deptTypeExpr.equalTo(Expression.string(\"department\"))\r\n                .and(locationTypeExpr.equalTo(Expression.string(\"location\"))))<\/pre>\n<h4>ANSI SQL<\/h4>\n<p>The equivalent SQL statement for the above query would be<\/p>\n<pre class=\"wrap:true lang:mysql decode:true \">SELECT\r\n  departmentDS.name AS DeptName,\r\n  locationDS.name AS LocationName,\r\n  locationDS.address\r\nFROM\r\n  `travel-sample` departmentDS\r\n  CROSS JOIN `travel-sample` locationDS\r\nWHERE\r\n  departmentDS.type = \"department\"<\/pre>\n<p>&nbsp;<\/p>\n<h3 id=\"chainingofjoins\">Chaining of JOINs<\/h3>\n<p>It is possible to specify multiple JOIN expressions in your <code>select<\/code> clause to be able to JOIN across documents based on different criteria.<\/p>\n<p>For example, considering the data model we presented earlier, let\u2019s suppose that you wanted to fetch the <code>firstName<\/code>, <code>lastName<\/code> of an \u201cemployee\u201d and the corresponding <code>name<\/code> of the \u201cdepartment\u201d that the employee belonged to. In addition, you also wanted to identify the <code>name<\/code> of the \u201clocation\u201d that the employee was based in.<\/p>\n<p>In this case, we use two JOIN expressions.<br \/>\nThe first JOIN expression is used to JOIN documents of <code>type<\/code> \u201cemployee\u201d with documents of <code>type<\/code> \u201cdepartment\u201d based on the \u201cdepartment code\u201d property. In this case, the <code>firstname<\/code> and <code>lastname<\/code> properties are fetched from document of <code>type<\/code> \u201cemployee\u201d and the department <code>name<\/code> is fetched from the document of <code>type<\/code> \u201cdepartment\u201d if and only if the <code>department<\/code> property of the \u201cemployee\u201d matches the corresponding <code>code<\/code> property in the \u201cdepartment\u201d.<\/p>\n<p>The second JOIN expression is used to JOIN documents of <code>type<\/code> \u201cemployee\u201d with documents of <code>type<\/code> \u201clocation\u201d based on the \u201clocation code\u201d property. In this case, the <code>firstname<\/code> and <code>lastname<\/code> properties are fetched from document of <code>type<\/code> \u201cemployee\u201d and the location <code>name<\/code> is fetched from the document of <code>type<\/code> \u201clocation\u201d if and only if the <code>location<\/code> property of the \u201cemployee\u201d matches the corresponding <code>code<\/code> property in the \u201cdepartment\u201d.<\/p>\n<figure><img decoding=\"async\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2018\/01\/multi-join.png\" alt=\"\" \/><\/figure>\n<h4 id=\"request\">Request<\/h4>\n<pre class=\"wrap:true lang:swift decode:true \"> \/\/ set up aliases to represent the data source for \"employee\" type document\r\n let employeeDS = DataSource.database(db).as(\"employeeDS\")\r\n \/\/ set up aliases to represent the data source for \"department\" type document\r\n let departmentDS = DataSource.database(db).as(\"departmentDS\")\r\n \/\/ set up aliases to represent the data source for \"location\" type document\r\n let locationDS = DataSource.database(db).as(\"locationDS\")\r\n \/\/ Property expression for \"department\"  property (in employee documents)   \r\n let employeeDeptExpr = Expression.property(\"department\").from(\"employeeDS\")\r\n\r\n \/\/ Property expression for \"location\"  property (in employee documents) \r\n let employeeLocationExpr = Expression.property(\"location\").from(\"employeeDS\")\r\n\r\n \/\/ Property expression for \"code\"  property (in department documents) \r\n let departmentCodeExpr = Expression.property(\"code\").from(\"departmentDS\")\r\n\r\n \/\/ Property expression for \"code\"  property (in location documents) \r\n let locationCodeExpr = Expression.property(\"code\").from(\"locationDS\")\r\n    \r\n \/\/ Join Criteria 1\r\n \/\/ Join where the \"department\" field of employee documents is equal to the department \"code\" of \"department\" documents\r\n let joinDeptCodeExpr = employeeDeptExpr.equalTo(departmentCodeExpr)\r\n        .and(Expression.property(\"type\").from(\"employeeDS\").equalTo(Expression.string(\"employee\")))\r\n        .and(Expression.property(\"type\").from(\"departmentDS\").equalTo(Expression.string(\"department\")))\r\n    \r\n\r\n \/\/ Join Criteria 2\r\n \/\/ Join where the \"department\" field of employee documents is equal to the department \"code\" of \"department\" documents\r\n let joinLocationCodeExpr = employeeLocationExpr.equalTo(locationCodeExpr)\r\n        .and(Expression.property(\"type\").from(\"employeeDS\").equalTo(Expression.string(\"employee\")))\r\n        .and(Expression.property(\"type\").from(\"locationDS\").equalTo(Expression.string(\"location\")))\r\n    \r\n \/\/ join expression for department code\r\n let joinDeptCode = Join.join(departmentDS).on(joinDeptCodeExpr)\r\n    \r\n \/\/ join expression for location code\r\n let joinLocationCode = Join.join(locationDS).on(joinLocationCodeExpr)\r\n    \r\n \/\/ Multiple join expressions in the join clause\r\n let searchQuery = QueryBuilder.select(SelectResult.expression(Expression.property(\"firstname\").from(\"employeeDS\")),\r\n       SelectResult.expression(Expression.property(\"lastname\").from(\"employeeDS\")),\r\n       SelectResult.expression(Expression.property(\"name\").from(\"departmentDS\")).as(\"deptName\"),\r\n       SelectResult.expression(Expression.property(\"name\").from(\"locationDS\")).as(\"locationName\"))\r\n        .from(employeeDS)\r\n        .join(joinDeptCode,joinLocationCode)<\/pre>\n<h3><\/h3>\n<h4>ANSI SQL<\/h4>\n<p>The equivalent SQL statement for the above query would be<\/p>\n<pre class=\"wrap:true lang:mysql decode:true \">SELECT\r\n  employeeDS.firstname,\r\n  employeeDS.lastname,\r\n  departmentDS.name AS deptName,\r\n  locationDS.name AS locationName\r\nFROM\r\n  `travel-sample` employeeDS\r\n  JOIN `travel-sample` departmentDS ON employeeDS.department = departmentDS.code\r\n  JOIN `travel-sample` locationDS ON employeeDS.location = locationDS.code\r\nWHERE\r\n  departmentDS.type = \"department\"\r\n  AND locationDS.type = \"location\"\r\n  AND employeeDS.type = \"employee\"<\/pre>\n<p>&nbsp;<\/p>\n<h3 id=\"joinexpressionswithfunctions\">JOIN Expressions with Functions<\/h3>\n<p>While all the examples used the <code>equalTo<\/code> comparison in the JOIN expression, it should be noted that you could use any comparison operators such as <code>between<\/code>, <code>greaterThanOrEqualTo<\/code> and so on in the JOIN expression. You can also include any valid <code>Function<\/code> expressions. This is a powerful feature.<\/p>\n<p>For example, considering the data model we presented earlier, let\u2019s suppose that you wanted to fetch the department <code>name<\/code> and corresponding <code>location<\/code> names of the \u201clocation\u201d where the department was based. A department could belong to one or more locations.<br \/>\nIn this case, the JOIN expression would join \u201cdepartment\u201d and \u201clocation\u201d type documents by looking for matches in any of the members of the <code>location<\/code> array property of the department document using the <code>ArrayFunction<\/code> expression.<\/p>\n<figure><img decoding=\"async\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2018\/02\/join_expression.png\" alt=\"\" \/><\/figure>\n<pre class=\"wrap:true lang:swift decode:true\">\/\/ set up aliases to represent the data source for \"department\" type document\r\n let departmentDS = DataSource.database(db).as(\"departmentDS\")\r\n\/\/ set up aliases to represent the data source for \"location\" type document\r\nlet locationDS = DataSource.database(db).as(\"locationDS\")\r\n\r\n\/\/ Property expression for &amp;quot;location&amp;quot;  property (in department documents)\r\nlet departmentLocationExpr = Expression.property(\"location\").from(\"departmentDS\")\r\n\r\n\/\/ Property expression for \"code\" property (in location documents)\r\nlet locationCodeExpr = Expression.property(\"code\").from(\"locationDS\")\r\n\r\n\/\/ Join where the \"code\" field of location documents is contained in\r\n\/\/ the \"location\"  array of \"department\" documents\r\nlet joinDeptCodeExpr = ArrayFunction.contains(departmentLocationExpr, value: locationCodeExpr)\r\n    .and(Expression.property(\"type\").from(\"locationDS\").equalTo(Expression.string(\"location\"))\r\n    .and(Expression.property(\"type\").from(\"departmentDS\").equalTo(Expression.string(\"department\"))))\r\n\r\n\/\/ join expression\r\nlet joinLocationCode = Join.join(departmentDS).on(joinDeptCodeExpr)\r\n\r\n\/\/ Search query with JOIN\r\nlet searchQuery = QueryBuilder.select(\r\nSelectResult.expression(Expression.property(\"name\").from(\"departmentDS\")).as(\"departmentName\"),\r\n    SelectResult.expression(Expression.property(\"name\").from(\"locationDS\")).as(\"locationName\"))\r\n    .from(locationDS)\r\n    .join(joinLocationCode)<\/pre>\n<p>&nbsp;<\/p>\n<h4>ANSI SQL<\/h4>\n<p>Arrays are not supported in SQL. However N1QL includes support for arrays. The corresponding SQL-like statement for the above query would be<\/p>\n<pre class=\"wrap:true lang:mysql decode:true \">SELECT\r\n  departmentDS.name AS departmentName,\r\n  locationDS.name AS locationName\r\nFROM\r\n  `travel-sample` locationDS\r\n  JOIN `travel-sample` departmentDS ON \r\n    ANY code IN departmentDS.location SATISFIES code = locationDS.location\r\n    END \r\n  \r\nWHERE\r\n  departmentDS.type = \"department\"\r\n  AND locationDS.type = \"location\"<\/pre>\n<p>&nbsp;<\/p>\n<h3 id=\"whatnext\">What Next<\/h3>\n<p>This blog post reviewed the powerful JOIN feature in Couchbase Mobile 2.0 that allows you to combine results from multiple JSON documents. You can <a href=\"https:\/\/www.couchbase.com\/downloads\/\">download<\/a> Couchbase Mobile 2.0 and test out the queries discussed in this post. This is a start. Expect to see more functionality in future releases.<\/p>\n<p>Here are a few other Couchbase Mobile Query related posts that may be of interest<br \/>\n&#8211; This <a href=\"https:\/\/www.couchbase.com\/blog\/sql-for-json-query-interface-couchbase-mobile\/\">blog post<\/a> discusses the fundamentals<br \/>\n&#8211; This <a href=\"https:\/\/www.couchbase.com\/blog\/querying-array-collections-couchbase-mobile\/\">blog post<\/a> discusses how to query array collections<br \/>\n&#8211; This <a href=\"https:\/\/www.couchbase.com\/blog\/full-text-search-couchbase-mobile-2-0\/\">blog post<\/a> discusses Full Text Search (FTS) capabilities.<\/p>\n<p>If\u00a0you have questions or feedback, please leave a comment below or feel free to reach out to me at Twitter\u00a0<a href=\"https:\/\/twitter.com\/rajagp\">@rajagp<\/a>\u00a0or email me\u00a0<a href=\"mailto:priya.rajagopal@couchbase.com\">priya.rajagopal@couchbase.com<\/a>. \u00a0The\u00a0<a href=\"https:\/\/www.couchbase.com\/forums\/\">Couchbase Forums<\/a> are another good place to reach out with\u00a0questions.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Couchbase Lite 2.0 supports the ability to perform JOINS across your JSON documents . This is part of the new Query interface based on N1QL, Couchbase\u2019s declarative query language that extends SQL for JSON. If you are familiar with SQL, [&hellip;]<\/p>\n","protected":false},"author":1423,"featured_media":4586,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[1815,7667,1810,1812],"tags":[2004],"ppma_author":[8948],"class_list":["post-4498","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-best-practices-and-tutorials","category-couchbase-lite","category-couchbase-mobile","category-n1ql-query","tag-couchbase-mobile-2-0"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.8 (Yoast SEO v25.8) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Introducing JOIN Queries in Couchbase Mobile - The Couchbase Blog<\/title>\n<meta name=\"description\" content=\"This post discusses how you can JOIN JSON documents in Couchbase Mobile. This capability allows you to combine the contents of multiple JSON documents.\" \/>\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\/join-queries-couchbase-mobile\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Introducing JOIN Queries in Couchbase Mobile\" \/>\n<meta property=\"og:description\" content=\"This post discusses how you can JOIN JSON documents in Couchbase Mobile. This capability allows you to combine the contents of multiple JSON documents.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:published_time\" content=\"2018-02-12T18:00:54+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-14T03:09:22+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/02\/join_expression.png\" \/>\n\t<meta property=\"og:image:width\" content=\"722\" \/>\n\t<meta property=\"og:image:height\" content=\"483\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Priya Rajagopal, Senior Director, Product Management\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@rajagp\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Priya Rajagopal, Senior Director, Product Management\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"12 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/\"},\"author\":{\"name\":\"Priya Rajagopal, Senior Director, Product Management\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/c2da90e57717ee4970c48a87a131ac2c\"},\"headline\":\"Introducing JOIN Queries in Couchbase Mobile\",\"datePublished\":\"2018-02-12T18:00:54+00:00\",\"dateModified\":\"2025-06-14T03:09:22+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/\"},\"wordCount\":1438,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/02\/join_expression.png\",\"keywords\":[\"couchbase mobile 2.0\"],\"articleSection\":[\"Best Practices and Tutorials\",\"Couchbase Lite\",\"Couchbase Mobile\",\"SQL++ \/ N1QL Query\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/\",\"name\":\"Introducing JOIN Queries in Couchbase Mobile - The Couchbase Blog\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/02\/join_expression.png\",\"datePublished\":\"2018-02-12T18:00:54+00:00\",\"dateModified\":\"2025-06-14T03:09:22+00:00\",\"description\":\"This post discusses how you can JOIN JSON documents in Couchbase Mobile. This capability allows you to combine the contents of multiple JSON documents.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/#primaryimage\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/02\/join_expression.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/02\/join_expression.png\",\"width\":722,\"height\":483,\"caption\":\"join on array\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.couchbase.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Introducing JOIN Queries in Couchbase Mobile\"}]},{\"@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\/c2da90e57717ee4970c48a87a131ac2c\",\"name\":\"Priya Rajagopal, Senior Director, Product Management\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/4b50a54778b979d8c345b036ab138734\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/acfb2349788955262cd069497a9e7bdb0e97c26326f2e55811e7c1174e9ef1be?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/acfb2349788955262cd069497a9e7bdb0e97c26326f2e55811e7c1174e9ef1be?s=96&d=mm&r=g\",\"caption\":\"Priya Rajagopal, Senior Director, Product Management\"},\"description\":\"Priya Rajagopal is a Senior Director of Product Management at Couchbase responsible for developer platforms for the cloud and the edge. She has been professionally developing software for over 20 years in several technical and product leadership positions, with 10+ years focused on mobile technologies. As a TISPAN IPTV standards delegate, she was a key contributor to the IPTV standards specifications. She has 22 patents in the areas of networking and platform security.\",\"sameAs\":[\"https:\/\/x.com\/rajagp\"],\"url\":\"https:\/\/www.couchbase.com\/blog\/author\/priya-rajagopalcouchbase-com\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Introducing JOIN Queries in Couchbase Mobile - The Couchbase Blog","description":"This post discusses how you can JOIN JSON documents in Couchbase Mobile. This capability allows you to combine the contents of multiple JSON documents.","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\/join-queries-couchbase-mobile\/","og_locale":"en_US","og_type":"article","og_title":"Introducing JOIN Queries in Couchbase Mobile","og_description":"This post discusses how you can JOIN JSON documents in Couchbase Mobile. This capability allows you to combine the contents of multiple JSON documents.","og_url":"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/","og_site_name":"The Couchbase Blog","article_published_time":"2018-02-12T18:00:54+00:00","article_modified_time":"2025-06-14T03:09:22+00:00","og_image":[{"width":722,"height":483,"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/02\/join_expression.png","type":"image\/png"}],"author":"Priya Rajagopal, Senior Director, Product Management","twitter_card":"summary_large_image","twitter_creator":"@rajagp","twitter_misc":{"Written by":"Priya Rajagopal, Senior Director, Product Management","Est. reading time":"12 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/"},"author":{"name":"Priya Rajagopal, Senior Director, Product Management","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/c2da90e57717ee4970c48a87a131ac2c"},"headline":"Introducing JOIN Queries in Couchbase Mobile","datePublished":"2018-02-12T18:00:54+00:00","dateModified":"2025-06-14T03:09:22+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/"},"wordCount":1438,"commentCount":0,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/02\/join_expression.png","keywords":["couchbase mobile 2.0"],"articleSection":["Best Practices and Tutorials","Couchbase Lite","Couchbase Mobile","SQL++ \/ N1QL Query"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/","url":"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/","name":"Introducing JOIN Queries in Couchbase Mobile - The Couchbase Blog","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/02\/join_expression.png","datePublished":"2018-02-12T18:00:54+00:00","dateModified":"2025-06-14T03:09:22+00:00","description":"This post discusses how you can JOIN JSON documents in Couchbase Mobile. This capability allows you to combine the contents of multiple JSON documents.","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/02\/join_expression.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/02\/join_expression.png","width":722,"height":483,"caption":"join on array"},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/join-queries-couchbase-mobile\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Introducing JOIN Queries in Couchbase Mobile"}]},{"@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\/c2da90e57717ee4970c48a87a131ac2c","name":"Priya Rajagopal, Senior Director, Product Management","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/4b50a54778b979d8c345b036ab138734","url":"https:\/\/secure.gravatar.com\/avatar\/acfb2349788955262cd069497a9e7bdb0e97c26326f2e55811e7c1174e9ef1be?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/acfb2349788955262cd069497a9e7bdb0e97c26326f2e55811e7c1174e9ef1be?s=96&d=mm&r=g","caption":"Priya Rajagopal, Senior Director, Product Management"},"description":"Priya Rajagopal is a Senior Director of Product Management at Couchbase responsible for developer platforms for the cloud and the edge. She has been professionally developing software for over 20 years in several technical and product leadership positions, with 10+ years focused on mobile technologies. As a TISPAN IPTV standards delegate, she was a key contributor to the IPTV standards specifications. She has 22 patents in the areas of networking and platform security.","sameAs":["https:\/\/x.com\/rajagp"],"url":"https:\/\/www.couchbase.com\/blog\/author\/priya-rajagopalcouchbase-com\/"}]}},"authors":[{"term_id":8948,"user_id":1423,"is_guest":0,"slug":"priya-rajagopalcouchbase-com","display_name":"Priya Rajagopal, Senior Director, Product Management","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/acfb2349788955262cd069497a9e7bdb0e97c26326f2e55811e7c1174e9ef1be?s=96&d=mm&r=g","author_category":"","last_name":"Rajagopal, Senior Director, Product Management","first_name":"Priya","job_title":"","user_url":"","description":"Priya Rajagopal is a Senior Director of Product Management at Couchbase responsible for developer platforms for the cloud and the edge. She has been professionally developing software for over 20 years in several technical and product leadership positions, with 10+ years focused on mobile technologies. As a TISPAN IPTV standards delegate, she was a key contributor to the IPTV standards specifications. She has 22 patents in the areas of networking and platform security."}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/4498","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\/1423"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/comments?post=4498"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/4498\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media\/4586"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media?parent=4498"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/categories?post=4498"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/tags?post=4498"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/ppma_author?post=4498"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}