<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: VisuaLeaf</title>
    <description>The latest articles on DEV Community by VisuaLeaf (@visualeaf).</description>
    <link>https://dev.to/visualeaf</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3818324%2F6cb54fe1-a36c-4f1a-a7b5-fa360f5daf8a.png</url>
      <title>DEV Community: VisuaLeaf</title>
      <link>https://dev.to/visualeaf</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/visualeaf"/>
    <language>en</language>
    <item>
      <title>What changed in MongoDB 9.0, and how can it help in everyday database work? Practical examples in VisuaLeaf show the new behavior and compare results with MongoDB 8.2.5.</title>
      <dc:creator>VisuaLeaf</dc:creator>
      <pubDate>Wed, 14 Oct 2026 17:43:06 +0000</pubDate>
      <link>https://dev.to/visualeaf/what-changed-in-mongodb-90-and-how-can-it-help-in-everyday-database-work-practical-examples-in-40ih</link>
      <guid>https://dev.to/visualeaf/what-changed-in-mongodb-90-and-how-can-it-help-in-everyday-database-work-practical-examples-in-40ih</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/roxana_haidiner/whats-new-in-mongodb-90-query-settings-validation-and-resource-limits-h1k" class="crayons-story__hidden-navigation-link"&gt;What's New in MongoDB 9.0: Query Settings, Validation, and Resource Limits&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/roxana_haidiner" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2224346%2Ff71cca7a-b282-4793-a3ae-b04d89957d47.png" alt="roxana_haidiner profile" class="crayons-avatar__image" width="512" height="512"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/roxana_haidiner" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Roxana-Maria Haidiner
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Roxana-Maria Haidiner
                
                
              
              &lt;div id="story-author-preview-content-4851574" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/roxana_haidiner" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2224346%2Ff71cca7a-b282-4793-a3ae-b04d89957d47.png" class="crayons-avatar__image" alt="" width="512" height="512"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Roxana-Maria Haidiner&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/roxana_haidiner/whats-new-in-mongodb-90-query-settings-validation-and-resource-limits-h1k" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Oct 14&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/roxana_haidiner/whats-new-in-mongodb-90-query-settings-validation-and-resource-limits-h1k" id="article-link-4851574"&gt;
          What's New in MongoDB 9.0: Query Settings, Validation, and Resource Limits
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/mongodb"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;mongodb&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/nosql"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;nosql&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/database"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;database&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/softwaredevelopment"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;softwaredevelopment&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/roxana_haidiner/whats-new-in-mongodb-90-query-settings-validation-and-resource-limits-h1k" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;4&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/roxana_haidiner/whats-new-in-mongodb-90-query-settings-validation-and-resource-limits-h1k#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            10 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>How to Open a SQLite Database: Query and Explore Your Data</title>
      <dc:creator>VisuaLeaf</dc:creator>
      <pubDate>Wed, 14 Oct 2026 17:36:15 +0000</pubDate>
      <link>https://dev.to/visualeaf/how-to-open-a-sqlite-database-query-and-explore-your-data-275d</link>
      <guid>https://dev.to/visualeaf/how-to-open-a-sqlite-database-query-and-explore-your-data-275d</guid>
      <description>&lt;p&gt;You have a SQLite file and want to see the information inside it. To open a SQLite database, select the file in software that can read SQLite, such as &lt;a href="https://visualeaf.com" rel="noopener noreferrer"&gt;VisuaLeaf&lt;/a&gt;. You can then browse the tables and use SQL to find the information you need.&lt;/p&gt;

&lt;p&gt;SQL is the language you use to tell a database what you want. A query is one of those requests. It might ask for ten inspection records, the name of a technician, or the number of checks that failed.&lt;/p&gt;

&lt;p&gt;This guide uses an example inspection database. You’ll open the file, browse its tables, join related records, read JSON values, export results to Excel, and create a chart. If you follow along with your own database, replace the example table and column names with yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, understand what is inside the file
&lt;/h2&gt;

&lt;p&gt;SQLite stores data in a database file. You don't need to set up a separate database server to open it.&lt;/p&gt;

&lt;p&gt;Inside the file, the data is organised into *&lt;strong&gt;&lt;em&gt;tables&lt;/em&gt;&lt;/strong&gt;*. A table looks a little like a spreadsheet: it has rows and columns.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5u5x1phg182i35aajvbu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5u5x1phg182i35aajvbu.png" alt="VisuaLeaf Split View showing the SQLite inspections table structure beside a data grid with five rows." width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;inspections&lt;/code&gt; table’s columns on the left and five inspection records on the right.&lt;/p&gt;

&lt;p&gt;The information is split across several tables:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;sites&lt;/code&gt; holds details about the places being inspected,
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;technicians&lt;/code&gt; holds details about the people doing the inspections,
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;inspection_items&lt;/code&gt; holds the results of individual checks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An inspection can therefore have one row describing the visit and several other rows describing its checklist items.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open your SQLite database in VisuaLeaf
&lt;/h2&gt;

&lt;p&gt;SQLite files often end in &lt;code&gt;.db&lt;/code&gt;, &lt;code&gt;.sqlite&lt;/code&gt;, or &lt;code&gt;.sqlite3&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://visualeaf.com/database/sqlite/" rel="noopener noreferrer"&gt;VisuaLeaf&lt;/a&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a connection and choose SQLite.&lt;/li&gt;
&lt;li&gt;Select your existing database file.&lt;/li&gt;
&lt;li&gt;Connect, then expand &lt;code&gt;main&lt;/code&gt; and *&lt;strong&gt;&lt;em&gt;Tables&lt;/em&gt;&lt;/strong&gt;* in the sidebar.&lt;/li&gt;
&lt;li&gt;Open a table to see its records.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You select a file instead of entering a server address, port, or database username. If your database is already connected, go straight to its tables.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;main&lt;/code&gt; is SQLite's name for the primary database opened by that connection. You don't need to create another database with that name.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fulczsn7mq6pfiorlzcha.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fulczsn7mq6pfiorlzcha.png" alt="VisuaLeaf SQLite connection form showing the database file path and a successful connection test." width="800" height="713"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Connecting to the local &lt;code&gt;field_inspections.sqlite&lt;/code&gt; database in VisuaLeaf.&lt;/p&gt;

&lt;p&gt;If you don’t have a SQLite database yet, click *&lt;strong&gt;&lt;em&gt;New DB&lt;/em&gt;&lt;/strong&gt;* in the connection form to create one. It starts empty, so you can add your own tables and data. The examples below use our existing inspection database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read a few inspections with SQL
&lt;/h2&gt;

&lt;p&gt;Open &lt;code&gt;inspections&lt;/code&gt; in the data view to browse its rows. You can also choose exactly which columns to display using the SQL Editor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT id, status, risk_score
FROM inspections
ORDER BY id
LIMIT 10;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read this query one line at a time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SELECT id, status, risk_score&lt;/code&gt; chooses the three columns to show.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;FROM inspections&lt;/code&gt; tells SQLite which table to read.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ORDER BY id&lt;/code&gt; sorts the records by their identifier, from smallest to largest.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;LIMIT 10&lt;/code&gt; asks for at most ten rows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This query reads data. It doesn't change the records.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd9iq6miyj0gif7fvleq6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd9iq6miyj0gif7fvleq6.png" alt="VisuaLeaf SQLite SQL Editor showing a SELECT query above ten inspection rows with id, status, and risk_score columns." width="800" height="616"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Reading ten inspection records, ordered by ID, with their statuses and risk scores.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;id&lt;/code&gt; column is the inspection's *&lt;strong&gt;&lt;em&gt;primary key&lt;/em&gt;&lt;/strong&gt;*: the value that identifies that row. Keep it in your results so you can find the same inspection again.&lt;/p&gt;

&lt;p&gt;Look at the values before writing a filter. A column called &lt;code&gt;status&lt;/code&gt; doesn't tell you which words the application stores there. Check the records rather than guessing whether a status is called &lt;code&gt;complete&lt;/code&gt;, &lt;code&gt;completed&lt;/code&gt;, or something else.&lt;/p&gt;

&lt;h2&gt;
  
  
  What do the column types mean?
&lt;/h2&gt;

&lt;p&gt;Open Table Management for &lt;code&gt;sites&lt;/code&gt;. It shows how each column is defined.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F732jo46emivb5oiednsj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F732jo46emivb5oiednsj.png" alt="VisuaLeaf Table Management showing the SQLite sites table with INTEGER, TEXT, and REAL columns, primary key id, and a foreign key linking client_id to clients.id." width="800" height="662"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;sites&lt;/code&gt; table in VisuaLeaf, showing each column’s data type, whether it allows missing values (&lt;code&gt;NULL&lt;/code&gt;), and its default value.&lt;/p&gt;

&lt;p&gt;The screenshot includes three common SQLite types:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Data type&lt;/th&gt;
&lt;th&gt;What it stores&lt;/th&gt;
&lt;th&gt;Example columns&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;INTEGER&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Whole numbers, such as 1, 2, or 3&lt;/td&gt;
&lt;td&gt;&lt;code&gt;id&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;TEXT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Text, such as names and cities&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;name&lt;/code&gt;, &lt;code&gt;city&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;REAL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Numbers with decimals, such as 44.43&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;latitude&lt;/code&gt;, &lt;code&gt;longitude&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;active&lt;/code&gt; column has a *&lt;strong&gt;&lt;em&gt;default&lt;/em&gt;&lt;/strong&gt;* of &lt;code&gt;1&lt;/code&gt;. That means SQLite supplies &lt;code&gt;1&lt;/code&gt; when a new row leaves this column out of the insert.&lt;/p&gt;

&lt;p&gt;A default alone also allows someone to enter another value. To restrict &lt;code&gt;active&lt;/code&gt; to &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;1&lt;/code&gt;, the table would need a *&lt;strong&gt;&lt;em&gt;constraint&lt;/em&gt;&lt;/strong&gt;*, which is a rule that data must follow. For example, &lt;code&gt;CHECK (active IN (0, 1))&lt;/code&gt; checks that the value is one of those two numbers. A separate &lt;code&gt;NOT NULL&lt;/code&gt; rule rejects a missing value.&lt;/p&gt;

&lt;p&gt;Click *&lt;strong&gt;&lt;em&gt;Show DDL&lt;/em&gt;&lt;/strong&gt;* to see the SQL that defines the &lt;code&gt;sites&lt;/code&gt; table. DDL means &lt;strong&gt;Data Definition Language&lt;/strong&gt;. This definition shows the column types and rules, such as primary keys, foreign keys, &lt;code&gt;NOT NULL&lt;/code&gt;, and default values.&lt;/p&gt;

&lt;p&gt;You can also inspect the table's *&lt;strong&gt;&lt;em&gt;indexes&lt;/em&gt;&lt;/strong&gt;*. An index is a lookup structure, a little like the index at the back of a book. It can help SQLite find certain values without reading every row. Having an index on a column does not automatically connect it to another table.&lt;/p&gt;

&lt;p&gt;SQLite is flexible with data types. In a regular table, a column marked &lt;code&gt;INTEGER&lt;/code&gt; may still accept text. The declared type tells you what the column is intended to store, but it does not always enforce that type.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understand foreign keys and join related tables
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;inspections&lt;/code&gt; table stores &lt;code&gt;site_id&lt;/code&gt; and &lt;code&gt;technician_id&lt;/code&gt; to identify where each inspection took place and who performed it. The site and technician details live in their own tables.&lt;/p&gt;

&lt;p&gt;A *&lt;strong&gt;&lt;em&gt;primary key&lt;/em&gt;&lt;/strong&gt;* identifies a specific row. For example, &lt;code&gt;sites.id&lt;/code&gt; identifies each site.&lt;/p&gt;

&lt;p&gt;A *&lt;strong&gt;&lt;em&gt;foreign key&lt;/em&gt;&lt;/strong&gt;* is a rule that connects a value in one table to a matching key in another. Here, &lt;code&gt;inspections.site_id&lt;/code&gt; refers to &lt;code&gt;sites.id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If an inspection has &lt;code&gt;site_id = 7&lt;/code&gt;, it belongs to the site whose &lt;code&gt;id&lt;/code&gt; is &lt;code&gt;7&lt;/code&gt;. You can look up that site’s name and city in &lt;code&gt;sites&lt;/code&gt;, without copying those details into every inspection row. Several inspections can have the same &lt;code&gt;site_id&lt;/code&gt; because you can inspect the same site more than once.&lt;/p&gt;

&lt;p&gt;The same idea applies to &lt;code&gt;technician_id&lt;/code&gt;: it refers to the technician’s &lt;code&gt;id&lt;/code&gt; in &lt;code&gt;technicians&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://visualeaf.com/features/visual-schema/" rel="noopener noreferrer"&gt;ER diagram&lt;/a&gt; shows these connections as lines between the tables.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn61o13tu4w7s8xiqfk27.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn61o13tu4w7s8xiqfk27.png" alt="VisuaLeaf ER diagram linking inspections.site_id to sites.id and inspections.technician_id to technicians.id, with foreign key settings and an open delete-action menu." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Foreign keys connect each inspection to its site and technician. The diagram shows these relationships and the available rules for handling deletions.&lt;/p&gt;

&lt;p&gt;A *&lt;strong&gt;&lt;em&gt;join&lt;/em&gt;&lt;/strong&gt;* uses those matching values to bring information from different tables into one result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT
    inspections.id,
    sites.name AS site,
    technicians.full_name AS technician,
    inspections.risk_score
FROM inspections
LEFT JOIN sites
    ON inspections.site_id = sites.id
LEFT JOIN technicians
    ON inspections.technician_id = technicians.id
ORDER BY inspections.id
LIMIT 10;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A name such as &lt;code&gt;sites.name&lt;/code&gt; means “the &lt;code&gt;name&lt;/code&gt; column in the &lt;code&gt;sites&lt;/code&gt; table.” Writing the table name first makes it clear which table we mean.&lt;/p&gt;

&lt;p&gt;The query starts with inspections, matches each one to its site, and then matches it to its technician. The result puts the inspection ID, both names, and the risk score together.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;LEFT JOIN&lt;/code&gt; keeps the inspection in the result even if a matching site or technician is missing. SQLite shows &lt;code&gt;NULL&lt;/code&gt; for the missing information. &lt;code&gt;NULL&lt;/code&gt; means no value. A normal inner &lt;code&gt;JOIN&lt;/code&gt; would leave out inspections without a match.&lt;/p&gt;

&lt;p&gt;A null name alone doesn't prove a record is missing: the related row could exist but have no name. Check the matching IDs before deciding what is wrong.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fae12h6mfildizaxw6qpm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fae12h6mfildizaxw6qpm.png" alt="VisuaLeaf displaying a LEFT JOIN query across inspections, sites, and technicians, with inspection IDs, site names, technician names, and risk scores in the results." width="800" height="635"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Joining three SQLite tables to show each inspection’s site, technician, and risk score.&lt;/p&gt;

&lt;p&gt;You can also create this query visually with VisuaLeaf’s &lt;a href="https://visualeaf.com/features/visual-query-builder/" rel="noopener noreferrer"&gt;Query Builder&lt;/a&gt;, without writing SQL. Select the tables, connect their matching columns, and choose which fields to include in the result.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftd11cxynttu8w01bdnlf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftd11cxynttu8w01bdnlf.png" alt="VisuaLeaf Query Builder showing LEFT JOINs from inspections to sites and technicians, a risk_score greater than 2 filter, ascending ID sorting, and query results below." width="800" height="598"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Joining tables visually in VisuaLeaf’s Query Builder, filtering for risk scores above 2, and sorting inspections by ID.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is SQLite checking the relationships?
&lt;/h3&gt;

&lt;p&gt;SQLite can have foreign keys defined while their enforcement is turned off. To check the current connection’s setting, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PRAGMA foreign_keys;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A result of &lt;code&gt;1&lt;/code&gt; means checking is on; &lt;code&gt;0&lt;/code&gt; means it is off. To enable it, run this before making changes, outside an active transaction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PRAGMA foreign_keys = ON;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setting applies to the current connection. It helps prevent new invalid references, but it does not fix existing ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Query JSON data in SQLite
&lt;/h2&gt;

&lt;p&gt;One database column can hold several related details as JSON. In our &lt;code&gt;inspections&lt;/code&gt; table, &lt;code&gt;device_info&lt;/code&gt; stores the device platform, app version, battery percentage, and whether the inspection was recorded offline.&lt;/p&gt;

&lt;p&gt;Start by viewing the original values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT id, device_info
FROM inspections
LIMIT 3;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see the result on the left of the screenshot. Each &lt;code&gt;device_info&lt;/code&gt; cell contains named values, such as &lt;code&gt;"platform":"android"&lt;/code&gt;. Here, &lt;code&gt;platform&lt;/code&gt; is the name and &lt;code&gt;android&lt;/code&gt; is its value.&lt;/p&gt;

&lt;p&gt;To display the platform separately, use &lt;code&gt;json_extract()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT
    i.id AS inspection_id,
    s.name AS site,
    json_extract(i.device_info, '$.platform') AS platform,
    json_extract(i.device_info, '$.app_version') AS app_version,
    json_extract(i.device_info, '$.battery_percent') AS battery,
    i.risk_score
FROM inspections AS i
JOIN sites AS s ON s.id = i.site_id
WHERE json_extract(i.device_info, '$.offline') = 1
ORDER BY i.risk_score DESC
LIMIT 12;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read that expression in three parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;device_info&lt;/code&gt; is the column containing the JSON.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;'$.platform'&lt;/code&gt; tells SQLite to find the value named &lt;code&gt;platform&lt;/code&gt; inside it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AS platform&lt;/code&gt; names the result column.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This creates a column in your query results. It does not add a new column to the database or change the stored JSON.&lt;/p&gt;

&lt;p&gt;The longer query on the right extracts the platform, app version, and battery percentage. It also adds the site name using a join.&lt;/p&gt;

&lt;p&gt;Its &lt;code&gt;WHERE&lt;/code&gt; condition keeps inspections where &lt;code&gt;offline&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;. SQLite returns that JSON value as &lt;code&gt;1&lt;/code&gt;. Finally, &lt;code&gt;ORDER BY ... DESC&lt;/code&gt; puts the highest risk scores first, and &lt;code&gt;LIMIT 12&lt;/code&gt; returns at most twelve rows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F16ts0rmr56l71608cbde.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F16ts0rmr56l71608cbde.png" alt="VisuaLeaf split view showing device_info JSON beside a SQLite query extracting platform, app version, and battery values for offline inspections." width="800" height="572"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Original device JSON on the left; extracted details for offline inspections on the right.&lt;/p&gt;

&lt;h2&gt;
  
  
  Export SQLite query results to Excel
