What is the best way to update a collection item while maintaining non-existing values in the query?

Consider this straightforward example, where an item is saved as:

{
  "test": {
    "a1": "a1"
  }
}

When collection.updateOne is called with $set and the following object:

await collection.updateOne(doc, {$set: {"test": {"a2": "a2"}}}, {upsert: true});
it updates the saved test object to:

{
  "test": {
    "a2": "a2"
  }
}

The key a1 will no longer exist since it is not present in the sent object.

The objective is to update saved items with the values given in the object **without** removing non-existing values. For instance,

{"test": {"a2": "a2"}}
would result in updating the item to:

{
  "test": {
    "a1": "a1",
    "a2": "a2"
  }
}

A function has been devised to recursively parse objects/arrays and construct a query for this purpose.

However, there are doubts regarding its correctness and clarity:

// The function attempts to parse arrays and objects recursively and recreate the query
function buildUpdateQuery(object, prefix = '') 
{
    let updateQuery = {};

    for (let [key, value] of Object.entries(object))
    {
        let newPrefix = prefix ? `${ prefix }.${ key }` : key;

        if (Array.isArray(value)) 
        {
            for (let i = 0; i < value.length; i++) 
            {
                let arrayPrefix = `${newPrefix}.${i}`;
                if (typeof value[i] === 'object' && value[i] !== null)
                    updateQuery = {...updateQuery, ...buildUpdateQuery(value[i], arrayPrefix)};
                else
                    updateQuery[arrayPrefix] = value[i];
            }
        }
        else if (typeof value === 'object' && value !== null)
            updateQuery = {...updateQuery, ...buildUpdateQuery(value, newPrefix)};
        else
            updateQuery[newPrefix] = value;
    }

    return updateQuery;
}

export const set = async (req, res /*key, object*/) =>   // Using mongodb and express
{
    try
    {
        const [collection, key] = req.body.key.split("|");
        if (collection === undefined || key === undefined)
            return res ? res.send('fail') : 'fail'

        const doc = buildUpdateQuery(req.body.object);
        let r = await db.collection(collection).updateOne({ [key]: { $exists: true } }, { $set: doc }, { upsert: true });

        r = r.modifiedCount ? 'ok' : 'fail';
        return res ? res.send(r) : r
    }
    catch (error)
    {
        console.log(`\n[set]\n${ JSON.stringify(req.body, null, 2) }\n\n`, error);
        return res ? res.send('fail') : 'fail'
    }
}

Invoke the set function like so:

{"key": "vars|test", "object": {"test": {"a2": "a2"}}}

at

const doc = buildUpdateQuery(req.body.object);

doc resolves to {test.a2: 'a2'} updating

{"test": {"a1": "a1"}}
to
{"test": {"a1": "a1"},{"a2":"a2"}}

There is concern that this could potentially disrupt a collection by inaccurately parsing data. Alternative methods have been considered, such as retrieving the existing object first:

const document = await db.collection(collection).findOne({ [key]: { $exists: true } });

and then applying the modifications, although this approach may not be ideal.

Is there a native solution in MongoDB for this scenario? If not, is the implementation of buildUpdateQuery accurate or is there a preferable alternative?

Answer №1

If you use the $set stage in an aggregation pipeline, you can achieve the desired outcome.

For instance, consider a document already stored in the database that needs updating:

{
   test: {
      a1: "a1"
   }
}

You can execute an update with {upsert: true} to either append properties to the existing object or create a new one if no matching document is found.

await collection.updateOne({
  test: {
    a1: "a1"
  }
},
[
  {
    $set: {
      test: {
        a2: "a2"
      }
    }
  }
],
{
  upsert: true
});
  1. Take a look at THIS EXAMPLE to see how the update adds properties to an existing matched object.
  2. Check out THIS EXAMPLE for a demonstration of creating a new object when there's no matching document and upsert: true is utilized.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Interactive Div Updates Dropdown Selections Automatically

// function to add set of elements var ed = 1; function new_server() { ed++; var newDiv = $('#server div:first').clone(); newDiv.attr('id', ed); var delLink = '<a class="btn btn-danger" style="text-align:right;m ...

Secure the data by encrypting it in the frontend before decrypting it in the

How can I achieve the highest level of security in this situation? I have experimented with using the same public key for all users to encrypt data transmitted over HTTPS to my backend automatically. However, individuals could potentially intercept and d ...

What is the best way to refresh flexslider after it has been updated via AJAX?

At first, my slider is functional upon loading the page. However, after making an ajax call and receiving new slides to populate the slider, it becomes deactivated (as expected) https://i.sstatic.net/LC0yG.png Is there a method to reinitialize the flexsl ...

