{"id":104145,"date":"2024-10-16T18:02:50","date_gmt":"2024-10-16T18:02:50","guid":{"rendered":"https:\/\/www.red-gate.com\/simple-talk\/?p=104145"},"modified":"2024-11-14T22:03:45","modified_gmt":"2024-11-14T22:03:45","slug":"creating-views-in-mongodb","status":"publish","type":"post","link":"https:\/\/www.red-gate.com\/simple-talk\/databases\/nosql\/mongodb\/creating-views-in-mongodb\/","title":{"rendered":"Creating Views in MongoDB: Aggregation Pipelines, $lookup Joins, On-Demand Materialised Views, and MongoDB Compass"},"content":{"rendered":"<p><strong>This article is part of Robert Sheldon's continuing series on Mongo DB. To see all of the items in the series, <a href=\"https:\/\/www.red-gate.com\/simple-talk\/collections\/robert-sheldon-ongoing-mongodb-primer\/\">click here<\/a>.<\/strong><\/p>\n\n<p>Like many relational database systems, MongoDB supports the use of views. A view is a read-only object in a MongoDB database that works much like <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/nosql\/getting-started-with-mongodb\/\">a collection<\/a>, except that the data is not persisted to disk. MongoDB retrieves the view\u2019s documents from the source collection when a client calls the view. In this sense, a view is essentially a saved query that MongoDB runs at the time the view is invoked.<\/p>\n<p>This article, which is part of a series on MongoDB, introduces you to views and how you can create, query, and update them. It also includes a number of examples that demonstrate how they work. The article focuses exclusively on standard views, which are one of two types of views in MongoDB. The platform also supports on-demand materialized views that persist data to disk. However, materialized views are beyond the scope of this article. For now, we\u2019ll stick with standard views, which is a good place to start when learning about views.<\/p>\n<p><em>Note: For the examples in this article, I used the same MongoDB Atlas and MongoDB Compass environments I used for the previous articles in this series. Refer to the first article for details about setting up these environments. The examples require the <\/em><code><em>hr<\/em><\/code><em> database and <\/em><code><em>candidates<\/em><\/code><em> collection, which were used in previous articles. If you haven\u2019t set them up, you should do so now, assuming you want to try out the examples in this article.<\/em><\/p>\n<h2>Introducing views in MongoDB<\/h2>\n<p>Adding a view to a MongoDB database is a fairly straightforward process. You can use either MongoDB Shell or the MongoDB Compass interface. For this article, I focus primarily on running commands in MongoDB Shell, although I provide an overview of the GUI approach later in the article.<\/p>\n<p>To create a view in MongoDB Shell, you can use either the <code>createView<\/code> or <code>createCollection<\/code> database method. I\u2019ve used the <code>createView<\/code> method in the examples here because I like the fact that the name clearly telegraphs what you\u2019re trying to achieve. If you want to learn how to use the <code>createCollection<\/code> method, refer to the MongoDB article <a href=\"https:\/\/www.mongodb.com\/docs\/manual\/reference\/method\/db.createCollection\/\">db.createCollection()<\/a>.<\/p>\n<p>When you create a view in MongoDB, you must define an aggregation pipeline that determines which documents to return when a client queries the view. You must also provide a name for a view, as well as the source collection, as shown in the following syntax:<\/p>\n<pre class=\"lang:none theme:none\">db.createView(\n  \"&lt;view_name&gt;\",\n  \"&lt;source_collection&gt;\",\n  [&lt;pipeline&gt;],\n  { \"collation\" : { &lt;settings&gt; } }\n)<\/pre>\n<p>In MongoDB, a <code>createView<\/code> method consists of the following components:<\/p>\n<ul>\n<li><strong>db.<\/strong> System variable for referencing the current database and accessing the properties and methods available to the database object.<\/li>\n<li><strong>createView.<\/strong> A method available to the database object for creating a view based on the target collection.<\/li>\n<li><strong><em>view_name<\/em>.<\/strong> Placeholder for the name that will be assigned to the view. The view\u2019s name is a string value.<\/li>\n<li><strong><em>source_collection<\/em>.<\/strong> Placeholder for the target collection. For this article, we\u2019ll be using the <code>candidates<\/code> collection. The collection\u2019s name is a string value.<\/li>\n<li><strong><em>pipeline<\/em>.<\/strong> Placeholder for the aggregation pipeline that determines which documents to return from the target collection. The pipeline is defined as an array, which means it must be enclosed in square brackets. In addition, it cannot include the <code>$out<\/code> or <code>$merge<\/code> stage.<\/li>\n<li><strong>collation.<\/strong> An optional component that lets you specify a view\u2019s collation. If the option is not included, the method uses the collation defined on the target collection. For this article, we won\u2019t be including this option. To learn how to specify a collation, refer to the MongoDB article <a href=\"https:\/\/www.mongodb.com\/docs\/manual\/reference\/method\/db.createView\/\">db.createView()<\/a>.<\/li>\n<\/ul>\n<p>The sections to follow walk you through the process of creating a view, based on the syntax above. You\u2019ll also learn how to update and delete a view.<\/p>\n<h2>Creating views in MongoDB<\/h2>\n<p>Before I show you how to create a view, there are a few preparatory steps you might need take to ensure you can use the <code>candidates<\/code> collection for the examples to follow. The exact steps depend on whether or not you already created the collection for a previous article.<\/p>\n<p>If the collection does not exist, you should create it now within the <code>hr<\/code> database. You can then skip the next two steps because they\u2019re specific to those with an existing collection.<\/p>\n<p>If the collection does exist, the first step is to ensure that no validation rules are defined on the <code>candidates<\/code> collection. To remove any validation rules, run the following <code>runCommand<\/code> statement:<\/p>\n<pre class=\"lang:none theme:none\">db.runCommand( { collMod: \"candidates\", validator: {} } );<\/pre>\n<p>As you learned in the previous article, the <code>runCommand<\/code> method calls the <code>collMod<\/code> database command, which specifies the <code>candidates<\/code> collection. The method also calls the <code>validator<\/code> command. The command\u2019s argument is an empty document (curly brackets), which means that no rules will be defined. If rules already exist, they will be removed.<\/p>\n<p>The second step is to remove any documents in the collection. For this, you can use a <code>deleteMany<\/code> statement to specify that all documents should be deleted, as in the following example:<\/p>\n<pre class=\"lang:none theme:none\">db.candidates.deleteMany({});<\/pre>\n<p>At this point, the <code>candidates<\/code> collection should contain no validation rules or documents, whether or not it already existed. Now run the following <code>insertMany<\/code> statement, which adds 10 documents to the collection:<\/p>\n<pre class=\"lang:none theme:none\">db.candidates.insertMany([\n  { \"_id\": 101, \"name\": \"Drew\", \"position\": \"Senior Developer\", \"dept\": 1001, \"active\": true },\n  { \"_id\": 102, \"name\": \"Parker\", \"position\": \"Data Scientist\", \"dept\": 1001, \"active\": true },\n  { \"_id\": 103, \"name\": \"Harper\", \"position\": \"Marketing Manager\", \"dept\": 1004, \"active\": true },\n  { \"_id\": 104, \"name\": \"Darcy\", \"position\": \"Senior Developer\", \"dept\": 1001, \"active\": false },\n  { \"_id\": 105, \"name\": \"Carey\", \"position\": \"SEO Specialist\", \"dept\": 1004, \"active\": false },\n  { \"_id\": 106, \"name\": \"Avery\", \"position\": \"Network Admin\", \"dept\": 1002, \"active\": true },\n  { \"_id\": 107, \"name\": \"Robin\", \"position\": \"Security Specialist\", \"dept\": 1002, \"active\": true },\n  { \"_id\": 108, \"name\": \"Koda\", \"position\": \"QA Specialist\", \"dept\": 1001, \"active\": true },\n  { \"_id\": 109, \"name\": \"Jessie\", \"position\": \"Brand Manager\", \"dept\": 1004, \"active\": false },\n  { \"_id\": 110, \"name\": \"Dana\", \"position\": \"Market Analyst\", \"dept\": 1004, \"active\": true }\n]);<\/pre>\n<p>With the documents in place, you can now create your first view. As I mentioned earlier, a view requires an aggregation pipeline that determines which documents to return. I find it useful to first define the pipeline in a separate query to make sure it will return the correct documents. For example, the following statement calls the <code>aggregate<\/code> method, which in turn, specifies an aggregation pipeline:<\/p>\n<pre class=\"lang:none theme:none\">db.candidates.aggregate(\n  [ { $match: { $and: [  { \"dept\": 1001 }, { \"active\": true } ] } } ]\n);<\/pre>\n<p>I won\u2019t go in great detail here because aggregations are explained in the fourth article in this series, <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/nosql\/mongodb\/building-mongodb-aggregations\/\">Building MongoDB Aggregations<\/a>. In this example, the pipeline includes only one stage, <code>$match<\/code>, which specifies that the pipeline should return only those documents in which the <code>dept<\/code> value is <code>1001<\/code> and the <code>active<\/code> value is <code>true<\/code>. The <code>aggregate<\/code> statement should return the following three documents:<\/p>\n<pre class=\"lang:none theme:none\">{\n  _id: 101,\n  name: 'Drew',\n  position: 'Senior Developer',\n  dept: 1001,\n  active: true\n}\n{\n  _id: 102,\n  name: 'Parker',\n  position: 'Data Scientist',\n  dept: 1001,\n  active: true\n}\n{\n  _id: 108,\n  name: 'Koda',\n  position: 'QA Specialist',\n  dept: 1001,\n  active: true\n}<\/pre>\n<p>Once you\u2019re satisfied with your aggregation pipeline, you can use it in a <code>createView<\/code> statement to define the view, as shown in the following example:<\/p>\n<pre class=\"lang:none theme:none\">db.createView(\n   \"rd_candidates\",\n   \"candidates\",\n   [ { $match: { $and: [  { \"dept\": 1001 }, { \"active\": true } ] } } ]\n);<\/pre>\n<p>The statement begins by calling the <code>db<\/code> system variable and the <code>createView<\/code> database method. The method takes three arguments: a name for the view (<code>rd_candidates<\/code>), the name of the target collection (<code>candidates<\/code>), and the aggregation pipeline.<\/p>\n<p>When you run this statement, MongoDB adds the view to the <code>hr<\/code> database. You can then access the view much like any collection in the database. For example, the following <code>find<\/code> statement returns all three documents in the view:<\/p>\n<pre class=\"lang:none theme:none\">db.rd_candidates.find();<\/pre>\n<p>Because a view\u2019s data is not persisted to disk, any changes to the underlying collection are reflected in the documents returned by the view. For example, the following <code>updateOne<\/code> statement modifies the document with an <code>_id<\/code> value of <code>108<\/code>, changing the <code>active<\/code> field value from <code>true<\/code> to <code>false<\/code>:<\/p>\n<pre class=\"lang:none theme:none\">db.candidates.updateOne(\n { \"_id\" : 108 },\n { $set: { \"active\": false } }\n);<\/pre>\n<p>If you rerun the <code>find<\/code> statement, it will now return only two documents because the view is looking only for documents whose <code>dept<\/code> value is <code>1001<\/code> and <code>active<\/code> value is <code>true<\/code>:<\/p>\n<pre class=\"lang:none theme:none\">db.rd_candidates.find();<\/pre>\n<p>You\u2019re not limited to such a basic <code>find<\/code> statement. You can define whatever type of query you need to return the required data. For example, the following <code>find<\/code> statement specifies that the <code>position<\/code> value must be <code>Senior<\/code> <code>Developer<\/code> for the view\u2019s documents to be returned:<\/p>\n<pre class=\"lang:none theme:none\">db.rd_candidates.find( { \"position\": \"Senior Developer\" } );<\/pre>\n<p>The <code>find<\/code> statement now returns only <code>one<\/code> document. Of course, you can make your <code>find<\/code> statements much more specific, depending on the source data and your specific requirements. The main point here is that you can search a view just like searching a collection. However, you cannot modify the data through a view like you can in some relational database systems.<\/p>\n<h2>Joining collections in a view<\/h2>\n<p>As you saw in the previous section, the key to creating a view is to define an aggregation pipeline. One of the advantages of using a pipeline is that you can include a <code>$lookup<\/code> stage that retrieves data from a second collection. The <code>$lookup<\/code> stage makes it possible to create a join condition, similar to the way you can join tables in a relational database.<\/p>\n<p>Before I demonstrate how this works, you need to prepare your test environment by adding another collection to the <code>hr<\/code> database. The following <code>createCollection<\/code> statement adds the <code>org<\/code> collection, and the <code>insertMany<\/code> statement adds five documents to the collection:<\/p>\n<pre class=\"lang:none theme:none\">db.createCollection(\"org\");\ndb.org.insertMany([\n  { \"_id\": 1001, \"dept\": \"R&amp;D\", \"location\": \"Building C\" },\n  { \"_id\": 1002, \"dept\": \"IT\", \"location\": \"Building C\" },\n  { \"_id\": 1003, \"dept\": \"Purchasing\", \"location\": \"Building B\" },\n  { \"_id\": 1004, \"dept\": \"Marketing\", \"location\": \"Building B\" },\n  { \"_id\": 1005, \"dept\": \"Legal\", \"location\": \"Building A\" }\n]);<\/pre>\n<p>With the collection in place, you can create your aggregation pipeline, once again defining it first within an query. The following <code>aggregate<\/code> statement begins with a <code>$match<\/code> stage, followed by several other stages, which are necessary to handle the lookup operation:<\/p>\n<pre class=\"lang:none theme:none\">db.candidates.aggregate(\n  [ \n    { $match: { \"active\": false } },\n    {\n      $lookup:\n      {\n        from: \"org\",\n        localField: \"dept\",\n        foreignField: \"_id\",\n        as: \"dept_name\"\n      }\n    },\n    {\n      $project:\n      {\n        name: 1,\n        position: 1,\n        dept_name: \"$dept_name.dept\"\n      }\n    } ,\n    { $unwind: \"$dept_name\" }\n  ]\n);<\/pre>\n<p>The <code>$match<\/code> stage specifies that the <code>active<\/code> field value must be <code>false<\/code>. This stage is followed by a <code>$lookup<\/code> stage, which performs a left outer join, based on the specified fields. The stage includes the following options:<\/p>\n<ul>\n<li><code><strong>from<\/strong><\/code><strong>.<\/strong> Specifies the target collection, which in this case is <code>org<\/code>. The target collection must be in the same database as the primary collection (<code>candidates<\/code>).<\/li>\n<li><code><strong>localField<\/strong><\/code><strong>.<\/strong> A field in the primary collection that matches a field in the target collection. For this example, we\u2019ll use the <code>dept<\/code> field in the <code>candidates<\/code> collection.<\/li>\n<li><code><strong>foreignField<\/strong><\/code><strong>.<\/strong> The field in the target collection that contains the matching values. For this example, we\u2019ll use the <code>_id<\/code> field in the <code>org<\/code> collection.<\/li>\n<li><code><strong>as<\/strong><\/code><strong>.<\/strong> A name for the field returned by the <code>$lookup<\/code> stage. For this example, we\u2019ll use <code>dept_name<\/code> for the returned field name The field is returned as an array that contains the documents whose <code>candidates.dept<\/code> value matches the <code>org._id<\/code> value. Because the <code>_id<\/code> field is a unique identifier, only one document will be returned per match.<\/li>\n<\/ul>\n<p>The <code>$lookup<\/code> stage is followed by the <code>$project<\/code> stage and <code>$unwind<\/code> stage. The <code>$project<\/code> stage specifies which fields to include in the results. It does not include the <code>_id<\/code> field because it is included by default. It also does not include the <code>active<\/code> field. Since all the field\u2019s values in the returned documents are <code>false<\/code>, the field is not needed (unless you want to include it for confirmation).<\/p>\n<p>Notice that the <code>$lookup<\/code> stage pulls only the name of the department from the <code>dept_name<\/code> field. Unfortunately, the field name is still returned as an array. This is why the pipeline also includes <code>$unwind<\/code> stage, which deconstructs the array. Again, refer to the article on aggregating data for more information about these two stages.<\/p>\n<p>When you run the <code>aggregate<\/code> statement, it returns the four documents whose <code>active<\/code> value is <code>false<\/code>, as shown in the following results:<\/p>\n<pre class=\"lang:none theme:none\">{\n  _id: 104,\n  name: 'Darcy',\n  position: 'Senior Developer',\n  dept_name: 'R&amp;D'\n}\n{\n  _id: 105,\n  name: 'Carey',\n  position: 'SEO Specialist',\n  dept_name: 'Marketing'\n}\n{\n  _id: 108,\n  name: 'Koda',\n  position: 'QA Specialist',\n  dept_name: 'R&amp;D'\n}\n{\n  _id: 109,\n  name: 'Jessie',\n  position: 'Brand Manager',\n  dept_name: 'Marketing'\n}<\/pre>\n<p>Once you\u2019re satisfied with your aggregation, you can plug it into your <code>createView<\/code> statement, as in the following example:<\/p>\n<pre class=\"lang:none theme:none\">db.createView(\n  \"inactive\",\n  \"candidates\",\n  [ \n    { $match: { \"active\": false } },\n    {\n      $lookup:\n      {\n        from: \"org\",\n        localField: \"dept\",\n        foreignField: \"_id\",\n        as: \"dept_name\"\n      }\n    },\n    {\n      $project:\n      {\n        name: 1,\n        position: 1,\n        dept_name: \"$dept_name.dept\"\n      }\n    } ,\n    { $unwind: \"$dept_name\" }\n  ]\n);<\/pre>\n<p>The <code>createView<\/code> statement defines a view named <code>inactive<\/code>. Notice that the method\u2019s second argument is still <code>candidates<\/code> because this is the primary collection. After you create the view, you can run the following <code>find<\/code> statement to verify that the view returns the expected documents:<\/p>\n<pre class=\"lang:none theme:none\">db.inactive.find();<\/pre>\n<p>The <code>find<\/code> statement should return the same four documents returned by the previous <code>aggregate <\/code>statement.<\/p>\n<h2>Updating and deleting views in MongoDB<\/h2>\n<p>MongoDB provides two methods for updating a view in MongoDB Shell. The first method is to use the <code>runCommand<\/code> method, which lets you specify options for updating a view, as shown in the following example:<\/p>\n<pre class=\"lang:none theme:none\">db.runCommand( {\n  collMod: \"rd_candidates\",\n  viewOn: \"candidates\",\n  \"pipeline\": [ { $match: { \"dept\": 1001 } } ]\n} );<\/pre>\n<p>The <code>runCommand<\/code> statement includes the following three database commands:<\/p>\n<ul>\n<li><code><strong>collMod<\/strong><\/code><strong>.<\/strong> Specifies the target collection or, as in this case, the target view. We\u2019ll be using the view we created above, <code>rd_candidates<\/code>.<\/li>\n<li><code><strong>viewOn<\/strong><\/code><strong>.<\/strong> Specifies the view\u2019s underlying source collection or view, which in this case is the <code>candidates<\/code> collection.<\/li>\n<li><code><strong>pipeline<\/strong><\/code><strong>.<\/strong> Defines the updated aggregation pipeline. The pipeline has been simplified to return only those documents with a <code>dept<\/code> value of <code>1001<\/code>.<\/li>\n<\/ul>\n<p>After you update the view, you can run the following <code>find<\/code> statement, which should return the four documents in department 1001:<\/p>\n<pre class=\"lang:none theme:none\">db.rd_candidates.find();<\/pre>\n<p>Another method you can use to update a view is to first drop the view and then re-create it. To drop the view, run the following command:<\/p>\n<pre class=\"lang:none theme:none\">db.rd_candidates.drop();<\/pre>\n<p>The command calls the <code>db<\/code> system variable, which is followed by the view\u2019s name. You can then use the <code>drop<\/code> method that\u2019s available to the view object to remove the view. After you\u2019ve dropped the view, you can run a <code>createView<\/code> statement that re-creates the view, using the updated pipeline definition:<\/p>\n<pre class=\"lang:none theme:none\">db.createView(\n   \"rd_candidates\",\n   \"candidates\",\n   [ { $match: { \"dept\": 1001 } } ]\n);<\/pre>\n<p>As you\u2019ve no doubt figured out, you can also use the <code>drop<\/code> method simply to remove a view, without re-creating it:<\/p>\n<pre class=\"lang:none theme:none\">db.rd_candidates.drop();<\/pre>\n<p>The following command does the same thing with the <code>inactive<\/code> view:<\/p>\n<pre class=\"lang:none theme:none\">db.inactive.drop();<\/pre>\n<p>That\u2019s really all there is to updating and dropping views. It\u2019s up to you which method you use to update a view\u2014the <code>runCommand<\/code> method or the <code>drop<\/code>\/<code>createView<\/code> method\u2014as long as you don\u2019t start dropping views that your applications still rely on.<\/p>\n<h2>Creating views in MongoDB Compass<\/h2>\n<p>I also want to provide you with an overview of how you can use the Compass GUI to create a view. I won\u2019t spend a lot of time on this because the process of creating a view is simply a matter of building an aggregation pipeline and saving it as a view. In in the fifth article in this series, <a href=\"https:\/\/www.red-gate.com\/simple-talk\/databases\/nosql\/mongodb\/building-mongodb-aggregations-in-mongodb-compass\/\">Building MongoDB Aggregations in MongoDB Compass<\/a>, I explain how to use the GUI to build a pipeline.<\/p>\n<p>Let me demonstrate with a simple example. Start by opening the <code>candidates<\/code> collection in the main Compass window and then go to the <strong>Aggregations<\/strong> tab. Add a <code>$match<\/code> stage and specify that the <code>active<\/code> field must be <code>true<\/code>. The following figure shows what the <strong>Aggregations<\/strong> tab should look like at this point.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"2464\" height=\"1478\" class=\"wp-image-104146\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/10\/word-image-104145-1.png\" \/><\/p>\n<p>This is the only stage you\u2019ll be adding. You can run the pipeline if you want to verify the pipeline\u2019s documents. Just be sure to return to editing mode when you\u2019re done. Next, click the <strong>Save<\/strong> button and select <strong>Create<\/strong> <strong>view<\/strong> from the drop-down list. When the <strong>Create a View<\/strong> dialog box appears, type <strong>active<\/strong> in the <strong>name<\/strong> text box, and then click <strong>Create<\/strong>.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"1642\" height=\"952\" class=\"wp-image-104147\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/10\/word-image-104145-2.png\" \/><\/p>\n<p>Compass will add the view to the database, open a new tab for the view, and display the documents returned by that view. The following figure shows the documents in Table View.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" width=\"2280\" height=\"938\" class=\"wp-image-104148\" src=\"https:\/\/www.red-gate.com\/simple-talk\/wp-content\/uploads\/2024\/10\/word-image-104145-3.png\" \/><\/p>\n<p>That\u2019s all there is to creating a view in the Compass GUI. That hard part, of course, is building the pipeline, which you must get right to return the documents you need.<\/p>\n<h2>Getting started with views in MongoDB<\/h2>\n<p>Views are a handy feature in MongoDB because they provide an extra layer of abstraction between a client and the collection\u2019s document structure. Views make it possible to exclude personal and private information and return only the data specified in the view definition. Views also make it easy to build predefined queries that include complex expressions and computed data, as well as pull data from other collections, while taking up relatively little disk space.<\/p>\n<p>You can learn more about views in the MongoDB documentation and other resources. A good place to start is with the MongoDB topic <a href=\"https:\/\/www.mongodb.com\/docs\/manual\/core\/views\/\">Views<\/a>. This will also point you to details about working with materialized views. The better you understand how to work with views and create them in your databases, the more you\u2019ll benefit from their use. Indeed, views can be a valuable tool in supporting most data-driven applications.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Creating views in MongoDB using aggregation pipelines &#8211; standard views, on-demand materialised views with $merge, joining collections with $lookup, filtering with $match, projecting with $project. Covers both MongoDB Shell commands (createCollection, db.runCommand) and the MongoDB Compass GUI approach.&hellip;<\/p>\n","protected":false},"author":221841,"featured_media":104149,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[53,159161],"tags":[5618,159226],"coauthors":[6779],"class_list":["post-104145","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-featured","category-mongodb","tag-mongodb","tag-mongodbseriesrobertsheldon"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/104145","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/users\/221841"}],"replies":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/comments?post=104145"}],"version-history":[{"count":2,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/104145\/revisions"}],"predecessor-version":[{"id":104160,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/posts\/104145\/revisions\/104160"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media\/104149"}],"wp:attachment":[{"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/media?parent=104145"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/categories?post=104145"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/tags?post=104145"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.red-gate.com\/simple-talk\/wp-json\/wp\/v2\/coauthors?post=104145"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}