&lt;/h2&gt;

&lt;p&gt;Once your query returns the information you need, you &lt;a href="https://visualeaf.com/blog/automate-mongodb-exports-imports-sync-jobs/" rel="noopener noreferrer"&gt;can export it to an Excel&lt;/a&gt; workbook. This lets you share the results with someone who doesn’t use a database tool.&lt;/p&gt;

&lt;p&gt;Open the export form for your query, then:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set *&lt;strong&gt;&lt;em&gt;Source Type&lt;/em&gt;&lt;/strong&gt;* to *&lt;strong&gt;&lt;em&gt;Query&lt;/em&gt;&lt;/strong&gt;*.&lt;/li&gt;
&lt;li&gt;Select your SQLite connection and the &lt;code&gt;main&lt;/code&gt; database.&lt;/li&gt;
&lt;li&gt;Check the SQL in the *&lt;strong&gt;&lt;em&gt;Query&lt;/em&gt;&lt;/strong&gt;* field.&lt;/li&gt;
&lt;li&gt;Set *&lt;strong&gt;&lt;em&gt;Target Type&lt;/em&gt;&lt;/strong&gt;* to *&lt;strong&gt;&lt;em&gt;Excel Workbook&lt;/em&gt;&lt;/strong&gt;*.&lt;/li&gt;
&lt;li&gt;Choose a file location and name, such as &lt;code&gt;inspections.xlsx&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Click *&lt;strong&gt;&lt;em&gt;Export Now&lt;/em&gt;&lt;/strong&gt;*.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For our JSON query, the exported columns contain the inspection ID, site, platform, app version, battery percentage, and risk score.&lt;/p&gt;

&lt;p&gt;The export follows your query’s filters and limits. If you keep &lt;code&gt;LIMIT 12&lt;/code&gt;, it exports at most twelve rows. Remove that limit if you want all matching inspections.&lt;/p&gt;

&lt;p&gt;The Excel file contains a copy of the query results. Editing it does not update your SQLite database.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6jkawm43x3ryw8l7hgcl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6jkawm43x3ryw8l7hgcl.png" alt="VisuaLeaf export form with a SQLite query as the source and inspections.xlsx as the Excel workbook destination." width="800" height="572"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Exporting SQLite query results to an Excel workbook.&lt;/p&gt;

&lt;h2&gt;
  
  
  Count and chart failed checks by category
&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;GROUP BY&lt;/code&gt; to group failed checklist items by category, then &lt;code&gt;COUNT(*)&lt;/code&gt; to count them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT
    issue_category,
    COUNT(*) AS failed_checks
FROM inspection_items
WHERE result = 'fail'
GROUP BY issue_category
ORDER BY failed_checks DESC
LIMIT 8;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The query returns up to eight categories with the most failed checks.&lt;/p&gt;

&lt;p&gt;In &lt;a href="https://visualeaf.com/features/chart-builder/" rel="noopener noreferrer"&gt;VisuaLeaf’s Chart Builder&lt;/a&gt;, choose *&lt;strong&gt;&lt;em&gt;Pie&lt;/em&gt;&lt;/strong&gt;&lt;em&gt;, set *&lt;/em&gt;&lt;strong&gt;Category&lt;/strong&gt;** to &lt;code&gt;issue_category&lt;/code&gt;, and *&lt;strong&gt;&lt;em&gt;Value&lt;/em&gt;&lt;/strong&gt;* to &lt;code&gt;failed_checks&lt;/code&gt;. Each slice shows that category’s share of the failed checks returned by the query.&lt;/p&gt;

&lt;p&gt;These totals count checklist items, not inspections. One inspection can contain several failed items. Remove &lt;code&gt;LIMIT 8&lt;/code&gt; if you want to include every category with failed checks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7est0hg824wgwa626rmh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7est0hg824wgwa626rmh.png" alt="VisuaLeaf pie chart of failed checks across eight categories, with documentation at 22.62% and electrical at 20.44%, the two largest shares." width="800" height="574"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each slice shows a category’s share of the failed checks included in the chart.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Start by opening your SQLite file and checking its tables and columns. Then use a small &lt;code&gt;SELECT&lt;/code&gt; query to read a few records before adding filters, joins, or calculations.&lt;/p&gt;

&lt;p&gt;Once the results contain what you need, you can export them to Excel or turn them into a chart. Check your filters and row limits first—they determine which data appears in the final result.&lt;/p&gt;

</description>
      <category>sql</category>
      <category>sqlite</category>
      <category>database</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>VisuaLeaf</dc:creator>
      <pubDate>Fri, 18 Sep 2026 08:52:14 +0000</pubDate>
      <link>https://dev.to/visualeaf/-449c</link>
      <guid>https://dev.to/visualeaf/-449c</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/roxana_haidiner/mongodb-sharding-manage-a-sharded-cluster-visually-1nkc" class="crayons-story__hidden-navigation-link"&gt;MongoDB Sharding: Manage a Sharded Cluster Visually&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/roxana_haidiner" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2224346%2Ff71cca7a-b282-4793-a3ae-b04d89957d47.png" alt="roxana_haidiner profile" class="crayons-avatar__image" width="512" height="512"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/roxana_haidiner" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Roxana-Maria Haidiner
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Roxana-Maria Haidiner
                
                
              
              &lt;div id="story-author-preview-content-4681989" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/roxana_haidiner" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2224346%2Ff71cca7a-b282-4793-a3ae-b04d89957d47.png" class="crayons-avatar__image" alt="" width="512" height="512"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Roxana-Maria Haidiner&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/roxana_haidiner/mongodb-sharding-manage-a-sharded-cluster-visually-1nkc" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Sep 18&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/roxana_haidiner/mongodb-sharding-manage-a-sharded-cluster-visually-1nkc" id="article-link-4681989"&gt;
          MongoDB Sharding: Manage a Sharded Cluster Visually
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/mongodb"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;mongodb&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/database"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;database&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/software"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;software&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/softwaredevelopment"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;softwaredevelopment&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/roxana_haidiner/mongodb-sharding-manage-a-sharded-cluster-visually-1nkc" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;5&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/roxana_haidiner/mongodb-sharding-manage-a-sharded-cluster-visually-1nkc#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            5 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Learn how to use DuckDB for local analysis: open a database, query CSV and Parquet files, inspect relationships, edit data, and create charts.</title>
      <dc:creator>VisuaLeaf</dc:creator>
      <pubDate>Thu, 03 Sep 2026 07:21:04 +0000</pubDate>
      <link>https://dev.to/visualeaf/learn-how-to-use-duckdb-for-local-analysis-open-a-database-query-csv-and-parquet-files-inspect-1ceo</link>
      <guid>https://dev.to/visualeaf/learn-how-to-use-duckdb-for-local-analysis-open-a-database-query-csv-and-parquet-files-inspect-1ceo</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/roxana_haidiner/how-to-use-duckdb-for-local-data-analysis-1koe" class="crayons-story__hidden-navigation-link"&gt;How to Use DuckDB for Local Data Analysis&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/roxana_haidiner" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2224346%2Ff71cca7a-b282-4793-a3ae-b04d89957d47.png" alt="roxana_haidiner profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/roxana_haidiner" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Roxana-Maria Haidiner
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Roxana-Maria Haidiner
                
                
              
              &lt;div id="story-author-preview-content-4562903" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/roxana_haidiner" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2224346%2Ff71cca7a-b282-4793-a3ae-b04d89957d47.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Roxana-Maria Haidiner&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/roxana_haidiner/how-to-use-duckdb-for-local-data-analysis-1koe" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Sep 3&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/roxana_haidiner/how-to-use-duckdb-for-local-data-analysis-1koe" id="article-link-4562903"&gt;
          How to Use DuckDB for Local Data Analysis
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/duckdb"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;duckdb&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sql"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sql&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/database"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;database&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/softwaredevelopment"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;softwaredevelopment&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/roxana_haidiner/how-to-use-duckdb-for-local-data-analysis-1koe" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;4&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/roxana_haidiner/how-to-use-duckdb-for-local-data-analysis-1koe#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            8 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>mongodump Tasks Automate and Schedule MongoDB Backups</title>
      <dc:creator>VisuaLeaf</dc:creator>
      <pubDate>Mon, 31 Aug 2026 11:00:28 +0000</pubDate>
      <link>https://dev.to/visualeaf/mongodump-tasks-automate-and-schedule-mongodb-backups-1emf</link>
      <guid>https://dev.to/visualeaf/mongodump-tasks-automate-and-schedule-mongodb-backups-1emf</guid>
      <description>&lt;p&gt;Running a MongoDB backup once is not difficult. With &lt;a href="https://visualeaf.com/database/mongodb-gui-tool/" rel="noopener noreferrer"&gt;MongoDB Database Tools&lt;/a&gt; installed, the complete PowerShell command can be as short as this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"C:\Program Files\MongoDB\Tools\100\bin\mongodump.exe"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;--uri&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"mongodb://127.0.0.1:27017"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;--db&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"gui_test_db"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;--gzip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nt"&gt;--out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"C:\MongoDBBackups\gui_test_db"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example uses an unauthenticated local MongoDB deployment. Authenticated or remote deployments also require the appropriate credentials, authentication database, TLS settings, and connection options.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;mongodump&lt;/code&gt; is MongoDB's command-line utility for creating a binary export of a database or collection. The problem is rarely the first run. It is repeating the same backup every day or week, keeping the settings consistent, using a fresh destination, and checking whether the operation actually completed.&lt;/p&gt;

&lt;p&gt;That is where a saved task becomes useful. In VisuaLeaf, the same binary dump can be configured once, run manually when needed, scheduled, and checked later from its execution history.&lt;/p&gt;

&lt;h2&gt;
  
  
  Save the Same mongodump as a VisuaLeaf Task
&lt;/h2&gt;

&lt;p&gt;Open &lt;a href="https://visualeaf.com/features/task-manager/" rel="noopener noreferrer"&gt;*&lt;strong&gt;&lt;em&gt;Task Manager&lt;/em&gt;&lt;/strong&gt;*&lt;/a&gt;, create a new job, and choose the binary dump/restore operation. Select *&lt;strong&gt;&lt;em&gt;Export to Dump&lt;/em&gt;&lt;/strong&gt;* for the operation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6jb72mohjnolhhspbz5x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6jb72mohjnolhhspbz5x.png" alt="VisuaLeaf Task Manager showing a completed mongodump task for the gui_test_db database and its four collections." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The full &lt;code&gt;gui_test_db&lt;/code&gt; database saved as a reusable mongodump task in VisuaLeaf.&lt;/p&gt;

&lt;p&gt;The task should have a name that still makes sense when you return to it later. I used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Full Database mongodump – 4.6 GB (Gzip)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is more useful than a generic name such as &lt;code&gt;Backup&lt;/code&gt; or &lt;code&gt;New Job&lt;/code&gt;. It tells me the scope, approximate source size, and whether compression is enabled.&lt;/p&gt;

&lt;p&gt;For the source, I selected the complete &lt;code&gt;gui_test_db&lt;/code&gt; database running locally on port &lt;code&gt;27017&lt;/code&gt;. Its uncompressed data size was approximately 4.6 GB, spread across four collections:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;dataset_30000&lt;/code&gt;  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;large_collection_4GB&lt;/code&gt;  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;large_docs_10mb&lt;/code&gt;  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;medium_docs_1mb&lt;/code&gt;  &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The source panel displayed the database statistics before the job ran. This gave me a quick check that I had selected the full database before creating several gigabytes of output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Back Up a Database or One Collection
&lt;/h2&gt;

&lt;p&gt;The correct scope depends on why you need the backup.&lt;/p&gt;

&lt;p&gt;Choose a *&lt;strong&gt;&lt;em&gt;collection dump&lt;/em&gt;&lt;/strong&gt;* when you only need one dataset, want to move a collection between environments, or need a smaller restore unit. Choose a *&lt;strong&gt;&lt;em&gt;database dump&lt;/em&gt;&lt;/strong&gt;* when the job should include all collections and their metadata.&lt;/p&gt;

&lt;p&gt;Be deliberate here. A successful task using the wrong source is still the wrong backup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose the Output Folder and Enable Gzip
&lt;/h2&gt;

&lt;p&gt;I saved the VisuaLeaf output in its own folder and enabled *&lt;strong&gt;&lt;em&gt;Compress with Gzip&lt;/em&gt;&lt;/strong&gt;*. A separate folder for every run keeps completed backups distinct and prevents CLI files, task files, or partial attempts from being mixed.&lt;/p&gt;

&lt;p&gt;The job summary also showed *&lt;strong&gt;&lt;em&gt;auto threads&lt;/em&gt;&lt;/strong&gt;* and *&lt;strong&gt;&lt;em&gt;no timeout&lt;/em&gt;&lt;/strong&gt;*. Auto threads leaves the worker configuration to VisuaLeaf. The gear icon opens the advanced settings if the task needs a specific thread limit or timeout. I left both settings at their defaults for this workflow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwg4lh49hkffdzq9b5mxz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwg4lh49hkffdzq9b5mxz.png" alt="VisuaLeaf Binary Dump task with gui_test_db selected as the source MongoDB database." width="800" height="683"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A full &lt;code&gt;gui_test_db&lt;/code&gt; backup configured as a binary dump task in VisuaLeaf.&lt;/p&gt;

&lt;p&gt;After choosing the source and destination, save the job. The connection, database, folder, compression, and operation settings remain attached to the task, so the next backup does not require rebuilding the command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Schedule and Monitor the MongoDB Backup
&lt;/h2&gt;

&lt;p&gt;You can still start a saved job manually, but scheduling is the main reason to turn this workflow into a task. VisuaLeaf's &lt;a href="https://visualeaf.com/docs/task-manager" rel="noopener noreferrer"&gt;Task Manager documentation&lt;/a&gt; lists one-time, hourly, daily, weekly, monthly, and custom cron schedules.&lt;/p&gt;

&lt;p&gt;Choose the frequency based on how much recent data the application could afford to lose. A weekly backup is easy to manage, but it may be inadequate for a database that changes throughout the day.&lt;/p&gt;

&lt;p&gt;Use a dated destination for recurring runs and decide how many older backups to keep. Otherwise, the task may work perfectly while the backup drive quietly runs out of space.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6c4xqo0xovbbkjc0o4xs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6c4xqo0xovbbkjc0o4xs.png" alt="VisuaLeaf Schedule Job window with a recurring daily 6 AM MongoDB backup selected." width="800" height="694"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The full database dump is scheduled to run every day at 6:00 AM.&lt;/p&gt;

&lt;p&gt;After a run, *&lt;strong&gt;&lt;em&gt;Execution History&lt;/em&gt;&lt;/strong&gt;* provides a detailed dump summary. In my test, it recorded:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Total duration, collections, documents, and database size.&lt;/li&gt;
&lt;li&gt;The thread used for each collection.&lt;/li&gt;
&lt;li&gt;Document count and size per collection.&lt;/li&gt;
&lt;li&gt;Individual collection duration and processing speed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frelq1v97ehhhcjntxjjt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frelq1v97ehhhcjntxjjt.png" alt="VisuaLeaf dump summary showing four MongoDB collections with document counts, sizes, threads, and export durations." width="800" height="567"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The completed dump included four collections, 30,449 documents, and 4.72 GB of source data.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;*&lt;strong&gt;&lt;em&gt;Performance note:&lt;/em&gt;&lt;/strong&gt;* On the same Windows machine, with Gzip enabled and the same 4.72 GB database, the VisuaLeaf task completed in 4 minutes 40 seconds. The equivalent direct &lt;code&gt;mongodump&lt;/code&gt; CLI run took 6 minutes 12 seconds, so VisuaLeaf finished about 25% faster in this test. Results may vary depending on the hardware, storage, data, and task settings.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn1y92olt2czobscn0e1u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn1y92olt2czobscn0e1u.png" alt="PowerShell result for a direct mongodump backup showing Completed, 372.31 seconds, and 3.1 GB of compressed output." width="800" height="738"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The equivalent direct &lt;code&gt;mongodump&lt;/code&gt; CLI run completed in 6 minutes and 12 seconds and produced 3.1 GB of compressed output.&lt;/p&gt;

&lt;p&gt;Before treating the exported files as a valid backup, check that the execution status is *&lt;strong&gt;&lt;em&gt;Completed&lt;/em&gt;&lt;/strong&gt;*. A cancelled or failed run may still leave partial files in the destination.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Files Does the Backup Create?
&lt;/h2&gt;