Verify that a certain number of checkboxes are left unchecked

I have a collection of checkbox input elements in my HTML: <input type="checkbox" id="dog_pop_123"> <input type="checkbox" id="cat_pop_123"> <input type="checkbox" id="parrot_pop_123"> My requirement is to check if none of these checkbo ...

Is there a way to efficiently create an ng-repeat for multiple divs?

I am looking to iterate through a collection of divs, as mentioned below. Each object in the list will have content-data and a link to an image. The code provided shows individual entries for each list item, but I would like to retrieve it from the angular ...

What to do when the 'image' property in Next.js next/image has an implicit 'any' type and needs fixing?

I'm a newcomer to Next.js and TypeScript and I can't seem to find any helpful resources on resolving this particular issue: import Image from 'next/image'; export default function Item({ image }) { // <-- parameter image needs a &ap ...

Encountering a syntax error while utilizing a JavaScript variable in a jQuery selector for a lightbox feature

I'm currently working on creating a lightbox feature and have reached the stage where I am implementing next and previous buttons to navigate through images. I am utilizing console.log to check if the correct href is being retrieved when the next butt ...

Update the contents of a webpage using AJAX

Can anyone help me with loading likes into a div using AJAX every second? I want the page's URL to be likes.php?p=0 on page load, likes.php?p=1 after 1 second, likes.php?p=2 after 2 seconds, and so on. This is my code snippet: var loadLikes = func ...

React-Redux-Saga: Only plain objects are allowed for actions. Consider using custom middleware for handling asynchronous actions

Struggling to integrate redux-saga into my react app, I keep encountering this error: Actions must be plain objects. Use custom middleware for async actions. The error appears at: 15 | changeText = event => { > 16 | this.props.chan ...

Error encountered while attempting to import a Bootstrap JavaScript file in webpack

I am currently working on a Gridsome project (v0.7.23) where I have loaded the Bootstrap framework via npm. In this project, I am using node v14.18.0 through nvm. However, when attempting to import a Bootstrap JS component (specifically 'collapse&apo ...

Applying limits and sorting to populated sub-documents in Mongoose

Having trouble sorting and limiting a nested sub doc, I've explored various solutions without success. System const systemSchema = mongoose.Schema({ data: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Data' }], ...other }) ...

How to handle Object data returned asynchronously via Promises in Angular?

Upon receiving an array of Question objects, it appears as a data structure containing question categories and questions within each category. The process involves initializing the object with board: JeopardyBoard = new JeopardyBoard();. Subsequently, popu ...

Obtain the dimensions of the outermost layer in a JSON file

Could you please help me extract the dimensions (600*600) of the outermost layer from the JSON below dynamically? { "path" : "shape\/", "info" : { "author" : "" }, "name" : "shape", "layers" : [ { ...

What could be causing my Ajax JSON data to not return accurately despite appearing correctly in the console logs?

My function is designed to retrieve a number (1,2,3, etc.) based on latitude and longitude coordinates: function getNumber(lat,lng) { var params="lat="+lat+"&long="+lng; $.ajax({ type: "POST", url: "https://www.page.com/cod ...

Is it possible to check dynamically if a string contains multiple substring matches?

Currently, I am in the process of developing a search suggest feature that will provide the best match based on certain criteria. Below is the code snippet along with my explanatory comments. /* string = {"Canna Terra PLUS 50 Litres", "Canna Vega ...

Determine whether the user has authorized the website with long-term access to obtain location information in the browser

Everything is running smoothly as I call navigator.geolocation.getCurrentPosition in my web app. Users are guided to a screen that explains the benefits of sharing their location, and once they press a button, the request is initiated without any issues. ...

Problem with the operation of SetInterval/ClearInterval loop

While I am aware that this question has been addressed previously, none of the solutions provided seemed to resolve my specific issue. The problem lies within my timer function which, upon invocation, is supposed to utilize setInterval to run every second ...

How to Access Data Attribute in React TypeScript on Click Event

Recently, I encountered a situation with my React component where I have a button with a click handler that utilizes the data-* attribute. In the past, with regular React, extracting the value from the data-* attribute was straightforward. However, as I am ...

What is the best way to create a circular to square gradient and save it within a two-dimensional array?

Can anyone guide me on creating a circle/square gradient and storing the data in a 2D array? I want to incorporate this gradient with simplex-noise to develop a procedural island by subtracting the gradient from the generated noise. Here are some visual re ...

Upload a JSON file to a server using a JavaScript app

I am in the process of creating a basic JavaScript application that allows users to annotate images stored on their machine and save these annotations as a JSON file. This application is lightweight and straightforward, not requiring an app server. Howeve ...