&lt;p&gt;A compressed directory dump creates one &lt;code&gt;.bson.gz&lt;/code&gt; data file and one &lt;code&gt;.metadata.json.gz&lt;/code&gt; file for each collection. The VisuaLeaf output for &lt;code&gt;gui_test_db&lt;/code&gt; contained:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dataset_30000.bson.gz
dataset_30000.metadata.json.gz
large_collection_4GB.bson.gz
large_collection_4GB.metadata.json.gz
large_docs_10mb.bson.gz
large_docs_10mb.metadata.json.gz
medium_docs_1mb.bson.gz
medium_docs_1mb.metadata.json.gz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The BSON files contain the documents. The metadata files contain collection options and index definitions used during restoration. MongoDB describes this directory structure in its official &lt;code&gt;[mongodump](https://www.mongodb.com/docs/database-tools/mongodump/)&lt;/code&gt; &lt;a href="https://www.mongodb.com/docs/database-tools/mongodump/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The 4.6 GB test database produced approximately 3.18 GB of compressed output in VisuaLeaf. That result is specific to this data. A similarly sized database containing images, encrypted values, or already compressed content may shrink much less.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr2yr84z3a4vk90hei0ec.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr2yr84z3a4vk90hei0ec.png" alt="Windows Explorer showing four bson.gz files and four metadata.json.gz files from gui_test_db, totaling 3.18 GB." width="800" height="907"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The compressed &lt;code&gt;gui_test_db&lt;/code&gt; backup contains eight BSON and metadata files totaling 3.18 GB.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can You Run mongodump in MongoDB Compass?
&lt;/h2&gt;

&lt;p&gt;No. MongoDB Compass can export an individual collection as JSON or CSV, but MongoDB explicitly says Compass is not a backup tool. It does not create a full binary database dump containing compressed BSON files and collection metadata. For that, MongoDB directs users to its separate Database Tools. See the official &lt;a href="https://www.mongodb.com/docs/compass/import-export/" rel="noopener noreferrer"&gt;Compass import and export documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftg7czp28z6f1wmfwta1o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftg7czp28z6f1wmfwta1o.png" alt="MongoDB Compass Export Data window showing JSON and CSV as the available collection export formats." width="800" height="553"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;MongoDB Compass exports collection data as JSON or CSV; it does not create a binary database backup.&lt;/p&gt;

&lt;p&gt;This is an important difference between exporting data and creating a backup. JSON can preserve MongoDB types when the correct Extended JSON format is used, while CSV can lose type information. Neither Compass export is the same workflow as a BSON database dump intended for &lt;code&gt;mongorestore&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Restore the BSON Backup in VisuaLeaf
&lt;/h2&gt;

&lt;p&gt;Creating compressed BSON files is only half of a backup test. You also need to confirm that MongoDB can restore them.&lt;/p&gt;

&lt;p&gt;To avoid changing the original &lt;code&gt;gui_test_db&lt;/code&gt; database, I created a separate VisuaLeaf restore job. Under *&lt;strong&gt;&lt;em&gt;Source Configuration&lt;/em&gt;&lt;/strong&gt;&lt;em&gt;, I selected *&lt;/em&gt;&lt;strong&gt;Restore (BSON)&lt;/strong&gt;** and chose the folder containing the &lt;code&gt;.bson.gz&lt;/code&gt; and &lt;code&gt;.metadata.json.gz&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;For the target, I selected the same MongoDB connection and entered a new database name: &lt;code&gt;gui_test_db_restore&lt;/code&gt;. Because that database did not exist, &lt;a href="https://visualeaf.com" rel="noopener noreferrer"&gt;VisuaLeaf&lt;/a&gt; indicated that it would create it when the restore job ran.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjelmskcuwpwvddmpkyws.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjelmskcuwpwvddmpkyws.png" alt="VisuaLeaf Restore BSON job using a compressed MongoDB dump folder and the new gui_test_db_restore database." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;VisuaLeaf restore job configured to import a compressed BSON dump into a new MongoDB database.&lt;/p&gt;

&lt;p&gt;This operation uses the binary dump-and-restore workflow. It reads the BSON data and collection metadata without converting the documents to JSON or CSV.&lt;/p&gt;

&lt;p&gt;When restoring to a different deployment, check version compatibility first. MongoDB requires the source and destination to use the same major version or feature compatibility version. See the official &lt;a href="https://www.mongodb.com/docs/database-tools/mongodump/mongodump-behavior/" rel="noopener noreferrer"&gt;mongodump behavior documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After the job finishes, check the restored database rather than relying only on the Completed status. The restore took 7 minutes and 34 seconds and recreated all four collections with 30,449 documents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Four collections.&lt;/li&gt;
&lt;li&gt;30,449 documents in total.&lt;/li&gt;
&lt;li&gt;30,000 documents in &lt;code&gt;dataset_30000&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;292 documents in &lt;code&gt;large_collection_4GB&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;57 documents in &lt;code&gt;large_docs_10mb&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;100 documents in &lt;code&gt;medium_docs_1mb&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The expected collection indexes and options.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fix2a93c1lfxdl27dyjiq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fix2a93c1lfxdl27dyjiq.png" alt="VisuaLeaf BSON restore summary showing four restored MongoDB collections, 30,449 documents, and a total size of 3.19 GB." width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;VisuaLeaf restored all four collections and 30,449 documents from the compressed BSON dump.&lt;/p&gt;

&lt;p&gt;The document counts matched the source, confirming that the &lt;a href="https://visualeaf.com/blog/postgresql-jsonb-vs-mongodb-bson-architectural-tradeoffs/" rel="noopener noreferrer"&gt;BSON&lt;/a&gt; files could be restored successfully. A production restore test should also verify indexes, collection options, and several real application queries.&lt;/p&gt;

&lt;p&gt;Do not restore a test dump over the only copy of your original database. Use a new database name or a separate development environment.&lt;/p&gt;

&lt;p&gt;A backup is not verified until the restore finishes successfully and the restored data matches the source.&lt;/p&gt;

&lt;h2&gt;
  
  
  If mongodump Does Not Run on Windows
&lt;/h2&gt;

&lt;p&gt;Two small Windows problems appeared during this test.&lt;/p&gt;

&lt;p&gt;If PowerShell says that &lt;code&gt;mongodump&lt;/code&gt; is not recognized, MongoDB Database Tools may be installed without its &lt;code&gt;bin&lt;/code&gt; directory being added to PATH. Using the complete path to &lt;code&gt;mongodump.exe&lt;/code&gt;, as shown in the opening command, avoids that problem.&lt;/p&gt;

&lt;p&gt;A connection error can also mean that the command is using the wrong host or port. One attempt used &lt;code&gt;localhost:27019&lt;/code&gt;, while the current MongoDB instance was listening on &lt;code&gt;27017&lt;/code&gt;. Check the connection used by VisuaLeaf or MongoDB Compass before retrying.&lt;/p&gt;

&lt;p&gt;Cancelling a dump may leave partial files in the destination. Delete those incomplete files or use a new output folder before starting another backup.&lt;/p&gt;

&lt;h2&gt;
  
  
  MongoDB Backup Limitations to Plan For
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;mongodump&lt;/code&gt; backup still needs consistency planning, safe storage, and restore testing.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Limitation&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;th&gt;What to do&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Writes continue during the dump&lt;/td&gt;
&lt;td&gt;A database-scoped dump may not represent one exact point in time.&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;--oplog&lt;/code&gt; cannot be combined with this &lt;code&gt;--db&lt;/code&gt; command; it requires a full replica-set member dump. Use another backup method when exact point-in-time consistency is required.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Collection names differ only by capitalization&lt;/td&gt;
&lt;td&gt;Directory dumps may conflict on case-insensitive Windows and macOS file systems.&lt;/td&gt;
&lt;td&gt;Avoid ambiguous collection names or use archive output with the CLI.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The backup stays on the MongoDB machine&lt;/td&gt;
&lt;td&gt;Disk failure, device loss, or ransomware could affect the database and backup together.&lt;/td&gt;
&lt;td&gt;Keep another copy on separate or off-machine storage.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The backup is never restored&lt;/td&gt;
&lt;td&gt;A Completed status and BSON files do not prove that the backup is usable.&lt;/td&gt;
&lt;td&gt;Restore into a separate database and verify document counts, indexes, and collection options.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Old backups are never removed&lt;/td&gt;
&lt;td&gt;Scheduled dumps can eventually fill the destination disk.&lt;/td&gt;
&lt;td&gt;Define a retention policy and monitor available storage space.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;MongoDB positions &lt;code&gt;mongodump&lt;/code&gt; and &lt;code&gt;mongorestore&lt;/code&gt; mainly for smaller deployments; larger or business-critical systems may require a different backup strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;A reliable MongoDB backup workflow does not end when files appear in a folder. It ends when those files can be restored, and the resulting database has been verified.&lt;/p&gt;

&lt;p&gt;The command line remains useful for one-off dumps and scripts. In VisuaLeaf, you can &lt;a href="https://visualeaf.com/blog/automate-mongodb-exports-imports-sync-jobs/" rel="noopener noreferrer"&gt;save both export and restore jobs&lt;/a&gt;, schedule repeated backups, and review their status, duration, history, and logs. You still need off-machine copies, retention rules, consistency planning, and restore tests, but the recurring steps are easier to manage from one place.&lt;/p&gt;

</description>
      <category>mongodb</category>
      <category>nosql</category>
      <category>database</category>
      <category>sql</category>
    </item>
    <item>
      <title>PostgreSQL JSONB: Query, Update, and Index JSON Data</title>
      <dc:creator>VisuaLeaf</dc:creator>
      <pubDate>Tue, 18 Aug 2026 11:20:25 +0000</pubDate>
      <link>https://dev.to/visualeaf/postgresql-jsonb-query-update-and-index-json-data-1h21</link>
      <guid>https://dev.to/visualeaf/postgresql-jsonb-query-update-and-index-json-data-1h21</guid>
      <description>&lt;p&gt;JSONB allows you to store JSON data within a PostgreSQL table row. It is suitable when certain columns remain unchanged, while the remaining columns may vary across rows.&lt;/p&gt;

&lt;p&gt;Let us take the support tickets table, where the status, priority, and the date a ticket was created cannot change. However, the client name, environment, tags, and even error details may be optional and have different formats. That is why the optional content may be stored in the &lt;a href="https://visualeaf.com/blog/postgresql-jsonb-vs-mongodb-bson-architectural-tradeoffs/" rel="noopener noreferrer"&gt;JSONB field&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This storage process is easy enough. However, you need answers to questions such as how to find a nested value, update a single field, or create an effective index for your database engine.&lt;/p&gt;

&lt;p&gt;This article explores how to achieve that goal using a support_tickets table and the corresponding details column.&lt;/p&gt;

&lt;h2&gt;
  
  
  PostgreSQL JSONB operations used in this guide
&lt;/h2&gt;

&lt;p&gt;We’ll use each of these against the same &lt;code&gt;support_tickets.details&lt;/code&gt; column, so you can see how querying, updating, and indexing work together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create the support tickets table
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE support_tickets (
    ticket_id  BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    status     TEXT NOT NULL,
    priority   TEXT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    details    JSONB NOT NULL
        CHECK (jsonb_typeof(details) = 'object')
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I kept &lt;code&gt;status&lt;/code&gt;, &lt;code&gt;priority&lt;/code&gt;, and &lt;code&gt;created_at&lt;/code&gt; outside JSONB because they have stable types and are useful for filtering and sorting. The less predictable ticket context goes into &lt;code&gt;details&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here is one record used in the test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;INSERT INTO support_tickets (status, priority, details)
VALUES (
    'open',
    'high',
    '{
      "customer": {
        "name": "Trevor Lisbon",
        "plan": "Professional"
      },
      "environment": {
        "browser": "Chrome",
        "os": "Windows 11",
        "appVersion": "4.8.2"
      },
      "tags": ["sync", "postgresql"],
      "error": {
        "code": "SYNC_TIMEOUT",
        "retryable": true
      }
    }'::jsonb
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;CHECK&lt;/code&gt; constraint confirms that &lt;code&gt;details&lt;/code&gt; contains a JSON object. It does not guarantee that &lt;code&gt;customer.plan&lt;/code&gt; exists or that &lt;code&gt;tags&lt;/code&gt; is always an array. JSONB validates the JSON format, not your complete application schema.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5r6vnci4oj9i1yjtyrrj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5r6vnci4oj9i1yjtyrrj.png" alt="VisuaLeaf displaying a PostgreSQL support_tickets table with expanded customer and tags fields from a JSONB column." width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PostgreSQL table with regular columns and nested JSONB data displayed in VisuaLeaf.&lt;/p&gt;

&lt;h2&gt;
  
  
  Query nested JSONB values
&lt;/h2&gt;

&lt;p&gt;PostgreSQL provides two operators that look similar but return different data types:&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;-&amp;gt;&lt;/code&gt; when you want an object or array to remain JSONB.&lt;br&gt;&lt;br&gt;
Use &lt;code&gt;-&amp;gt;&amp;gt;&lt;/code&gt; when you need a scalar value for an ordinary SQL comparison.&lt;/p&gt;

&lt;p&gt;This query returns the complete customer object and extracts the plan as text:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT
    ticket_id,
    details -&amp;gt; 'customer' AS customer,
    details -&amp;gt; 'customer' -&amp;gt;&amp;gt; 'plan' AS plan
FROM support_tickets
WHERE ticket_id &amp;lt;= 4
ORDER BY ticket_id;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftkimn4ihws1k3zhnv7yw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftkimn4ihws1k3zhnv7yw.gif" alt="VisuaLeaf showing an expandable customer JSONB object and the customer plan returned as text in PostgreSQL." width="800" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Expanding the &lt;code&gt;customer&lt;/code&gt; JSONB object while &lt;code&gt;plan&lt;/code&gt; is returned as text.&lt;/p&gt;

&lt;p&gt;A common mistake is to use &lt;code&gt;-&amp;gt;&lt;/code&gt; for the final value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WHERE details -&amp;gt; 'customer' -&amp;gt; 'plan' = 'Professional'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The left side is JSONB, while &lt;code&gt;Professional&lt;/code&gt; is being treated as a SQL string. PostgreSQL may return:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;invalid input syntax for type json
Token "Professional" is invalid.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The clearer fix is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WHERE details -&amp;gt; 'customer' -&amp;gt;&amp;gt; 'plan' = 'Professional'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To filter by a nested value, use &lt;code&gt;-&amp;gt;&amp;gt;&lt;/code&gt; for the final part of the path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT
    ticket_id,
    status,
    details -&amp;gt; 'customer' -&amp;gt;&amp;gt; 'name' AS customer_name
FROM support_tickets
WHERE details -&amp;gt; 'error' -&amp;gt;&amp;gt; 'code' = 'SYNC_TIMEOUT';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnmgp4il1nssvm2uadivi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnmgp4il1nssvm2uadivi.png" alt="VisuaLeaf SQL editor querying nested PostgreSQL JSONB fields with arrow operators and returning matching customer names." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Filtering tickets by a nested JSONB error code and returning the customer name as text.&lt;/p&gt;

&lt;p&gt;If a row has no &lt;code&gt;error&lt;/code&gt; object, PostgreSQL returns SQL &lt;code&gt;NULL&lt;/code&gt; for that expression. It does not fail.&lt;/p&gt;

&lt;p&gt;Search inside a JSONB array&lt;/p&gt;

&lt;p&gt;You can search the &lt;code&gt;tags&lt;/code&gt; array with the &lt;code&gt;?&lt;/code&gt; operator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT ticket_id, status, details -&amp;gt; 'tags' AS tags
FROM support_tickets
WHERE details -&amp;gt; 'tags' ? 'postgresql';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This assumes that &lt;code&gt;tags&lt;/code&gt; contains an array of strings. If some rows store &lt;code&gt;"tags": "postgresql"&lt;/code&gt; instead, the data is valid JSONB but has the wrong shape for this query.&lt;/p&gt;

&lt;h2&gt;
  
  
  Match part of a document with &lt;code&gt;@&amp;gt;&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The containment operator checks whether one JSONB value contains another:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT ticket_id, status, details -&amp;gt; 'error' AS error
FROM support_tickets
WHERE details @&amp;gt; '{
  "error": {
    "code": "SYNC_TIMEOUT"
  }
}'::jsonb;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwd1e6puk0exx6iwo0jvf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwd1e6puk0exx6iwo0jvf.png" alt="VisuaLeaf running a PostgreSQL JSONB containment query and displaying two matching support tickets with expanded error details." width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;@&amp;gt;&lt;/code&gt; to find tickets containing the &lt;code&gt;SYNC_TIMEOUT&lt;/code&gt; error object.&lt;/p&gt;

&lt;p&gt;The structure must match the document. This does not work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WHERE details @&amp;gt; '{"code": "SYNC_TIMEOUT"}'::jsonb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;code&lt;/code&gt; is nested under &lt;code&gt;error&lt;/code&gt;; it is not a top-level key. This matters when you add a GIN index, because containment queries can use that index directly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Update a nested value with &lt;code&gt;jsonb_set&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Ticket 1 was reproduced in version &lt;code&gt;4.8.4&lt;/code&gt;. Instead of replacing the entire &lt;code&gt;details&lt;/code&gt; document, I updated only &lt;code&gt;environment.appVersion&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;support_tickets&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;details&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jsonb_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;details&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'{environment,appVersion}'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;to_jsonb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'4.8.4'&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="k"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;ticket_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="n"&gt;RETURNING&lt;/span&gt;
    &lt;span class="n"&gt;ticket_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;details&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'environment'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result shows &lt;code&gt;appVersion&lt;/code&gt; as &lt;code&gt;4.8.4&lt;/code&gt;, while &lt;code&gt;browser&lt;/code&gt; and &lt;code&gt;os&lt;/code&gt; remain unchanged.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhtaz3o7ae29s2aa7d2gq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhtaz3o7ae29s2aa7d2gq.png" alt="VisuaLeaf running jsonb_set to update a nested PostgreSQL JSONB value from version 4.8.2 to 4.8.4." width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Updating &lt;code&gt;environment.appVersion&lt;/code&gt; while preserving the other JSONB fields.&lt;/p&gt;

&lt;p&gt;The final &lt;code&gt;false&lt;/code&gt; means the key must already exist. The &lt;code&gt;::text&lt;/code&gt; cast is also important; without it, PostgreSQL may not know which type to convert:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;could not determine polymorphic type because input has type unknown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  When the parent object is missing
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;jsonb_set&lt;/code&gt; can create the final key, but not a missing parent object. Ticket 3 has no &lt;code&gt;error&lt;/code&gt; object, so updating &lt;code&gt;error.firstSeenAt&lt;/code&gt; would run without an error but change nothing.&lt;/p&gt;

&lt;p&gt;This version creates the parent when needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;support_tickets&lt;/span&gt;
&lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;details&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jsonb_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;details&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'{error}'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;COALESCE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;details&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'error'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'{}'&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;jsonb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;jsonb_build_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s1"&gt;'firstSeenAt'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'2026-08-03T11:05:00Z'&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="k"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;ticket_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;
&lt;span class="n"&gt;RETURNING&lt;/span&gt; &lt;span class="n"&gt;details&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'error'&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;RETURNING&lt;/code&gt; lets you confirm that the field was actually added.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fudm1j9vsulslb1oudb1l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fudm1j9vsulslb1oudb1l.png" alt="JSONB error object with the new firstSeenAt field." width="799" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Creating the missing &lt;code&gt;error&lt;/code&gt; object.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add a GIN index for containment searches
&lt;/h2&gt;

&lt;p&gt;For the indexing test, I added 40,000 generated tickets. The table contained 40,004 rows in total, while the original four remained available for the earlier examples.&lt;/p&gt;

&lt;p&gt;Because the query uses &lt;code&gt;@&amp;gt;&lt;/code&gt; against &lt;code&gt;details&lt;/code&gt;, I created a containment-focused GIN index:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_support_tickets_details_gin&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;support_tickets&lt;/span&gt;
&lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;GIN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;details&lt;/span&gt; &lt;span class="n"&gt;jsonb_path_ops&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;ANALYZE&lt;/span&gt; &lt;span class="n"&gt;support_tickets&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;jsonb_path_ops&lt;/code&gt; works well for containment searches, but it does not support every JSONB operator. For example, it cannot support the &lt;code&gt;?&lt;/code&gt; operator used in the tags query.&lt;/p&gt;

&lt;p&gt;I then opened the same query in VisuaLeaf’s Explain view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;ticket_id&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;support_tickets&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;details&lt;/span&gt; &lt;span class="o"&gt;@&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'{
  "error": {
    "code": "SYNC_TIMEOUT"
  }
}'&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;jsonb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PostgreSQL found 402 matching tickets and used &lt;code&gt;idx_support_tickets_details_gin&lt;/code&gt; through a Bitmap Index Scan.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F93iz2oa3p9jho2gfgrvn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F93iz2oa3p9jho2gfgrvn.png" alt="VisuaLeaf Explain showing a PostgreSQL Bitmap Index Scan using a JSONB GIN index." width="800" height="605"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The JSONB query uses the GIN index instead of scanning the full table.&lt;/p&gt;

&lt;p&gt;The index worked for this query and dataset. PostgreSQL may still choose a sequential scan for a small table or when many rows match.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use JSONB
&lt;/h2&gt;

&lt;p&gt;JSONB is useful for flexible data, but stable fields usually belong in regular columns.&lt;/p&gt;

&lt;p&gt;In this example, &lt;code&gt;environment&lt;/code&gt; and &lt;code&gt;error&lt;/code&gt; fit inside JSONB. Fields such as &lt;code&gt;status&lt;/code&gt;, &lt;code&gt;priority&lt;/code&gt;, and &lt;code&gt;created_at&lt;/code&gt; are better as regular columns.&lt;/p&gt;

&lt;p&gt;I ran these examples in &lt;a href="https://visualeaf.com/database/postgresql/" rel="noopener noreferrer"&gt;VisuaLeaf&lt;/a&gt;, using its SQL editor, nested JSONB table view, and visual query plan to inspect the results.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.postgresql.org/docs/current/datatype-json.html" rel="noopener noreferrer"&gt;PostgreSQL: JSON Types&lt;/a&gt;  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.postgresql.org/docs/current/functions-json.html" rel="noopener noreferrer"&gt;PostgreSQL: JSON Functions and Operators&lt;/a&gt;  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.postgresql.org/docs/current/using-explain.html" rel="noopener noreferrer"&gt;PostgreSQL: Using EXPLAIN&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>postgres</category>
      <category>postgressql</category>
      <category>database</category>
      <category>sql</category>
    </item>
    <item>
      <title>MongoDB Schema Design: Build or Reverse Engineer Visually</title>
      <dc:creator>VisuaLeaf</dc:creator>
      <pubDate>Thu, 06 Aug 2026 11:45:16 +0000</pubDate>
      <link>https://dev.to/visualeaf/mongodb-schema-design-build-or-reverse-engineer-visually-5d28</link>
      <guid>https://dev.to/visualeaf/mongodb-schema-design-build-or-reverse-engineer-visually-5d28</guid>
      <description>&lt;p&gt;MongoDB collections can become difficult to understand once they contain nested objects, arrays, references, and documents with slightly different structures.&lt;/p&gt;

&lt;p&gt;Looking at one JSON document at a time shows you the data, but not the complete model. It is harder to see how collections connect, which fields use inconsistent BSON types, or whether a value is embedded or referenced elsewhere.&lt;/p&gt;

&lt;p&gt;The opposite problem happens when the database does not exist yet. You may know which collections and fields you need, but reviewing the design across separate JSON files or application models is not easy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://visualeaf.com/features/visual-schema/" rel="noopener noreferrer"&gt;VisuaLeaf’s Visual Schema&lt;/a&gt; supports both directions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate a diagram from an existing MongoDB database.&lt;/li&gt;
&lt;li&gt;Design a new schema from an empty diagram.&lt;/li&gt;
&lt;li&gt;Keep the schema virtual or materialize it into real collections.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I tested both workflows using a travel-planning database containing collections such as &lt;code&gt;travelers&lt;/code&gt;, &lt;code&gt;places&lt;/code&gt;, &lt;code&gt;flights&lt;/code&gt;, &lt;code&gt;accommodations&lt;/code&gt;, &lt;code&gt;restaurants&lt;/code&gt;, &lt;code&gt;activities&lt;/code&gt;, &lt;code&gt;carRentals&lt;/code&gt;, and &lt;code&gt;vacationPlans&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3df2kfd2ac64dpf9s0rn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3df2kfd2ac64dpf9s0rn.png" alt="MongoDB Visual Schema in VisuaLeaf showing the central vacationPlans collection connected to travelers, flights, accommodations, restaurants, car rentals, activities, and places." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;VisuaLeaf generates a visual MongoDB schema showing collections, fields, BSON types, and relationships in the &lt;code&gt;TravelPlannerDemo&lt;/code&gt; database.&lt;/p&gt;

&lt;p&gt;Although this guide focuses on MongoDB, VisuaLeaf’s Visual Schema can also be used with &lt;a href="https://visualeaf.com/database/" rel="noopener noreferrer"&gt;SQL databases.&lt;/a&gt; If you’re deciding which database model fits your project or want to compare their structures, read &lt;a href="https://visualeaf.com/blog/mongodb-vs-postgresql-whats-the-difference/" rel="noopener noreferrer"&gt;MongoDB vs PostgreSQL: What’s the Difference?&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reverse engineer an existing MongoDB database
&lt;/h3&gt;

&lt;p&gt;If your database already contains data, you can generate a Visual Schema from its collections.&lt;/p&gt;

&lt;p&gt;Connect to MongoDB, open *&lt;strong&gt;&lt;em&gt;Visual Schema&lt;/em&gt;&lt;/strong&gt;*, and select the database you want to analyze. VisuaLeaf reads the existing documents and creates a diagram containing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;collections and fields;&lt;/li&gt;
&lt;li&gt;detected BSON types;&lt;/li&gt;
&lt;li&gt;embedded objects;&lt;/li&gt;
&lt;li&gt;arrays and their nested fields;&lt;/li&gt;
&lt;li&gt;detected relationships between collections.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In my &lt;code&gt;TravelPlannerDemo&lt;/code&gt; database, the generated diagram included the main travel collections and their nested structures.&lt;/p&gt;

&lt;p&gt;For example, &lt;code&gt;vacationPlans&lt;/code&gt; contains simple fields such as &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;status&lt;/code&gt;, and &lt;code&gt;createdAt&lt;/code&gt;, but it also includes more complex structures:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;budget&lt;/code&gt; and &lt;code&gt;dates&lt;/code&gt; as embedded objects;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;itinerary&lt;/code&gt;, &lt;code&gt;checklist&lt;/code&gt;, and &lt;code&gt;documents&lt;/code&gt; as arrays;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;destinationIds&lt;/code&gt; and &lt;code&gt;travelerIds&lt;/code&gt; as arrays of references;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ownerTravelerId&lt;/code&gt; and &lt;code&gt;selectedAccommodationId&lt;/code&gt; as individual references.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of opening several documents to understand how these fields fit together, I could inspect the structure in one diagram.&lt;/p&gt;

&lt;p&gt;This is particularly useful when you are working with a database you did not design, or when the structure has changed, but its documentation has not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Review the detected structure
&lt;/h2&gt;

&lt;p&gt;Here’s the trap most of us fall into: reverse-engineering a database shows what’s currently stored, not what was actually intended.&lt;/p&gt;

&lt;p&gt;MongoDB allows documents in the same collection to have different fields and BSON types. For example, most documents might store a date correctly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ISODate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2026-07-18T09:30:00Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;while an older import stores it as a string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2026-07-18&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These values may look similar in a table, but one has the BSON type &lt;code&gt;date&lt;/code&gt; and the other has the type &lt;code&gt;string&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The same problem can occur with identifiers. A field such as &lt;code&gt;ownerTravelerId&lt;/code&gt; may contain an &lt;code&gt;ObjectId&lt;/code&gt; in most documents but a string in others. That difference matters when you try to connect it to &lt;code&gt;travelers._id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The generated schema represents the data VisuaLeaf analyzed. Rare fields may also be absent if they do not appear in the sampled documents. Review unusual records and any fields with multiple detected types before using the diagram as your final model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Review and add relationships
&lt;/h2&gt;

&lt;p&gt;MongoDB does not store relationships as enforced foreign keys. It stores the reference value, but the meaning of that value often needs to be confirmed.&lt;/p&gt;

&lt;p&gt;In the travel database, relationships can connect fields such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vacationPlans.ownerTravelerId → travelers._id
vacationPlans.travelerIds[] → travelers._id
vacationPlans.destinationIds[] → places._id
vacationPlans.selectedAccommodationId → accommodations._id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Other collections use similar connections:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;accommodations.placeId → places._id
restaurants.placeId → places._id
activities.placeId → places._id
flights.passengers.travelerId → travelers._id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check both ends before accepting or adding a relationship. An &lt;code&gt;ObjectId&lt;/code&gt; does not match a string containing the same characters.&lt;/p&gt;

&lt;p&gt;For example, these two values use different BSON types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nc"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;66af9138ad73c91224345c10&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;66af9138ad73c91224345c10&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The diagram can document a relationship, but it does not make MongoDB enforce it. Your application can still insert an &lt;code&gt;ownerTravelerId&lt;/code&gt; that does not exist in &lt;code&gt;travelers&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The generated diagram may also need some visual cleanup. In a database with many related collections, lines can overlap and detected connections may not represent the relationship you intended. Reposition the collections and review each relationship rather than accepting every connection without checking.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdlhmsx6fe1ms6dl1ndug.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdlhmsx6fe1ms6dl1ndug.png" alt="Close-up of a MongoDB schema diagram in VisuaLeaf showing nested fields, BSON types, arrays, and relationships between vacation plans, places, and accommodations." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A closer look at nested fields, BSON types, arrays, and relationships between&lt;/strong&gt; &lt;em&gt;`_vacationPlans&lt;/em&gt;&lt;code&gt;___,__ _&lt;/code&gt;&lt;em&gt;places&lt;/em&gt;&lt;code&gt;___, and__ _&lt;/code&gt;&lt;em&gt;accommodations&lt;/em&gt;`_ &lt;strong&gt;in VisuaLeaf.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Modify the generated schema
&lt;/h2&gt;

&lt;p&gt;A reverse-engineered schema does not have to remain a read-only picture of the current database.&lt;/p&gt;

&lt;p&gt;You can use it as a starting point and then:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add or remove fields;&lt;/li&gt;
&lt;li&gt;correct BSON types;&lt;/li&gt;
&lt;li&gt;define embedded objects and arrays;&lt;/li&gt;
&lt;li&gt;add missing relationships;&lt;/li&gt;
&lt;li&gt;remove incorrect relationships;&lt;/li&gt;
&lt;li&gt;reorganize the diagram.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this example, I added a new optional &lt;code&gt;travelStyle&lt;/code&gt; field to &lt;code&gt;vacationPlans&lt;/code&gt;. I also manually connected &lt;code&gt;destinationIds&lt;/code&gt; to &lt;code&gt;places._id&lt;/code&gt; to represent the reference between the two collections.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhvmgwyzbmksmydxhiroc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhvmgwyzbmksmydxhiroc.png" alt="Editing the vacationPlans MongoDB schema in VisuaLeaf by adding the optional travelStyle field with the String BSON type." width="800" height="523"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The same collection contains embedded data as well. The &lt;code&gt;budget&lt;/code&gt; object belongs to one vacation plan, so storing it inside the document keeps the related values together. &lt;code&gt;destinationIds&lt;/code&gt;, however, contains references to documents stored in the &lt;code&gt;places&lt;/code&gt; collection.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;vacationPlans
├── budget
│   ├── currency
│   ├── estimatedTotal
│   └── paidSoFar
├── dates
├── itinerary[]
├── ownerTravelerId
├── travelerIds[]
└── destinationIds[]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This shows both modeling approaches in the same collection: embedded data for information owned by the vacation plan and references for information stored elsewhere and reused.&lt;/p&gt;

&lt;p&gt;Changing the virtual schema does not automatically migrate existing documents. If you rename a field, change its type, or move it into an embedded object, the records already stored in MongoDB remain unchanged.&lt;/p&gt;

&lt;p&gt;The diagram describes the proposed model. Data migration remains a separate operation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design a new MongoDB schema from scratch
&lt;/h2&gt;

&lt;p&gt;You can also use Visual Schema before the database exists.&lt;/p&gt;

&lt;p&gt;Start with an empty diagram and add the collections you need. You can then define their fields, BSON types, embedded structures, arrays, and relationships.&lt;/p&gt;

&lt;p&gt;For this example, I created a small tour-booking schema with two collections:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Collection&lt;/th&gt;
&lt;th&gt;Example fields&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;departures&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;_id&lt;/code&gt;, &lt;code&gt;packageId&lt;/code&gt;, &lt;code&gt;startDate&lt;/code&gt;, &lt;code&gt;capacity&lt;/code&gt;, &lt;code&gt;meetingPoint&lt;/code&gt;, &lt;code&gt;status&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;reservations&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;_id&lt;/code&gt;, &lt;code&gt;departureId&lt;/code&gt;, &lt;code&gt;bookingReference&lt;/code&gt;, &lt;code&gt;leadGuest&lt;/code&gt;, &lt;code&gt;totalAmount&lt;/code&gt;, &lt;code&gt;paymentStatus&lt;/code&gt;, &lt;code&gt;createdAt&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;leadGuest&lt;/code&gt; field is an embedded object containing fields such as &lt;code&gt;fullName&lt;/code&gt; and &lt;code&gt;email&lt;/code&gt;. The relationship connects:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;reservations.departureId → departures._id&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This represents a one-to-many relationship: one departure can have multiple reservations.&lt;/p&gt;

&lt;p&gt;Defining the schema visually makes several problems easier to notice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;references pointing to the wrong field;&lt;/li&gt;
&lt;li&gt;incorrect BSON types;&lt;/li&gt;
&lt;li&gt;fields that should be grouped inside an embedded object;&lt;/li&gt;
&lt;li&gt;missing or unnecessary relationships;&lt;/li&gt;
&lt;li&gt;inconsistent field names between collections.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhebkwgkol5au017x1dvf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhebkwgkol5au017x1dvf.png" alt="VisuaLeaf visual schema showing departures and reservations connected through departures._id and reservations.departureId, with BSON types and an embedded leadGuest object." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After completing the design, save the schema and bind it to the MongoDB connection where you want to create the collections. The diagram remains a virtual model until you materialize it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the schema virtual or materialize it
&lt;/h2&gt;

&lt;p&gt;The diagram can remain virtual if you only need it for documentation, planning, or discussing changes with your team.&lt;/p&gt;

&lt;p&gt;Nothing is created in MongoDB until you choose to materialize it.&lt;/p&gt;

&lt;p&gt;When the design is ready, save it and bind it to the MongoDB connection and target database. You can then click *&lt;strong&gt;&lt;em&gt;Materialize&lt;/em&gt;&lt;/strong&gt;* to review the collections and generated validators before applying the schema.&lt;/p&gt;

&lt;p&gt;This allows both workflows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Existing MongoDB database → Visual Schema
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Visual Schema → MongoDB database
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can therefore reverse engineer an existing database such as &lt;code&gt;TravelPlannerDemo&lt;/code&gt;, modify its virtual schema, and materialize the result into a separate test database.  &lt;/p&gt;

&lt;p&gt;You can also begin with an empty diagram, as I did with &lt;code&gt;TourBookingSchema&lt;/code&gt;, and create a new database from that design.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fulny3425f5e17xilwyqe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fulny3425f5e17xilwyqe.png" alt="VisuaLeaf Materialize screen showing the reservations collection, its BSON fields, embedded leadGuest object, generated MongoDB validator, and Apply to Database button." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose whether to attach validators
&lt;/h2&gt;

&lt;p&gt;Materializing a schema does not require you to enforce every field and BSON type.&lt;/p&gt;

&lt;p&gt;The materialization screen lets you review the JSON Schema generated for each collection. You can then decide whether MongoDB should use it as a &lt;code&gt;$jsonSchema&lt;/code&gt; validator.&lt;/p&gt;

&lt;p&gt;If you want to create the collections without enforcing the schema, leave *&lt;strong&gt;&lt;em&gt;Attach validator&lt;/em&gt;&lt;/strong&gt;* disabled.&lt;/p&gt;

&lt;p&gt;These are separate decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The visual schema describes the intended structure.&lt;/li&gt;
&lt;li&gt;Materialization creates the collections in MongoDB.&lt;/li&gt;
&lt;li&gt;The generated JSON Schema describes the field and type rules.&lt;/li&gt;
&lt;li&gt;The attached validator makes MongoDB enforce those rules during writes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A field is mandatory only if it is included in the schema’s &lt;code&gt;required&lt;/code&gt; array. Otherwise, its BSON type is checked only when the field is present.&lt;/p&gt;

&lt;p&gt;This distinction matters when your application still writes inconsistent data.&lt;/p&gt;

&lt;p&gt;Suppose the generated schema expects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;bsonType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;date&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the application sends this instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2026-08-03&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MongoDB will reject the write when the validator is attached. A document validation failure commonly returns MongoDB error code &lt;code&gt;121&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The solution is not necessarily to remove validation. First check what your application actually sends. You may need to fix the value before inserting it or adjust the schema if both types are intentionally supported.&lt;/p&gt;

&lt;h2&gt;
  
  
  Review the materialization plan
&lt;/h2&gt;

&lt;p&gt;Before clicking *&lt;strong&gt;&lt;em&gt;Apply to Database&lt;/em&gt;&lt;/strong&gt;*, check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the selected connection and database;&lt;/li&gt;
&lt;li&gt;which collections will be created;&lt;/li&gt;
&lt;li&gt;which collections already exist;&lt;/li&gt;
&lt;li&gt;collection and field names;&lt;/li&gt;
&lt;li&gt;BSON types;&lt;/li&gt;
&lt;li&gt;embedded objects and arrays;&lt;/li&gt;
&lt;li&gt;relationships in the visual diagram;&lt;/li&gt;
&lt;li&gt;generated JSON Schemas;&lt;/li&gt;
&lt;li&gt;which collections have the *&lt;strong&gt;&lt;em&gt;Attach validator&lt;/em&gt;&lt;/strong&gt;* enabled.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the first test, use a new database instead of applying the model directly to an existing one.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TourBookingDemo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes the result easier to inspect and avoids mixing the materialized collections with the original &lt;code&gt;TravelPlannerDemo&lt;/code&gt; data.&lt;/p&gt;

&lt;p&gt;If you materialize changes against a database that already contains records, changing &lt;code&gt;reservations.departureId&lt;/code&gt; from &lt;code&gt;String&lt;/code&gt; to &lt;code&gt;ObjectId&lt;/code&gt; in the diagram will not convert the stored values. Data migration remains a separate operation.&lt;/p&gt;

&lt;p&gt;It also does not create enforced foreign keys. The relationship between &lt;code&gt;reservations.departureId&lt;/code&gt; and &lt;code&gt;departures._id&lt;/code&gt; remains part of the model, but your application must maintain valid references.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0sve8uozl6kk4og7t34z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0sve8uozl6kk4og7t34z.png" alt="VisuaLeaf showing the newly created departures and reservations collections in MongoDB." width="800" height="486"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Verify the result
&lt;/h2&gt;

&lt;p&gt;After applying the materialization plan, open the target database and confirm that the &lt;code&gt;departures&lt;/code&gt; and &lt;code&gt;reservations&lt;/code&gt; collections were created.&lt;/p&gt;

&lt;p&gt;If you enabled *&lt;strong&gt;&lt;em&gt;Attach validator&lt;/em&gt;&lt;/strong&gt;*, inspect the validation settings and test them with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one valid document that should be accepted;&lt;/li&gt;
&lt;li&gt;one invalid document that should be rejected.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frhcezatzy0lhnfk0b1zh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frhcezatzy0lhnfk0b1zh.png" alt="Split-screen MongoDB shell in VisuaLeaf showing a valid reservation inserted successfully and an invalid reservation rejected by JSON Schema validation because several BSON types do not match." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you left the option disabled, the collections should still exist, but MongoDB will continue accepting documents with different structures.&lt;/p&gt;

&lt;p&gt;Also remember that a validator does not replace indexes. Fields used for lookups and filtering may still need indexes, such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reservations&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createIndex&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;departureId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;departures&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createIndex&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;startDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Validators control document structure. Indexes support query performance and can enforce uniqueness when created with the &lt;code&gt;unique&lt;/code&gt; option.&lt;br&gt;&lt;br&gt;
Relationships explain how collections connect, but MongoDB does not enforce them as foreign keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  One schema, two directions
&lt;/h2&gt;

&lt;p&gt;Visual Schema is useful when you inherit an existing MongoDB database and need to understand its structure. It is also useful before you create the first collection.&lt;/p&gt;

&lt;p&gt;In my travel-planning example, I could generate a diagram containing &lt;code&gt;travelers&lt;/code&gt;, &lt;code&gt;places&lt;/code&gt;, &lt;code&gt;flights&lt;/code&gt;, &lt;code&gt;accommodations&lt;/code&gt;, &lt;code&gt;activities&lt;/code&gt;, &lt;code&gt;vacationPlans&lt;/code&gt;, and the other related collections. I could inspect nested structures such as &lt;code&gt;budget&lt;/code&gt; and &lt;code&gt;itinerary&lt;/code&gt;, review the references, and correct the model where necessary.&lt;/p&gt;

&lt;p&gt;The same workspace also supports the opposite workflow. You can start with an empty diagram, design the collections visually, and then decide whether to keep the model virtual or materialize it into MongoDB.&lt;/p&gt;

&lt;p&gt;When you materialize it, attaching the generated &lt;a href="https://visualeaf.com/blog/mongodb-json-schema-validation/" rel="noopener noreferrer"&gt;JSON Schema validators&lt;/a&gt; remains optional.&lt;/p&gt;

&lt;p&gt;*&lt;strong&gt;&lt;em&gt;Want to turn your MongoDB database into a diagram—or your diagram into a database?&lt;/em&gt;&lt;/strong&gt;*&lt;/p&gt;

&lt;p&gt;&lt;a href="https://visualeaf.com/download" rel="noopener noreferrer"&gt;Download VisuaLeaf&lt;/a&gt; and generate a schema from your existing MongoDB database, or design a new one from scratch.&lt;/p&gt;

</description>
      <category>mongodb</category>
      <category>database</category>
      <category>javascript</category>
      <category>software</category>
    </item>
    <item>
      <title>Which MongoDB Client Handles Large Collections Best: VisuaLeaf vs Compass vs Studio 3T</title>
      <dc:creator>VisuaLeaf</dc:creator>
      <pubDate>Tue, 04 Aug 2026 07:56:19 +0000</pubDate>
      <link>https://dev.to/visualeaf/which-mongodb-client-handles-large-collections-best-visualeaf-vs-compass-vs-studio-3t-1jam</link>
      <guid>https://dev.to/visualeaf/which-mongodb-client-handles-large-collections-best-visualeaf-vs-compass-vs-studio-3t-1jam</guid>
      <description>&lt;p&gt;A MongoDB client can feel fast with a few hundred documents and become frustrating as soon as the result grows.&lt;/p&gt;

&lt;p&gt;The database is not always the problem. MongoDB may finish the query while the desktop client is still fetching batches, converting BSON values, and building thousands of rows or JSON blocks.&lt;/p&gt;

&lt;p&gt;I tested VisuaLeaf, MongoDB Compass, and Studio 3T with the same collection of 50,844 documents. Then I tested three related workflows: comparing collections, automating repeated work, and investigating an unindexed query.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test setup
&lt;/h2&gt;

&lt;p&gt;I ran all three applications on the same HP laptop with 16 GB of RAM and Windows 11 Pro. Each application was updated to the latest version available when I ran the test. All three connected to the same MongoDB database through the same internet connection and opened the same &lt;code&gt;largeCollection&lt;/code&gt;. The collection contained nested objects and arrays, not only flat fields.&lt;/p&gt;

&lt;p&gt;For the first test, I opened the collection without a filter, projection, or custom limit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;db.largeCollection.find({})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is what happened:&lt;/p&gt;

&lt;h3&gt;
  
  
  Test 1: What the large-result test actually shows
&lt;/h3&gt;

&lt;p&gt;The three clients made different choices.&lt;/p&gt;

&lt;p&gt;VisuaLeaf allowed one continuous result. That is useful when you need to scroll through a large set, select many documents, inspect distant rows, or export the current result without moving through pages.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuaranufuy6h5x7sazkjw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuaranufuy6h5x7sazkjw.png" alt="VisuaLeaf displaying 50,000 MongoDB documents with a load time of 13.481 seconds." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;VisuaLeaf loaded 50,000 documents in one result in 13.481 seconds.&lt;/p&gt;

&lt;p&gt;Compass limited the amount of data visible at once. This protects the interface from an unexpectedly large query and works well when you only need to inspect a sample, edit one document, or confirm a filter. The tradeoff is that browsing the whole collection requires hundreds of pages.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwm1gf7v40vgutjfgfojd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwm1gf7v40vgutjfgfojd.png" alt="MongoDB Compass showing documents 1–100 of 50,844 with page-size options up to 100." width="799" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;MongoDB Compass displayed the collection in pages of up to 100 documents.&lt;/p&gt;

&lt;p&gt;Studio 3T did not finish this particular operation before I stopped it. That does not prove that every version, display mode, or computer will behave the same way. It does mean that an unrestricted result was not practical in this setup.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzojedptegz8loztlb68f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzojedptegz8loztlb68f.png" alt="Studio 3T showing a not responding message while reading documents from a MongoDB collection." width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Studio 3T became unresponsive while attempting to load 50,000 documents.&lt;/p&gt;

&lt;p&gt;If a client struggles, reduce both the number of documents and the number of fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;db.largeCollection.find(
  {},
  {
    _id: 1,
    company: 1,
    registered: 1
  }
).limit(1000)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is usually the better daily workflow. A large result may be useful for inspection or export, but it still consumes database resources, network bandwidth, memory, and rendering time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test 2: Comparing two MongoDB collections
&lt;/h3&gt;

&lt;p&gt;For this test, I used two controlled collections in the &lt;code&gt;QA_Edit&lt;/code&gt; database: &lt;code&gt;orders_compare_source&lt;/code&gt; and &lt;code&gt;orders_compare_target&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Both collections started with 1,000 identical documents. I then modified 100 documents, removed 50, and added 50 in the target collection. I matched the documents by &lt;code&gt;_id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;VisuaLeaf and Studio 3T both found 850 identical documents, 100 modified documents, 50 missing from the source, and 50 missing from the target.&lt;/p&gt;

&lt;p&gt;A useful comparison must answer three questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Which documents exist on only one side?  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Which fields changed inside matching documents?  &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What will be inserted, updated, or deleted during synchronization?  &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  VisuaLeaf
&lt;/h3&gt;

&lt;p&gt;VisuaLeaf has a dedicated &lt;a href="https://visualeaf.com/features/collection-compare/" rel="noopener noreferrer"&gt;Collection Compare&lt;/a&gt; workflow for schema and data differences. It keeps the comparison next to the document, query, and data-operation tools.&lt;/p&gt;

&lt;p&gt;The important setup choice is the matching field. &lt;code&gt;_id&lt;/code&gt; works only if both collections preserve the same values. If an import regenerated them, use a stable key such as &lt;code&gt;customerId&lt;/code&gt;, &lt;code&gt;orderId&lt;/code&gt;, or &lt;code&gt;externalId&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiq7a0zy8xurbsmqnxhcl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiq7a0zy8xurbsmqnxhcl.png" alt="VisuaLeaf comparison results for two MongoDB collections showing 850 identical documents, 100 modified, 50 missing in source, and 50 missing in target." width="800" height="683"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;VisuaLeaf found 850 identical, 100 modified, and 50 documents missing from each collection.&lt;/p&gt;

&lt;h3&gt;
  
  
  Studio 3T
&lt;/h3&gt;

&lt;p&gt;Studio 3T provides the most detailed comparison controls of the three. Its &lt;a href="https://studio3t.com/knowledge-base/articles/compare-mongodb-collections/" rel="noopener noreferrer"&gt;Data Compare &amp;amp; Sync&lt;/a&gt; supports collections on different databases or servers, document- and field-level differences, result export, and synchronization in either direction.&lt;/p&gt;

&lt;p&gt;Bi-directional synchronization is useful, but it increases the cost of a wrong choice. Decide which collection is the source of truth before applying changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsst1txnfatzh32wc7zwl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsst1txnfatzh32wc7zwl.png" alt="Studio 3T MongoDB collection comparison showing 850 identical documents, 100 different, 50 without a source match, and 50 without a target match." width="800" height="707"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Studio 3T returned the same differences using &lt;code&gt;_id&lt;/code&gt; as the matching field.&lt;/p&gt;

&lt;h3&gt;
  
  
  MongoDB Compass
&lt;/h3&gt;

&lt;p&gt;Compass does not provide the same dedicated collection-diff workflow. You can still build part of the comparison with an aggregation.&lt;/p&gt;

&lt;p&gt;For example, this pipeline finds source documents without a matching &lt;code&gt;_id&lt;/code&gt; in the target:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;db.orders_compare_source.aggregate([
  {
    $lookup: {
      from: "orders_compare_target",
      localField: "_id",
      foreignField: "_id",
      as: "target"
    }
  },
  {
    $match: {
      target: {
        $eq: []
      }
    }
  },
  {
  $count: "missingInTarget"
  }
  }
])
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This finds missing matches, not every field-level change. It also assumes both collections are in the same database. Comparing data on different servers requires a script, a temporary copy, or another tool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffrss7hzgk6ctcpa765ul.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffrss7hzgk6ctcpa765ul.png" alt="MongoDB Compass pipeline showing 50 documents missing from orders_compare_target." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Compass used an aggregation pipeline to find 50 source documents missing from the target.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test 3: Automating Daily Orders
&lt;/h2&gt;

&lt;p&gt;Large collections make small mistakes more expensive.&lt;/p&gt;

&lt;p&gt;For this test, I used a daily orders JSON file.&lt;/p&gt;

&lt;p&gt;In Studio 3T, I imported the file into a &lt;code&gt;daily_orders&lt;/code&gt; collection. Studio 3T can also save and schedule tasks, so it works well when you want to repeat imports, exports, or other database jobs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffpjf7d4nhpa03nrp24j5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffpjf7d4nhpa03nrp24j5.png" alt="Build a connected workflow: import to staging, then continue with the next task" width="800" height="570"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;JSON import task: import a file into a collection&lt;/p&gt;

&lt;p&gt;In VisuaLeaf, I used &lt;a href="https://visualeaf.com/features/task-manager/" rel="noopener noreferrer"&gt;Task Manager&lt;/a&gt; to connect more than one step:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Import Daily Orders JSON → Add Orders to Main Collection → Export Sales Report&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcte0fhqvveg4r2p5x5nl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcte0fhqvveg4r2p5x5nl.png" alt="VisuaLeaf can turn import, copy, and export steps into one connected workflow." width="800" height="526"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;VisuaLeaf lets you connect tasks into a workflow, not just run one import.&lt;/p&gt;

&lt;p&gt;The useful part is that tasks can have parent tasks. So the next task runs after the previous one is completed.&lt;/p&gt;

&lt;p&gt;This is helpful when the work is not just “import this file.” In a real workflow, you may need to import data, map fields, move it into the main collection, mask sensitive data, and export a report after that.&lt;/p&gt;

&lt;p&gt;For example, field mapping matters because &lt;code&gt;orderedAt&lt;/code&gt; should be mapped to &lt;code&gt;createdAt&lt;/code&gt; as a MongoDB Date, and values like email or phone numbers may need data masking before export.&lt;/p&gt;

&lt;p&gt;So for me, Studio 3T is strong for saving and scheduling database tasks. VisuaLeaf is easier when I want to see a connected workflow in one place and control the order of the steps.&lt;/p&gt;

&lt;p&gt;Compass is different. It is great for interactive work, but recurring workflows usually need something outside the desktop app, like a script, cron, Windows Task Scheduler, CI, or an Atlas service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test 4: Checking the Query Before Blaming the Client
&lt;/h2&gt;

&lt;p&gt;The first test measured how each tool handled a large result on screen.&lt;/p&gt;

&lt;p&gt;This test is different. Here, I wanted to check the work MongoDB was doing before the data even reached the client.&lt;/p&gt;

&lt;p&gt;I started with a query that had no supporting index:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;db.largeCollection.find({
  company: "ZENSOR"
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part was the explain plan.&lt;/p&gt;

&lt;p&gt;I checked:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;scan stage in the winning plan: &lt;code&gt;COLLSCAN&lt;/code&gt; or &lt;code&gt;IXSCAN&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;totalDocsExamined&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;totalKeysExamined&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nReturned&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;execution time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When the query uses &lt;code&gt;COLLSCAN&lt;/code&gt;, MongoDB has to scan the collection to find matching documents. That means the slow part is not always the GUI. Sometimes the database is simply doing too much work.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;COLLSCAN&lt;/code&gt; is not automatically a problem. But in this case, scanning 50,844 documents to return only 51 showed that the query could benefit from an index.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6tldf2vqff2bhxa8vown.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6tldf2vqff2bhxa8vown.png" alt="VisuaLeaf Query Profiler showing a collection scan with 50,844 documents examined and 51 returned." width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;VisuaLeaf detected a &lt;code&gt;COLLSCAN&lt;/code&gt;, examining 50,844 documents to return 51.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiee01pq7102ob5althrg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiee01pq7102ob5althrg.png" alt="Studio 3T Visual Explain showing a collection scan with 50,844 documents examined and 51 returned." width="800" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Studio 3T reported a collection scan across 50,844 documents for 51 results.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftsosejsdllswyi86leny.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftsosejsdllswyi86leny.png" alt="MongoDB Compass Explain Plan showing a collection scan with 50,844 documents examined and 51 returned." width="800" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Compass used a &lt;code&gt;COLLSCAN&lt;/code&gt;, examining all 50,844 documents to find 51 matches.&lt;/p&gt;

&lt;p&gt;After that, I created an index:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;db.largeCollection.createIndex({
  company: 1
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I ran the same explain operation again.&lt;/p&gt;

&lt;p&gt;A lower execution time is useful, but the stronger evidence is that the winning plan now contains an &lt;code&gt;IXSCAN&lt;/code&gt;. In this test, &lt;code&gt;totalDocsExamined&lt;/code&gt; dropped from 50,844 to 51, matching the 51 returned documents.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://visualeaf.com/features/query-profiler/" rel="noopener noreferrer"&gt;VisuaLeaf’s Query Profiler&lt;/a&gt; is useful here because it brings slow queries, execution plans, index usage, scanned documents, returned documents, and optimization hints into one place.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flnau13lp7bkxu91xst07.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flnau13lp7bkxu91xst07.png" alt="VisuaLeaf Query Profiler showing the company index with 51 documents and 51 index keys examined." width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After adding the &lt;code&gt;company_1&lt;/code&gt; index, VisuaLeaf examined 51 documents and 51 index keys.&lt;/p&gt;

&lt;p&gt;Studio 3T has the deepest performance tools in this comparison, with Query Profiler, Visual Explain, and Index Manager.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgog4gnfsvko3tdj4gdag.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgog4gnfsvko3tdj4gdag.png" alt="Studio 3T Visual Explain showing an index scan using company_1 and examining 51 documents." width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With the &lt;code&gt;company_1&lt;/code&gt; index, Studio 3T used an index scan and examined 51 documents.&lt;/p&gt;

&lt;p&gt;Compass also has Explain Plan, which works well when you already know which query you want to inspect.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsxogwnyk27lb4qg0j8tm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsxogwnyk27lb4qg0j8tm.png" alt="MongoDB Compass Explain Plan showing an index scan with 51 documents and 51 index keys examined." width="800" height="553"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After indexing &lt;code&gt;company&lt;/code&gt;, Compass changed to &lt;code&gt;IXSCAN&lt;/code&gt; and examined only 51 documents.&lt;/p&gt;

&lt;p&gt;For this test, the main point was simple: before blaming the client, check if MongoDB is scanning too much data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which client fits which workflow?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;If you mainly need to…&lt;/th&gt;
&lt;th&gt;Best fit&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Open and work with a large continuous result&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;VisuaLeaf&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;It loaded 50,000 documents in 13.481 seconds and remained usable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Compare two collections visually&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;VisuaLeaf&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;It clearly separated identical, modified, and missing documents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Automate multi-step data workflows&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;VisuaLeaf&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Import, copy, export, and scheduling can be managed in one workflow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Find inefficient queries in the same workspace&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;VisuaLeaf&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Query Profiler combines plans, index usage, scanned documents, and optimization hints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browse documents in small pages with a free official client&lt;/td&gt;
&lt;td&gt;MongoDB Compass&lt;/td&gt;
&lt;td&gt;Pagination keeps browsing controlled and predictable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Access advanced synchronization and administration controls&lt;/td&gt;
&lt;td&gt;Studio 3T&lt;/td&gt;
&lt;td&gt;It provides more granular diff, sync, and performance options&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There is no single client for every MongoDB job. Compass is a good option for controlled, paginated browsing, while Studio 3T offers advanced administration and synchronization controls. But for everyday work with large results, collection comparisons, automated tasks, and query analysis, &lt;a href="https://visualeaf.com" rel="noopener noreferrer"&gt;VisuaLeaf&lt;/a&gt; provides the most complete workflow in one place.&lt;/p&gt;

&lt;p&gt;Choose the client around the operation that costs you time, not the longest feature list.&lt;/p&gt;

</description>
      <category>mongodb</category>
      <category>javascript</category>
      <category>database</category>
      <category>software</category>
    </item>
    <item>
      <title>How to Copy and Sync a MongoDB Collection to PostgreSQL</title>
      <dc:creator>VisuaLeaf</dc:creator>
      <pubDate>Tue, 28 Jul 2026 07:46:26 +0000</pubDate>
      <link>https://dev.to/visualeaf/how-to-copy-and-sync-a-mongodb-collection-to-postgresql-1of7</link>
      <guid>https://dev.to/visualeaf/how-to-copy-and-sync-a-mongodb-collection-to-postgresql-1of7</guid>
      <description>&lt;p&gt;I tested a MongoDB-to-PostgreSQL sync with a &lt;code&gt;visits&lt;/code&gt; collection.&lt;/p&gt;

&lt;p&gt;At first, I thought the hard part would be the sync itself. It was not. The harder part was making PostgreSQL understand the MongoDB document shape.&lt;/p&gt;

&lt;p&gt;MongoDB lets one document contain strings, dates, nested objects, arrays, and an &lt;code&gt;_id&lt;/code&gt;. PostgreSQL wants fixed columns, clear data types, and a primary key.&lt;/p&gt;

&lt;p&gt;So before running the sync, I had to decide what the PostgreSQL table should look like.&lt;/p&gt;

&lt;p&gt;For this test, I used &lt;a href="https://visualeaf.com/features/mongosync/" rel="noopener noreferrer"&gt;VisuaLeaf Mongo Sync.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I used it because I wanted to set up the workflow visually: source collection, target PostgreSQL table, field mapping, and sync status. I still had to create the PostgreSQL table and check the mapping carefully, but I did not have to write a custom sync script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Source Collection
&lt;/h2&gt;

&lt;p&gt;The source was:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;database_compare_demo.visits&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Each MongoDB document had simple fields like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;_id&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;visitId&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;status&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;visitDate&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;visitReason&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also had nested fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;patient&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;doctor&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;symptoms&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;prescriptions&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;invoice&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;labResults&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;visitDetails&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vitals&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;clinic&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl8hcpn2ccn6ad1z3w82i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl8hcpn2ccn6ad1z3w82i.png" alt="VisuaLeaf showing the MongoDB visits collection in a local replica set." width="800" height="621"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;MongoDB &lt;code&gt;visits&lt;/code&gt; collection with nested patient, doctor, lab results, and vitals data.&lt;/p&gt;

&lt;p&gt;This is where the mapping matters. If you try to force every nested MongoDB field into separate SQL columns too early, the setup gets messy fast.&lt;/p&gt;

&lt;p&gt;For this test, I used a PostgreSQL landing table.&lt;/p&gt;

&lt;h2&gt;
  
  
  PostgreSQL Table
&lt;/h2&gt;

&lt;p&gt;I created one table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mongo_visits_sync&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;mongo_id&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"visit_id"&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"visit_date"&lt;/span&gt; &lt;span class="n"&gt;TIMESTAMPTZ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"visit_reason"&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="n"&gt;patient&lt;/span&gt; &lt;span class="n"&gt;JSONB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;doctor&lt;/span&gt; &lt;span class="n"&gt;JSONB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;symptoms&lt;/span&gt; &lt;span class="n"&gt;JSONB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;prescriptions&lt;/span&gt; &lt;span class="n"&gt;JSONB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;invoice&lt;/span&gt; &lt;span class="n"&gt;JSONB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"lab_results"&lt;/span&gt; &lt;span class="n"&gt;JSONB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;"visit_details"&lt;/span&gt; &lt;span class="n"&gt;JSONB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;vitals&lt;/span&gt; &lt;span class="n"&gt;JSONB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;clinic&lt;/span&gt; &lt;span class="n"&gt;JSONB&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I kept the simple fields as normal columns.&lt;/p&gt;

&lt;p&gt;I stored the nested MongoDB objects and arrays as &lt;code&gt;JSONB&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This was easier to test, and it kept the original document structure instead of flattening everything too early.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why &lt;code&gt;mongo_id&lt;/code&gt; Is Important
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://visualeaf.com/database/postgresql/" rel="noopener noreferrer"&gt;PostgreSQL table&lt;/a&gt; needs a primary key.&lt;/p&gt;

&lt;p&gt;In this setup, MongoDB &lt;code&gt;_id&lt;/code&gt; maps to PostgreSQL &lt;code&gt;mongo_id&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;_id -&amp;gt; mongo_id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without this, the sync cannot safely update existing rows.&lt;/p&gt;

&lt;p&gt;One error I hit was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SQL target has no key columns — cannot upsert idempotently
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means PostgreSQL did not have a usable key for the sync job.&lt;/p&gt;

&lt;p&gt;The fix was to create &lt;code&gt;mongo_id&lt;/code&gt; as the primary key and map MongoDB &lt;code&gt;_id&lt;/code&gt; to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Field Mapping and Data Types
&lt;/h2&gt;

&lt;p&gt;In VisuaLeaf, the &lt;a href="https://visualeaf.com/docs/mongosync" rel="noopener noreferrer"&gt;mapping shows&lt;/a&gt; the MongoDB fields with simple source types like &lt;code&gt;String&lt;/code&gt;, &lt;code&gt;Object&lt;/code&gt;, and &lt;code&gt;Array&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That matters because not every MongoDB field should become a normal text column in PostgreSQL.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnn7djc7733kh5pcy834r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnn7djc7733kh5pcy834r.png" alt="New transformation mapping screen in VisuaLeaf with Generate Field Mappings button." width="799" height="341"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Creating a new transformation mapping before generating the MongoDB to PostgreSQL fields.&lt;/p&gt;

&lt;p&gt;For this test, I used this logic:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm567sg8wo440fq8s0t1m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm567sg8wo440fq8s0t1m.png" alt="VisuaLeaf field mapping table for MongoDB to PostgreSQL sync." width="800" height="503"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Field mapping from MongoDB fields to PostgreSQL columns, including objects and arrays.&lt;/p&gt;

&lt;p&gt;The simple string fields went into normal PostgreSQL text columns.&lt;/p&gt;

&lt;p&gt;The objects and arrays went into &lt;code&gt;JSONB&lt;/code&gt; columns.&lt;/p&gt;

&lt;p&gt;This was the important part. If I mapped an object or array into a normal text column, the sync would either fail or store the data in a way that is harder to query later.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;patient&lt;/code&gt; is an object, so I mapped it to a &lt;code&gt;JSONB&lt;/code&gt; column.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;symptoms&lt;/code&gt; is an array, so I also mapped it to &lt;code&gt;JSONB&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The main key was:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;_id -&amp;gt; mongo_id&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;That gave PostgreSQL a stable primary key for updates and deletes during sync.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Error I Had With Column Names
&lt;/h2&gt;

&lt;p&gt;At first, my PostgreSQL table used names like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lab_results
visit_details
visit_reason
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the sync tried to insert into:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;labResults
visitDetails
visitReason
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So PostgreSQL returned this error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ERROR: column "labResults" of relation "mongo_visits_sync" does not exist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6donwbfxzdi6dt5v14k0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6donwbfxzdi6dt5v14k0.png" alt="VisuaLeaf Mongo Sync audit log showing a PostgreSQL column name mismatch error for labResults." width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The sync failed because the MongoDB field &lt;code&gt;labResults&lt;/code&gt; did not match the PostgreSQL column &lt;code&gt;lab_results&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The problem was not PostgreSQL. The problem was the mismatch between the mapping and the real column names.&lt;/p&gt;

&lt;p&gt;You can fix this in two ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Keep snake_case in PostgreSQL and map each field manually.&lt;/li&gt;
&lt;li&gt;Use quoted camelCase column names in PostgreSQL.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Sync Setup
&lt;/h2&gt;

&lt;p&gt;The source MongoDB database was a replica set.&lt;/p&gt;

&lt;p&gt;That matters because continuous sync uses MongoDB change streams. Change streams work with replica sets and sharded clusters, not standalone MongoDB.&lt;/p&gt;

&lt;p&gt;The setup was:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvueygds3b4eyuotmzz5p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvueygds3b4eyuotmzz5p.png" alt="VisuaLeaf Mongo Sync rules screen mapping visits to a target table." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Mapping the MongoDB &lt;code&gt;visits&lt;/code&gt; collection to the PostgreSQL target table.&lt;/p&gt;

&lt;p&gt;I used Full sync because I wanted the first copy and the later changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4cudvrsz4z1w8ojv1i9n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4cudvrsz4z1w8ojv1i9n.png" alt="VisuaLeaf Mongo Sync review screen before launching the sync job." width="800" height="575"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Final review before saving the MongoDB to PostgreSQL sync job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checking the Initial Copy
&lt;/h2&gt;

&lt;p&gt;After running the sync, I checked the row count:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT COUNT(*) AS synced_rows
FROM public.mongo_visits_sync;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I checked a few fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT
    "visit_id",
    status,
    "visit_date",
    patient -&amp;gt;&amp;gt; 'fullName' AS patient_name,
    doctor -&amp;gt;&amp;gt; 'fullName' AS doctor_name
FROM public.mongo_visits_sync;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9dmq1h98l2053vohpdoy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F9dmq1h98l2053vohpdoy.png" alt="PostgreSQL table in VisuaLeaf showing synced MongoDB visit rows." width="800" height="621"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Synced MongoDB visit documents shown in the PostgreSQL target table.&lt;/p&gt;

&lt;p&gt;This confirmed that normal fields were copied into columns, and nested values were still queryable from &lt;code&gt;JSONB&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Insert, Update, and Delete
&lt;/h2&gt;

&lt;p&gt;The first copy is not enough to prove the sync works.&lt;/p&gt;

&lt;p&gt;So I tested changes from MongoDB too.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw44x2yu0fkuglryshkzu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw44x2yu0fkuglryshkzu.png" alt="VisuaLeaf Mongo Sync monitor showing live insert update and delete events." width="800" height="575"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Live sync monitor showing inserts, updates, deletes, and connection status.&lt;/p&gt;

&lt;p&gt;For the update test, I used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;db.visits.updateOne(
  { visitId: "VIS-SYNC-TEST" },
  {
    $set: {
      status: "completed",
      "visitDetails.notes": "Updated after initial sync"
    }
  }
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I checked PostgreSQL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT
    "visit_id",
    status,
    "visit_details" -&amp;gt;&amp;gt; 'notes' AS notes
FROM public.mongo_visits_sync
WHERE "visit_id" = 'VIS-SYNC-TEST';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The updated status and note appeared in PostgreSQL.&lt;/p&gt;

&lt;p&gt;I also tested insert and delete. The new document appeared in PostgreSQL, and the deleted document was removed from the table.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Would Watch For
&lt;/h2&gt;

&lt;p&gt;The sync works, but the setup is not automatic magic.&lt;/p&gt;

&lt;p&gt;You still need to be careful with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;primary keys&lt;/li&gt;
&lt;li&gt;field mapping&lt;/li&gt;
&lt;li&gt;camelCase vs snake_case column names&lt;/li&gt;
&lt;li&gt;nested objects&lt;/li&gt;
&lt;li&gt;arrays&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;JSONB&lt;/code&gt; fields&lt;/li&gt;
&lt;li&gt;unmapped MongoDB fields&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a first version, one PostgreSQL landing table is usually easier than trying to normalize everything.&lt;/p&gt;

&lt;p&gt;After the data is synced, you can decide if some fields should move into separate SQL tables later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This kind of sync is useful when MongoDB is where the app data lives, but PostgreSQL is better for reporting, SQL queries, dashboards, or sharing data with teams that work mostly in relational databases.&lt;/p&gt;

&lt;p&gt;The goal is not only to copy the collection once. The useful part is keeping PostgreSQL updated when documents are inserted, updated, or deleted in MongoDB.&lt;/p&gt;

&lt;p&gt;For this test, the cleanest setup was to store simple fields as normal PostgreSQL columns and keep nested objects and arrays as &lt;code&gt;JSONB&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once the primary key, column names, data types, and field mapping were correct, &lt;a href="https://visualeaf.com/features/mongosync/" rel="noopener noreferrer"&gt;VisuaLeaf Mongo Sync&lt;/a&gt; could handle the copy and the later changes without writing a custom sync script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Want to Sync MongoDB to Atlas Instead?
&lt;/h2&gt;

&lt;p&gt;If you want to copy a local MongoDB collection to Atlas and keep it in sync, I covered the full process in this detailed guide: &lt;a href="https://visualeaf.com/blog/copy-sync-local-mongodb-collection-to-atlas/" rel="noopener noreferrer"&gt;How to Copy a Local MongoDB Collection to Atlas—and Keep It in Sync.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mongodb</category>
      <category>postgres</category>
      <category>database</category>
      <category>software</category>
    </item>
    <item>
      <title>How to Import JSON into MongoDB and Export to CSV with Data Masking</title>
      <dc:creator>VisuaLeaf</dc:creator>
      <pubDate>Thu, 23 Jul 2026 09:20:23 +0000</pubDate>
      <link>https://dev.to/visualeaf/how-to-import-json-into-mongodb-and-export-to-csv-with-data-masking-58p4</link>
      <guid>https://dev.to/visualeaf/how-to-import-json-into-mongodb-and-export-to-csv-with-data-masking-58p4</guid>
      <description>&lt;p&gt;Every morning, an online store receives the previous day’s orders from a marketplace partner.&lt;/p&gt;

&lt;p&gt;The file comes in JSON format. The company needs to add those orders to its main MongoDB &lt;code&gt;orders&lt;/code&gt; collection. The sales manager also needs a CSV report that can be opened in Excel.&lt;/p&gt;

&lt;p&gt;That sounds like a small task. Import the file, copy the documents, export the report.&lt;/p&gt;

&lt;p&gt;But in practice, a few things can break the process.&lt;/p&gt;

&lt;p&gt;A date can be imported as a string. A field can have the wrong name. One batch may use &lt;code&gt;total&lt;/code&gt;, while the main collection uses &lt;code&gt;totalAmount&lt;/code&gt;. A temporary collection can keep old records and trigger duplicate key errors. A CSV export can create null values because the mapping points to fields that do not exist.&lt;/p&gt;

&lt;p&gt;And then there is customer data. The manager may need the sales numbers, but they probably do not need real customer names or internal customer IDs.&lt;/p&gt;

&lt;p&gt;This article walks through a real daily workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Import marketplace JSON
        ↓
Store the batch in a temporary MongoDB collection
        ↓
Copy the orders into the main orders collection
        ↓
Mask customer fields during export
        ↓
Create a CSV report
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal is not just to move data from JSON to CSV. The goal is to make the process repeatable, easier to check, and safer to share.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8g992dpxpflixeleq9x2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8g992dpxpflixeleq9x2.png" alt="VisuaLeaf Task Manager showing a MongoDB workflow that imports yesterday’s orders from JSON, adds them to the main orders collection, and exports a CSV report." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The workflow
&lt;/h2&gt;

&lt;p&gt;The workflow has three jobs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Import Yesterday Orders
        ↓
Add Orders to Main
        ↓
Export Daily Sales Report
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important part is the parent relationship between the jobs.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Add Orders to Main&lt;/code&gt; depends on &lt;code&gt;Import Yesterday Orders&lt;/code&gt;, so it only runs after the JSON file is imported successfully.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Export Daily Sales Report&lt;/code&gt; depends on &lt;code&gt;Add Orders to Main&lt;/code&gt;, so the CSV is created only after the main &lt;code&gt;orders&lt;/code&gt; collection has been updated.&lt;/p&gt;

&lt;p&gt;This prevents the report from being generated when data is missing or incomplete.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flvxggmrnum09wzra7ibg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flvxggmrnum09wzra7ibg.png" alt="VisuaLeaf Task Manager table view showing a daily MongoDB workflow where the import job has no parent, the copy job depends on the import, and the CSV export depends on the copy job." width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The incoming JSON file
&lt;/h2&gt;

&lt;p&gt;The partner sends a file with yesterday’s completed orders.&lt;/p&gt;

&lt;p&gt;A single order looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"orderId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ORD-2026-07-201"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"customerId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CUST-1003"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"customerName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Sofia Rossi"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"orderDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07-21T08:20:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"completed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"paymentStatus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"paid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"EUR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sku"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"EL-002"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"productName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Wireless Mouse"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Electronics"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"quantity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"unitPrice"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;24.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"lineTotal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;24.99&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sku"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"EL-003"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"productName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mechanical Keyboard"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"category"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Electronics"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"quantity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"unitPrice"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;79.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"lineTotal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;79.99&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"itemsSummary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1x Wireless Mouse, 1x Mechanical Keyboard"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"itemCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"subtotal"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;104.98&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"discount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"shippingFee"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;4.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"totalAmount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;99.97&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"couponCode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"WELCOME10"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two fields worth pointing out here.&lt;/p&gt;

&lt;p&gt;The first is &lt;code&gt;items&lt;/code&gt;. This is the real order structure. It keeps each product as a nested object with its own SKU, quantity, price, and line total.&lt;/p&gt;

&lt;p&gt;The second is &lt;code&gt;itemsSummary&lt;/code&gt;. This is not as rich as the &lt;code&gt;items&lt;/code&gt; array, but it works better in a CSV report. Instead of putting a full JSON array into one spreadsheet cell, the manager sees a readable value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1x Wireless Mouse, 1x Mechanical Keyboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MongoDB handles nested arrays well. CSV does not. So for the database, keep the array. For the report, export the summary.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The first job imports the file into a temporary collection:&lt;br&gt;&lt;br&gt;
&lt;code&gt;online_store.daily_sales_import&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fns2vukq2ddqqaz5li67r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fns2vukq2ddqqaz5li67r.png" alt="VisuaLeaf Task Manager import job configured to load a JSON file into the online_store.daily_sales_import MongoDB collection." width="800" height="579"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why use a temporary collection?
&lt;/h2&gt;

&lt;p&gt;The first job imports the JSON file into &lt;code&gt;online_store.daily_sales_import&lt;/code&gt; instead of writing directly to &lt;code&gt;orders&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This extra step is worth it.&lt;/p&gt;

&lt;p&gt;The file comes from another system. Even if the partner usually sends the correct structure, a single change can break your report. A field can be renamed. A value can arrive as text instead of a number. A date can be formatted differently. Or the import job can use an old field mapping from another file.&lt;/p&gt;

&lt;p&gt;The temporary collection gives you a place to check the batch before it becomes part of the main order history.&lt;/p&gt;

&lt;p&gt;If it keeps old documents, the next run may copy the same orders again. That can cause duplicate key errors later.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcqykslgrvrvu70gfnpxw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcqykslgrvrvu70gfnpxw.png" alt="VisuaLeaf showing the daily_sales_import MongoDB collection with one imported order document expanded, including fields such as customerId, customerName, items, itemsSummary, and orderDate." width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate field mappings every time the JSON structure changes
&lt;/h2&gt;

&lt;p&gt;This is the easiest step to skip, and it is also where a lot of bad imports start.&lt;/p&gt;

&lt;p&gt;When you select the JSON file, click:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Generate Field Mappings
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this order file, the mapping should include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;_id           → _id
orderId       → orderId
customerId    → customerId
customerName  → customerName
orderDate     → orderDate
status        → status
paymentStatus → paymentStatus
currency      → currency
items         → items
itemsSummary  → itemsSummary
itemCount     → itemCount
subtotal      → subtotal
discount      → discount
shippingFee   → shippingFee
totalAmount   → totalAmount
couponCode    → couponCode
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you do not regenerate the mappings, the job may reuse fields from an older import.&lt;/p&gt;

&lt;p&gt;For example, if the previous import was a customer file, the mapping may still expect fields like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;country
email
joinedAt
name
customerId
status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The import may still run. But the order fields will be missing, and the result will look wrong. You may see null values or documents that only contain a few shared fields like &lt;code&gt;customerId&lt;/code&gt; and &lt;code&gt;status&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That is not a MongoDB issue. It is a mapping issue.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frpztwq8l13bacqmnby5w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frpztwq8l13bacqmnby5w.png" alt="VisuaLeaf field mappings for a MongoDB copy job." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Copy the batch into the main orders collection
&lt;/h2&gt;

&lt;p&gt;The second job copies documents from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;daily_sales_import
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;into:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;orders
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This job should use &lt;code&gt;Import Yesterday Orders&lt;/code&gt; as its parent.&lt;/p&gt;

&lt;p&gt;A simple configuration is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Source: online_store.daily_sales_import
Target: online_store.orders
Mode: Insert or Append
Parent: Import Yesterday Orders
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use insert or append because you are adding new orders to the order history.&lt;/p&gt;

&lt;p&gt;Do not use replace mode on &lt;code&gt;orders&lt;/code&gt; unless you really want to overwrite the full collection.&lt;/p&gt;

&lt;p&gt;The main collection should keep all orders. The temporary collection should only hold the latest imported file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjtg7s7u3lqcfy8s5phd8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjtg7s7u3lqcfy8s5phd8.png" alt="VisuaLeaf copy job from daily_sales_import to orders." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Duplicate key errors are useful, but they still need a fix
&lt;/h2&gt;

&lt;p&gt;If you run the same batch twice, MongoDB may return this error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;E11000 duplicate key error
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means MongoDB blocked a duplicate value for a unique field, often &lt;code&gt;_id&lt;/code&gt; or &lt;code&gt;orderId&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is usually good. It stops the same order from being inserted twice.&lt;/p&gt;

&lt;p&gt;But in a daily workflow, the error also tells you something is wrong with the process.&lt;/p&gt;

&lt;p&gt;Common causes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The temporary collection was not cleared.
The same JSON file was imported twice.
The partner sent duplicate order IDs.
The copy job tried to insert records already present in orders.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In testing, the temporary collection is often the problem.&lt;/p&gt;

&lt;p&gt;If &lt;code&gt;daily_sales_import&lt;/code&gt; contains old records and new records, the copy job tries to insert all of them. MongoDB accepts the new ones and rejects the duplicates.&lt;/p&gt;

&lt;p&gt;The fix is simple: clear or replace the temporary collection before each new import.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvw2b97rdaaz1e01g8n0i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvw2b97rdaaz1e01g8n0i.png" alt="VisuaLeaf Task Manager showing an E11000 duplicate key error during a MongoDB copy job." width="800" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Export the weekly report from the main collection
&lt;/h2&gt;

&lt;p&gt;The company imports marketplace orders every day, so the main MongoDB &lt;code&gt;orders&lt;/code&gt; collection stays up to date.&lt;/p&gt;

&lt;p&gt;But the manager does not need a CSV file every morning. In this case, the manager needs a weekly sales report.&lt;/p&gt;

&lt;p&gt;That changes the source of the export job.&lt;/p&gt;

&lt;p&gt;If you export from &lt;strong&gt;daily_sales_import,&lt;/strong&gt; you export only the latest imported batch.&lt;/p&gt;

&lt;p&gt;If you export from &lt;strong&gt;orders&lt;/strong&gt;, you export the main order history, including the orders that were imported during the week.&lt;/p&gt;

&lt;p&gt;For this workflow, the export job uses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Source: online_store.orders
Output: weekly-sales-report-{{yyyy-MM-dd}}.csv
Parent: Add Orders to Main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The parent relationship still matters. The weekly CSV should be created only after the latest imported orders have been added to the main &lt;code&gt;orders&lt;/code&gt; collection.&lt;/p&gt;

&lt;p&gt;This gives the manager one report with the updated sales data, instead of sending a separate file every day.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffcef2dva65u1pwqndjhj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffcef2dva65u1pwqndjhj.png" alt="VisuaLeaf export job from MongoDB orders collection to a weekly CSV report." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Mask customer data during export
&lt;/h2&gt;

&lt;p&gt;The manager needs sales data, but they do not need the real customer name or internal customer ID.&lt;/p&gt;

&lt;p&gt;Instead of changing the original MongoDB documents, the export job applies masking only in the CSV output.&lt;/p&gt;

&lt;p&gt;In this workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;customerId   → hash(value)
customerName → maskName(value)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps the original data intact in MongoDB, while the exported report hides sensitive customer details.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnp5mjkvulbf1o4qx0p53.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnp5mjkvulbf1o4qx0p53.png" alt="VisuaLeaf export mappings with hash(value) and maskName(value) applied." width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What maskName(value) does
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://visualeaf.com/docs/data-masking" rel="noopener noreferrer"&gt;&lt;code&gt;maskName(value)&lt;/code&gt; transformation&lt;/a&gt; keeps the first character and replaces the rest with asterisks.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Alex Ionescu → A***********
Mia Thompson → M***********
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It does not create a fake name.&lt;/p&gt;

&lt;p&gt;It masks the original value. The report still has a customer-name column, but the real name is hidden.&lt;/p&gt;

&lt;p&gt;This is useful when the report needs to show that a customer exists, but not who the customer is.&lt;/p&gt;

&lt;h2&gt;
  
  
  What hash(value) does
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;hash(value)&lt;/code&gt; transformation changes the customer ID into a hashed value.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CUST-1002 → 043a5f9b...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same input should produce the same hashed output.&lt;/p&gt;

&lt;p&gt;That means the report can still show that two orders belong to the same customer without exposing the original customer ID.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Original customerId: CUST-1002
Masked customerId:   043a5f9b...

Original customerId: CUST-1002
Masked customerId:   043a5f9b...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is useful for analysis.&lt;/p&gt;

&lt;p&gt;The manager can group orders by the masked ID, but they cannot see the real internal ID.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv68luj2i7s4jcit6rjxp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv68luj2i7s4jcit6rjxp.png" alt="VisuaLeaf export mappings with hash(value) and maskName(value) applied." width="799" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Schedule the workflow
&lt;/h2&gt;

&lt;p&gt;Once each job works on its own, schedule the full workflow.&lt;/p&gt;

&lt;p&gt;The final process is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Receive marketplace JSON
        ↓
Import the daily batch
        ↓
Add orders to MongoDB
        ↓
Mask customer details
        ↓
Export the CSV report
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the schedule to run every morning at 09:00.&lt;/p&gt;

&lt;p&gt;The parent relationships keep the jobs in order.&lt;/p&gt;

&lt;p&gt;The report does not run before the copy job. The copy job does not run before the import job.&lt;/p&gt;

&lt;p&gt;That is the main reason to use a workflow instead of three separate jobs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvt34ftngxxddmfe4bsw0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvt34ftngxxddmfe4bsw0.png" alt="VisuaLeaf schedule dialog showing a daily 9 AM recurring job." width="799" height="685"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What this workflow actually solves
&lt;/h2&gt;

&lt;p&gt;This workflow is not only about converting JSON to CSV.&lt;/p&gt;

&lt;p&gt;It solves a real process that many teams deal with: marketplace data comes in one format, MongoDB stores the operational data, and the manager needs a spreadsheet they can review without seeing unnecessary customer details.&lt;/p&gt;

&lt;p&gt;In this setup, the daily JSON file is imported into &lt;code&gt;daily_sales_import&lt;/code&gt;. Then the new orders are copied into the main &lt;code&gt;orders&lt;/code&gt; collection. At the end, the weekly CSV report is created from the updated &lt;code&gt;orders&lt;/code&gt; collection.&lt;/p&gt;

&lt;p&gt;Before the CSV is exported, sensitive customer fields are masked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;customerName → A***********
customerId   → 043a5f9b...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The original MongoDB documents stay complete. Only the exported report hides customer details.&lt;/p&gt;

&lt;p&gt;This makes the process repeatable, easier to check, and safer to share.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final result
&lt;/h2&gt;

&lt;p&gt;At the end, you have one scheduled workflow that handles the daily marketplace file and keeps MongoDB up to date.&lt;/p&gt;

&lt;p&gt;You avoid repeating the same import and export steps by hand. You reduce the chance of copying old batches again. You keep the report readable with fields like &lt;code&gt;itemsSummary&lt;/code&gt;. And you share the sales data without exposing the original customer identities.&lt;/p&gt;

&lt;p&gt;The small details still matter: generate the mappings when the JSON structure changes, keep field names consistent, store dates as &lt;code&gt;DATE_TIME&lt;/code&gt;, and preview the export before trusting the CSV.&lt;/p&gt;

&lt;p&gt;Once those pieces are set, the workflow is easier to run, easier to check, and safer to share.&lt;/p&gt;

&lt;p&gt;I built this example in &lt;a href="https://visualeaf.com" rel="noopener noreferrer"&gt;VisuaLeaf&lt;/a&gt; using &lt;a href="https://visualeaf.com/features/task-manager/" rel="noopener noreferrer"&gt;Task Manager&lt;/a&gt; and export transformations.&lt;/p&gt;

&lt;p&gt;If you work with MongoDB imports, scheduled exports, or masked reports, you can try it here: &lt;a href="https://visualeaf.com/download" rel="noopener noreferrer"&gt;https://visualeaf.com/download&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mongodb</category>
      <category>database</category>
      <category>software</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>How to Copy and Sync a Local MongoDB Collection to Atlas</title>
      <dc:creator>VisuaLeaf</dc:creator>
      <pubDate>Mon, 20 Jul 2026 14:04:46 +0000</pubDate>
      <link>https://dev.to/visualeaf/how-to-copy-and-sync-a-local-mongodb-collection-to-atlas-9me</link>
      <guid>https://dev.to/visualeaf/how-to-copy-and-sync-a-local-mongodb-collection-to-atlas-9me</guid>
      <description>&lt;p&gt;Moving data from a local MongoDB database to Atlas usually means exporting files, importing them again, and repeating the same process when something changes.&lt;/p&gt;

&lt;p&gt;I wanted to test a simpler way in VisuaLeaf: copy a local collection to MongoDB Atlas and keep the Atlas collection updated automatically.&lt;/p&gt;

&lt;p&gt;For this example, I used a local &lt;code&gt;visits&lt;/code&gt; collection containing seven documents. The documents included ordinary fields, nested objects, arrays, dates, and ObjectIds, so I could also check whether the original MongoDB structure remained intact in Atlas.&lt;/p&gt;

&lt;p&gt;Here is the complete process, followed by an insert, update, and delete test.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I used
&lt;/h2&gt;

&lt;p&gt;My source and target were:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Connection&lt;/th&gt;
&lt;th&gt;Database&lt;/th&gt;
&lt;th&gt;Collection&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Source&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Local Replica Set&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;database_compare_demo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;visits&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Target&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MongoDB Atlas&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;database_compare_demo&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;visits_atlas_sync&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I used a different target collection name so I could verify the copied data without mixing it with anything already stored in Atlas.&lt;/p&gt;

&lt;p&gt;The local source was a replica set because continuous synchronization relies on MongoDB change streams. Change streams are available for replica sets and sharded clusters, not standalone MongoDB deployments. (&lt;a href="https://www.mongodb.com/docs/manual/changestreams/" rel="noopener noreferrer"&gt;MongoDB change streams&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;If the source is standalone, the job can still perform an initial copy, but it cannot continue watching for later changes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fel4d5b2jahtrnq4ifit7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fel4d5b2jahtrnq4ifit7.png" alt="The local  raw `visits` endraw  collection before it was copied to Atlas." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The local &lt;code&gt;visits&lt;/code&gt; collection before it was copied to Atlas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparing the Atlas connection
&lt;/h2&gt;

&lt;p&gt;Before creating the sync job, I made sure VisuaLeaf could write to my Atlas cluster.&lt;/p&gt;

&lt;p&gt;Before using a standard Atlas connection, I configured two access items:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A database user with permission to access the target database.&lt;/li&gt;
&lt;li&gt;My computer’s public IP address added to the project’s IP access list.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Atlas database users are separate from the account used to sign in to the Atlas website. For this test, I used a database user with read and write access to &lt;code&gt;database_compare_demo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I added only my current IP address to the Atlas access list instead of allowing connections from every IP. Atlas only accepts standard client connections from addresses included in that list. (&lt;a href="https://www.mongodb.com/docs/atlas/mongo-shell-connection/" rel="noopener noreferrer"&gt;MongoDB Atlas connection prerequisites&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;From the Atlas cluster’s *&lt;strong&gt;&lt;em&gt;Connect&lt;/em&gt;&lt;/strong&gt;* window, I copied the driver connection string. Its general format was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mongodb+srv://&amp;lt;username&amp;gt;:&amp;lt;password&amp;gt;@&amp;lt;cluster-host&amp;gt;/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I used that URI to create a connection in VisuaLeaf and named it &lt;code&gt;MongoDB Atlas&lt;/code&gt;. I tested the connection before continuing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the sync job
&lt;/h2&gt;

&lt;p&gt;I opened *&lt;strong&gt;&lt;em&gt;Mongo Sync&lt;/em&gt;&lt;/strong&gt;* and created a new job with these details:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Job name&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Local Visits to Atlas&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Description&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Copy local clinic visits to Atlas and keep later changes synchronized.&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source connection&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Local Replica Set&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source database&lt;/td&gt;
&lt;td&gt;&lt;code&gt;database_compare_demo&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The source connection showed full change-stream support, which meant I could select Full sync later.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Futet1ywge0qu0vowdlph.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Futet1ywge0qu0vowdlph.png" alt="The local replica set and  raw `database_compare_demo` endraw  database selected as the source." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The local replica set and &lt;code&gt;database_compare_demo&lt;/code&gt; database selected as the source.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mapping the local collection to Atlas
&lt;/h2&gt;

&lt;p&gt;In the Rules step, I added the Atlas connection as the target and configured one rule:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Target connection&lt;/td&gt;
&lt;td&gt;&lt;code&gt;MongoDB Atlas&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Target database&lt;/td&gt;
&lt;td&gt;&lt;code&gt;database_compare_demo&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Source collection&lt;/td&gt;
&lt;td&gt;&lt;code&gt;visits&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Target collection&lt;/td&gt;
&lt;td&gt;&lt;code&gt;visits_atlas_sync&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Filter&lt;/td&gt;
&lt;td&gt;Empty&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transformation&lt;/td&gt;
&lt;td&gt;&lt;code&gt;None (pass-through)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I left the filter empty because I wanted all seven documents.&lt;/p&gt;

&lt;p&gt;I also used *&lt;strong&gt;&lt;em&gt;None (pass-through)&lt;/em&gt;&lt;/strong&gt;* instead of generating field mappings. Both sides were MongoDB, so I wanted to preserve each document exactly as it was, including its &lt;code&gt;_id&lt;/code&gt;, nested objects, arrays, dates, and optional fields.&lt;/p&gt;

&lt;p&gt;Field mappings are useful when renaming fields, changing types, masking values, or sending data into a different schema. They were unnecessary for this direct MongoDB-to-MongoDB copy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5sik23zrxfj0d3idawy6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5sik23zrxfj0d3idawy6.png" alt="The  raw `visits` endraw  collection mapped to  raw `visits_atlas_sync` endraw  with pass-through documents." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;visits&lt;/code&gt; collection mapped to &lt;code&gt;visits_atlas_sync&lt;/code&gt; with pass-through documents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing Full sync
&lt;/h2&gt;

&lt;p&gt;The Options step offered three modes:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;th&gt;When to use it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Initial Only&lt;/td&gt;
&lt;td&gt;Copies the documents that currently exist, then stops&lt;/td&gt;
&lt;td&gt;One-time collection copy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Incremental Only&lt;/td&gt;
&lt;td&gt;Watches only for changes made after the job starts&lt;/td&gt;
&lt;td&gt;The target is already populated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Full sync&lt;/td&gt;
&lt;td&gt;Copies existing documents and then watches for later changes&lt;/td&gt;
&lt;td&gt;Initial copy plus continuous synchronization&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I selected *&lt;strong&gt;&lt;em&gt;Full sync&lt;/em&gt;&lt;/strong&gt;* and enabled Start sync immediately after creation.&lt;/p&gt;

&lt;p&gt;This mode first copies the seven documents already stored locally. After the initial phase finishes, it continues listening for new inserts, updates, and deletes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffkkb4ugkbnseowrd2gl6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffkkb4ugkbnseowrd2gl6.png" alt="Full sync copies existing documents and continues watching the local collection." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Full sync copies existing documents and continues watching the local collection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reviewing and launching the job
&lt;/h2&gt;

&lt;p&gt;The Review screen showed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Local Replica Set / database_compare_demo&lt;/code&gt; as the source;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MongoDB Atlas / database_compare_demo&lt;/code&gt; as the target;&lt;/li&gt;
&lt;li&gt;one rule from &lt;code&gt;visits&lt;/code&gt; to &lt;code&gt;visits_atlas_sync&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;no filter or transformation;&lt;/li&gt;
&lt;li&gt;Full sync with automatic start.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It also estimated an initial copy of approximately seven documents.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzsxs3cu9wn3x2lsofapc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzsxs3cu9wn3x2lsofapc.png" alt="The complete local-to-Atlas configuration before launching the job." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The complete local-to-Atlas configuration before launching the job.&lt;/p&gt;

&lt;p&gt;I clicked *&lt;strong&gt;&lt;em&gt;Create &amp;amp; launch&lt;/em&gt;&lt;/strong&gt;* and waited for the initial phase to finish. Once the monitor showed that the job was running and waiting for changes, I checked the Atlas collection directly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4nmdoyzxh5vol2kh0z8a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4nmdoyzxh5vol2kh0z8a.png" alt="The initial copy is complete and the job is monitoring later changes." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The initial copy is complete and the job is monitoring later changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verifying the initial copy in Atlas
&lt;/h2&gt;

&lt;p&gt;I opened &lt;code&gt;database_compare_demo.visits_atlas_sync&lt;/code&gt; on the Atlas connection and counted its documents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visits_atlas_sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;countDocuments&lt;/span&gt;&lt;span class="p"&gt;({})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result was &lt;code&gt;7&lt;/code&gt;, matching the local source collection.&lt;/p&gt;

&lt;p&gt;I also opened several documents and checked their BSON types. The ObjectIds, dates, nested patient and doctor objects, and arrays were preserved because the job used pass-through documents rather than converting them into a new schema.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqahb7ju8f9ooh4czebxp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqahb7ju8f9ooh4czebxp.png" width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The initial count proved that the collection was copied. It did not yet prove that the Full sync job would handle later changes, so I tested each main operation separately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test 1: Inserting a new local document
&lt;/h3&gt;

&lt;p&gt;I inserted one clearly labeled test visit into the local source:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy3f1wjvd9t2bozeroukt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fy3f1wjvd9t2bozeroukt.png" alt="Insert a new document into the local connection." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Insert a new document into the local connection.&lt;/p&gt;

&lt;p&gt;Then I searched for the same &lt;code&gt;visitId&lt;/code&gt; in Atlas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visits_atlas_sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;visitId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;VIS-ATLAS-TEST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3091869bg990uofkm4ru.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3091869bg990uofkm4ru.gif" alt="A new local visit appears automatically in the Atlas collection." width="800" height="444"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A new local visit appears automatically in the Atlas collection.&lt;/p&gt;

&lt;p&gt;The new document appeared in the target collection without restarting or recreating the job. The target count increased from seven to eight.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test 2: Updating the local document
&lt;/h3&gt;

&lt;p&gt;Next, I updated both a top-level field and a nested field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateOne&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;visitId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;VIS-ATLAS-TEST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;$set&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;completed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;doctor.roomNumber&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;204&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;updatedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I checked the Atlas document again:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fic7sucvfx38dsx3gud77.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fic7sucvfx38dsx3gud77.png" alt="Top-level and nested changes from the local document are visible in Atlas." width="800" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Top-level and nested changes from the local document are visible in Atlas.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test 3: Deleting the local document
&lt;/h3&gt;

&lt;p&gt;Finally, I deleted the test document from the local collection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deleteOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;visitId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;VIS-ATLAS-TEST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I verified the target one more time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visits_atlas_sync&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;countDocuments&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;visitId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;VIS-ATLAS-TEST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdiimdu68xq4ruwp6xysc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fdiimdu68xq4ruwp6xysc.png" alt="Deleting the local test document removes its synchronized Atlas copy." width="799" height="475"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Deleting the local test document removes its synchronized Atlas copy.&lt;/p&gt;

&lt;p&gt;The result returned to &lt;code&gt;0&lt;/code&gt;, and the full Atlas collection returned to seven documents.&lt;/p&gt;

&lt;h2&gt;
  
  
  When this workflow is useful
&lt;/h2&gt;

&lt;p&gt;This type of Mongo Sync job makes sense when I want to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;copy selected local collections to an Atlas development or staging environment;&lt;/li&gt;
&lt;li&gt;keep a cloud copy updated while continuing to write locally;&lt;/li&gt;
&lt;li&gt;move a collection without repeatedly exporting and importing JSON files;&lt;/li&gt;
&lt;li&gt;test an application against Atlas using recent local data;&lt;/li&gt;
&lt;li&gt;synchronize only the collections needed for a specific workflow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important word is *&lt;strong&gt;&lt;em&gt;selected&lt;/em&gt;&lt;/strong&gt;*. This was a collection-level sync, not a complete production migration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgs5hoieozvptjgpsd2sy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgs5hoieozvptjgpsd2sy.png" alt="VisuaLeaf Mongo Sync monitor showing live inserts, updates, and deletes." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Live MongoDB sync from local replica set to Atlas.&lt;/p&gt;

&lt;h2&gt;
  
  
  How this differs from other Atlas migration methods
&lt;/h2&gt;

&lt;p&gt;MongoDB provides other tools for different jobs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;mongodump&lt;/code&gt; and &lt;code&gt;mongorestore&lt;/code&gt; are useful for a self-managed, one-time transfer using a BSON dump.&lt;/li&gt;
&lt;li&gt;Atlas Live Migration is designed for moving a deployment into Atlas and managing a production cutover with minimal downtime.&lt;/li&gt;
&lt;li&gt;This Mongo Sync workflow focuses on copying selected collections and continuing to apply their changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Atlas recommends its Live Migration workflow when performing a supported production migration. It uses MongoDB’s &lt;code&gt;mongosync&lt;/code&gt; technology underneath and includes a cutover process for redirecting applications to the Atlas destination. (&lt;a href="https://www.mongodb.com/docs/atlas/live-migration/" rel="noopener noreferrer"&gt;Atlas Live Migration&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;I would not present a collection-level sync as a replacement for that process.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would still verify before using it in production
&lt;/h2&gt;

&lt;p&gt;This small test verified document copying and three change types. It did not test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;high write volume or very large collections;&lt;/li&gt;
&lt;li&gt;long network interruptions and recovery;&lt;/li&gt;
&lt;li&gt;schema changes while the job was running;&lt;/li&gt;
&lt;li&gt;independent writes made to both source and target;&lt;/li&gt;
&lt;li&gt;users, roles, indexes, validation rules, or other database metadata;&lt;/li&gt;
&lt;li&gt;a full application cutover from local MongoDB to Atlas.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I treated the local collection as the source of truth and used an empty target collection. That kept the test easy to understand and avoided conflicts with existing Atlas documents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final takeaway
&lt;/h2&gt;

&lt;p&gt;The first copy was easy to check: 7 local documents appeared in Atlas.&lt;/p&gt;

&lt;p&gt;But the more important part was what happened after that. When I inserted, updated, and deleted a local document, the same changes appeared in Atlas without exporting another file or recreating the job.&lt;/p&gt;

&lt;p&gt;Because both sides were MongoDB, I could use pass-through mode and keep the original document structure.&lt;/p&gt;

&lt;p&gt;For development, staging, testing, or selected collection sync, this is a practical way to move local MongoDB data to Atlas and keep it updated. For a full production migration, I would still follow MongoDB’s official migration process and plan the cutover separately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Want to try it?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://visualeaf.com/" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs7aaysdkhl0hzivzdm6k.png" alt="CTA Image" width="799" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://visualeaf.com/" rel="noopener noreferrer"&gt;Download for Free and test Mongo Sync with your own local MongoDB database and Atlas cluster.&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.mongodb.com/docs/manual/changestreams/" rel="noopener noreferrer"&gt;MongoDB change streams&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.mongodb.com/docs/atlas/mongo-shell-connection/" rel="noopener noreferrer"&gt;Connect to a MongoDB Atlas cluster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.mongodb.com/docs/atlas/security/add-ip-address-to-list/" rel="noopener noreferrer"&gt;Manage the Atlas IP access list&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.mongodb.com/docs/atlas/live-migration/" rel="noopener noreferrer"&gt;Atlas Live Migration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.mongodb.com/docs/database-tools/mongorestore/" rel="noopener noreferrer"&gt;&lt;code&gt;mongorestore&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>mongodb</category>
      <category>database</category>
      <category>software</category>
      <category>javascript</category>
    </item>
    <item>
      <title>7 MongoDB Query Mistakes That Return the Wrong Results</title>
      <dc:creator>VisuaLeaf</dc:creator>
      <pubDate>Tue, 14 Jul 2026 09:39:05 +0000</pubDate>
      <link>https://dev.to/visualeaf/7-mongodb-query-mistakes-that-return-the-wrong-results-5faj</link>
      <guid>https://dev.to/visualeaf/7-mongodb-query-mistakes-that-return-the-wrong-results-5faj</guid>
      <description>&lt;p&gt;MongoDB queries look simple. You type a field, give it a value, hit run, and you get your data back.&lt;/p&gt;

&lt;p&gt;But just because a query runs without throwing an error doesn't mean it worked right. Sometimes you get a blank screen. Sometimes you get way too many records. Other times, the data looks fine at first glance, but it doesn't actually match what you asked for.&lt;/p&gt;

&lt;p&gt;Most of these slip-ups happen for one basic reason: the query structure doesn't match the way the data actually sits in the database.&lt;/p&gt;

&lt;p&gt;To show you what we mean, we’ll use a clinic database with a collection called &lt;code&gt;visits&lt;/code&gt;. Here is what a typical document looks like:&lt;/p&gt;

&lt;p&gt;JSON&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "_id": "6871b6f9c3f1d1a4c2a10001",
  "status": "completed",
  "visitDate": "2026-07-01T09:30:00.000Z",
  "patient": { "name": "Anna Keller", "age": 34 },
  "doctor": { "name": "Dr. James Carter", "specialty": "Cardiology" },
  "symptoms": ["cough", "fever"],
  "prescriptions": [
    { "name": "Ibuprofen", "active": false },
    { "name": "Paracetamol", "active": true }
  ],
  "invoice": { "paid": true, "method": "card", "total": 250 }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can run these examples right in the &lt;a href="https://visualeaf.com/blog/mongodb-shell-visual-output/" rel="noopener noreferrer"&gt;VisuaLeaf MongoDB Shell&lt;/a&gt;. Using visual tools makes a big difference because you can see exactly what MongoDB is returning in real time.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Forgetting the Curly Braces
&lt;/h2&gt;

&lt;p&gt;This is just a quick typo, but it breaks things right away.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Mistake:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;completed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Correct Query&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiu7e84hx4awafe8ue9eq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fiu7e84hx4awafe8ue9eq.png" alt="MongoDB query examples showing common mistakes with $or, $in, arrays, dates, and text search in VisuaLeaf." width="800" height="545"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;find()&lt;/code&gt; tool always expects an object. Even if you are only looking for one specific thing, you still need to wrap that condition in curly braces &lt;code&gt;{}&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Treating &lt;code&gt;$or&lt;/code&gt; Like a Regular Object
&lt;/h2&gt;

&lt;p&gt;This one trips a lot of people up because the broken version looks like it should work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Mistake:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;db.visits.find({
  $or: {
    status: "completed",
    "invoice.paid": false
  }
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What is wrong:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$or expects an array of conditions, but this query gives it one object.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The error will usually be something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MongoServerError: $or must be an array
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Correct Query&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F42cfbhf55y928ge2j9yg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F42cfbhf55y928ge2j9yg.png" alt="MongoDB Shell query using $or with separate conditions, showing matching visit records in VisuaLeaf Table View." width="800" height="545"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first query is wrong because &lt;code&gt;$or&lt;/code&gt; needs an array, not one regular object.&lt;/p&gt;

&lt;p&gt;Each condition has to be written as its own object inside &lt;code&gt;[]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The corrected query returns visits where the status is &lt;code&gt;pending&lt;/code&gt; or &lt;code&gt;completed&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Querying Nested Fields Like They Are Flat
&lt;/h2&gt;

&lt;p&gt;MongoDB documents often have fields inside other fields.&lt;/p&gt;

&lt;p&gt;In our &lt;code&gt;visits&lt;/code&gt; collection, &lt;code&gt;doctor&lt;/code&gt; is an object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;doctor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Dr. Michael Brown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;specialization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Neurology&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Query to avoid:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;doctor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Neurology&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This asks MongoDB to find a &lt;code&gt;doctor&lt;/code&gt; field that is exactly &lt;code&gt;"Neurology"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But &lt;code&gt;doctor&lt;/code&gt; is not a string. It is an object.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Better query:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;doctor.specialization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Neurology&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhs6po958xjoqt0qxj5dy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhs6po958xjoqt0qxj5dy.png" alt="MongoDB query for a nested doctor.specialization field shown in VisuaLeaf Tree View." width="799" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The dot tells MongoDB to go inside the &lt;code&gt;doctor&lt;/code&gt; object and check the &lt;code&gt;specialization&lt;/code&gt; field.&lt;/p&gt;

&lt;p&gt;So instead of asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is the whole &lt;code&gt;doctor&lt;/code&gt; field equal to Neurology?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;you are asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is the doctor’s specialization Neurology?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  4. Typing the Same Field Twice
&lt;/h2&gt;

&lt;p&gt;Say you want to find visits with doctors from Cardiology or Dermatology.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Query to avoid:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;doctor.specialization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cardiology&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;doctor.specialization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Dermatology&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Better query:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvo1fjvgc4mtvbocfj91y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvo1fjvgc4mtvbocfj91y.png" alt="MongoDB Shell query using $in to search visits by multiple values in the same field, with results shown in VisuaLeaf Table View." width="800" height="545"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In JavaScript and MongoDB, you should not reuse the same key within a single object.&lt;/p&gt;

&lt;p&gt;If you type &lt;code&gt;"doctor.specialization"&lt;/code&gt; twice, the second value can overwrite the first one. So MongoDB may only search for &lt;code&gt;Dermatology&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If one field can match more than one value, use &lt;code&gt;$in&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Treating Dates Like Plain Text
&lt;/h2&gt;

&lt;p&gt;Dates can look like regular text, but in MongoDB they are often stored as real Date values.&lt;/p&gt;

&lt;p&gt;In our &lt;code&gt;visits&lt;/code&gt; collection, the invoice date is stored inside the &lt;code&gt;invoice&lt;/code&gt; object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;issuedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ISODate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2026-05-18T10:15:00Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;So this query will not work as expected:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;db.visits.find({
  "invoice.issuedAt": "2026-05-18"
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This searches for a string, not a Date, so it will usually return nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Better query:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faeb7guvs2u7ncb45nrsn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faeb7guvs2u7ncb45nrsn.png" alt="MongoDB Shell query in VisuaLeaf using $gte and $lt with new Date() to find invoices issued on May 18, 2026, with the matching result shown in Table View." width="799" height="564"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This returns invoices created on May 18, 2026.&lt;/p&gt;

&lt;p&gt;The range matters because dates usually include time. A visit at &lt;code&gt;1:15 PM&lt;/code&gt; is still on May 18, but it is not equal to midnight.&lt;/p&gt;

&lt;p&gt;So instead of asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is the date exactly &lt;code&gt;"2026-05-18"&lt;/code&gt;?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;you are asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Is the date on or after May 18 and before May 19?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  6. Expecting Text Search to Work Automatically
&lt;/h2&gt;

&lt;p&gt;MongoDB does not search text like Google by default.&lt;/p&gt;

&lt;p&gt;In our &lt;code&gt;visits&lt;/code&gt; collection, the doctor name is stored like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;doctor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;fullName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Dr. Michael Brown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;So this query will not work:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;db.visits.find({
  "doctor.fullName": "Michael"
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This searches for an exact value. MongoDB checks if &lt;code&gt;doctor.fullName&lt;/code&gt; is exactly &lt;code&gt;"Michael"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But the real value is &lt;code&gt;"Dr. Michael Brown"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Better setup:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For text search, you need to create a text index first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createIndex&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;doctor.fullName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you can search inside the text field:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwptblq34hq57llfos5ra.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwptblq34hq57llfos5ra.png" alt="MongoDB Shell query in VisuaLeaf using $text search for “Michael,” with the matching doctor name shown in Table View." width="800" height="560"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now MongoDB can search for the word &lt;code&gt;Michael&lt;/code&gt; inside the indexed field.&lt;/p&gt;

&lt;p&gt;The important part is this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;$text&lt;/code&gt; search does not work unless the collection has a text index.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  7. Reading &lt;code&gt;$or&lt;/code&gt; Logic Backwards
&lt;/h2&gt;

&lt;p&gt;This query runs fine, but it is easy to read it *&lt;strong&gt;&lt;em&gt;the wrong way&lt;/em&gt;&lt;/strong&gt;*:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;completed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;doctor.specialization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Neurology&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;invoice.paymentStatus&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unpaid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This does &lt;strong&gt;not&lt;/strong&gt; mean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;completed OR Neurology OR unpaid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It actually means:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;completed AND (Neurology OR unpaid)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because &lt;code&gt;status&lt;/code&gt; is outside the &lt;code&gt;$or&lt;/code&gt; block, MongoDB treats it as required.&lt;/p&gt;

&lt;p&gt;So MongoDB first looks for visits where &lt;code&gt;status&lt;/code&gt; is &lt;code&gt;completed&lt;/code&gt;. Then, from those visits, it checks if the doctor is specialized in &lt;code&gt;Neurology&lt;/code&gt; or the invoice is &lt;code&gt;unpaid&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you want all three conditions to be part of the OR logic, move &lt;code&gt;status&lt;/code&gt; inside the &lt;code&gt;$or&lt;/code&gt; array.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Better query:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;visits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;$or&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;completed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;doctor.specialization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Neurology&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;invoice.paymentStatus&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unpaid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the query means:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;completed OR Neurology OR unpaid
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same fields. Different structure. Different result.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzqjs8i0zq2a1mw1zswj8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzqjs8i0zq2a1mw1zswj8.png" alt="Side-by-side VisuaLeaf MongoDB Shell screenshot comparing two $or queries, showing how moving status inside the $or array returns more matching documents." width="800" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  One More Thing to Do before Using Your Query
&lt;/h2&gt;

&lt;p&gt;A MongoDB query is only as good as your understanding of the underlying document structure.&lt;/p&gt;

&lt;p&gt;Before you spend hours rewriting a broken query, do one simple thing: &lt;a href="https://visualeaf.com/blog/how-to-explore-and-work-with-mongodb-data-visually/" rel="noopener noreferrer"&gt;&lt;strong&gt;Open a single raw document from your collection.&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Look closely at the exact spelling of field names.&lt;/li&gt;
&lt;li&gt;Check the data types (Are numbers stored as strings? Are dates stored as objects?).&lt;/li&gt;
&lt;li&gt;Map out your nested objects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is exactly where a visual tool like &lt;a href="https://visualeaf.com" rel="noopener noreferrer"&gt;&lt;strong&gt;VisuaLeaf&lt;/strong&gt;&lt;/a&gt; saves the day. Instead of guessing from your code editor, you can test your queries inside the built-in shell and instantly toggle over to the &lt;a href="https://visualeaf.com/features/browse-collections/" rel="noopener noreferrer"&gt;&lt;strong&gt;Table View&lt;/strong&gt; or &lt;strong&gt;Tree View&lt;/strong&gt;&lt;/a&gt; to see exactly how your documents are laid out in real-time. No more guessing.&lt;/p&gt;

</description>
      <category>mongodb</category>
      <category>coding</category>
      <category>software</category>
      <category>database</category>
    </item>
  </channel>
</rss>
