Reorganize components in MongoDB record

Description:

Each customer object includes a name field.

A line object is comprised of the following fields:

  • inLine - an array of customers
  • currentCustomer - a customer object
  • processed - an array of customers

The 'line' collection stores documents representing line objects.


Issue at Hand:

I am working on implementing a process that includes the following steps:

  1. Add currentCustomer to the processed array
  2. Assign the 1st element in inLine to currentCustomer
  3. Remove the 1st element from inLine

Ensuring atomicity is crucial as the new value depends on the previous state of another field.

Approaches Tried:

Initial Attempt

db.collection('line').findOneAndUpdate({
    _id: new ObjectId(lineId),
}, {
    $set: {
        currentCustomer: '$inLine.0',
    },
    $pop: {
        inLine: -1,
    },
    $push: {
        processed: '$currentCustomer',
    },
});

However, the result was assigning strings like "$inLine.0" and "$currentCustomer" instead of actual values.

Aggregation Method

db.collection('line').findOneAndUpdate({
    _id: new ObjectId(lineId),
}, [{
    $set: {
        currentCustomer: '$inLine.0',
    },
    $pop: {
        inLine: -1,
    },
    $push: {
        processed: '$currentCustomer',
    },
}]);

This approach resulted in an error stating that a pipeline stage must contain exactly one field.

Multi-stage Aggregation Strategy

db.collection('line').findOneAndUpdate({
    _id: new ObjectId(lineId),
}, [{
    $set: {
        currentCustomer: '$inLine.0',
    },
}, {
    $pop: {
        inLine: -1,
    },
}, {
    $push: {
        processed: '$currentCustomer',
    },
}]);

Unfortunately, using $pop and $push led to errors of unrecognized pipeline stage names.

Efforts to solely utilize $set stages were unsuccessful and resulted in complex code that still did not resolve the issue.

Answer №1

Using turivishal's solution, the issue was addressed as follows:

db.collection('line').findOneAndUpdate({
    _id: new ObjectId(lineId),
}, [{
    $set: {
        // currentCustomer = inLine.length === 0 ? null : inLine[0]
        currentCustomer: {
            $cond: [
                { $eq: [{ $size: '$inLine' }, 0] },
                null,
                { $first: '$inLine' },
            ],
        },
        // inLine = inLine.slice(1)
        inLine: {
            $cond: [
                { $eq: [{ $size: '$inLine' }, 0] },
                [],
                { $slice: ['$inLine', 1, { $size: '$inLine' }] },
            ],
        },
        // if currentCustomer !== null then processed.push(currentCustomer)
        processed: {
            $cond: [
                {
                    $eq: ['$currentCustomer', null],
                },
                '$processed',
                {
                    $concatArrays: [
                        '$processed', ['$currentCustomer'],
                    ],
                }
            ],
        },
    },
}]);

Answer №2

Using a simple update with $push or $pop may not achieve the desired result.

Your experiment has shown that direct $push, $pop stages at the root level of aggregation are not supported. I have made adjustments to your query as follows:

  • The currentCustomer checks if the size of the inLine is 0; if so, it returns null, otherwise it retrieves the first element from the inLine array using $arrayElemAt.
  • The inLine checks the size of the inLine; if it's 0, it returns an empty array, otherwise it removes the first element from the inLine array using $slice and $size.
  • The processed concatenates both arrays using $concatArrays. It uses $ifNull to check for null fields, returning a blank array if so. If currentCustomer is null, it returns an empty array; otherwise, it returns currentCustomer.
db.collection('line').findOneAndUpdate(
  { _id: new ObjectId(lineId), }, 
  [{
    $set: {
      currentCustomer: {
        $cond: [
          { $eq: [{ $size: "$inLine" }, 0] },
          null,
          { $arrayElemAt: ["$inLine", 0] }
        ]
      },
      inLine: {
        $cond: [
          { $eq: [{ $size: "$inLine" }, 0] },
          [],
          { $slice: ["$inLine", 1, { $size: "$inLine" }] }
        ]
      },
      processed: {
        $concatArrays: [
          { $ifNull: ["$processed", []] },
          {
            $cond: [
              { $eq: ["$currentCustomer", null] },
              [],
              ["$currentCustomer"]
            ]
          }
        ]
      }
    }
  }]
);

Check out the Playground

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

What is the best way to retrieve the latest state after applying useEffect to trigger an onChange event for an element?

I am encountering an issue with an input field in my component that requires an onChange event to update the state. Despite binding the onChange event on mounting the component, the state remains unchanged as the onChange event seems to have the initial st ...

Combining switch statements from various classes

Is there a way to merge switch statements from two different classes, both with the same function name, into one without manually overriding the function or copying and pasting code? Class A: protected casesHandler(): void { switch (case){ ...

Tips for passing a distinct identifier to the following page when transitioning with Link in NextJS

I need to pass an identifier to the next page when a user navigates. On my homepage, I am fetching an array of data and using the map method to display it in a card-based UI. When a user clicks on a card, they should be taken to another page that display ...

When utilizing the multipart/form-data content type in a form, the files being sent are transmitted as blank

I am currently developing a VueJS component that functions as a form to collect user information and enable multiple image uploads. The form is set with enctype="multipart/form-data" and exhibits the following structure : <template> <div> ...

Switch between showing or hiding at least three divs

I'm currently using a simple script that toggles the visibility of two div elements: <script type='text/javascript'> $(window).load(function(){ function toggle_contents() { $('#page1').toggle(); ...

What is the best way to calculate the total sum of a column in Vue JS?

To manually add a row, each row is stored in the "items" array items: [ { occuGroup:'', constType:'', bfloorArea: 0, cfloorArea: 0 }, ], Below is the code I wrote to calculate the total: subTotal: function() { var ...

Cross-domain scripting with JavaScript

I currently manage 2 subdomains securityservices.example.com workstations.example.com All workstations are associated with the workstations.example.com One of the workstations, known as WS789012 on the domain, is fully qualified as: WS789012.workst ...

Allow frontend JavaScript to retrieve a text file that is restricted to individual users

Trying to articulate my goal is challenging, but I'll do my best to clarify. My objective is to enable JavaScript (or an alternative solution) to retrieve data from a text file on a static site and utilize that data in some way. The challenge here is ...

Transfer the values selected from checkboxes into an HTML input field

I am attempting to capture the values of checkboxes and then insert them into an input field once they have been checked. Using JavaScript, I have managed to show these values in a <span> tag successfully. However, when trying to do the same within a ...

What could be causing the script to not function properly on 3D text in Unity5?

Here is the code snippet I have been working on: function OnMouseEnter() { GetComponent(Renderer).material.color = Color.grey; } function OnMouseExit() { GetComponent(Renderer).material.color = Color.white; } I've noticed that when I apply t ...

Using Javascript to automatically submit a form when the enter key is pressed

Can anyone help me with a password form issue? I want the enter key to trigger the submit button but it's not working for me. I understand that the password can be viewed in the source code, this is just for practice purposes. <!DOCTYPE html> & ...

Incorporate npm libraries into vanilla JavaScript for HTML pages

Attempting to incorporate an npm package into plain JS/HTML served using Python and Flask. <script type="module"> import { configureChains, createClient } from "./node_modules/@wagmi/core"; import { bsc } from "./nod ...

Looking for a script to automate clicking on a specific web element using JavaScript

When working with Selenium, we utilize an action to interact with an element by clicking on it at specific coordinates (X, Y). action.MoveToElement(element, X, Y).Click().Build().Perform() I am looking to achieve the same functionality using Javascript. ...

What could be causing my textarea-filling function to only be successful on the initial attempt?

Programming: function updateText() { $("body").on("click", ".update_text", function(event) { $(this).closest("form").find("textarea").text("updated text"); event.preventDefault(); }); } $(document).ready(function() { updateText(); }); H ...

I am currently having trouble with req.query not functioning correctly within Next.js for reading query parameters

I am facing an issue while working with a REST API in Next.js 13. I have created the API, which can be accessed at http://localhost:3000/api/portfolio. However, when I try to filter the data using query parameters like http://localhost:3000/api/portfolio?s ...

Retrieving JSON data to create and showcase an HTML table

Can you help me figure out what's going wrong with my code? I have an HTML page with a table where I fetch data from the web in JSON format using JavaScript. The logic works perfectly when the fetch code is let to run on its own, but when I try to ex ...

Tips for triggering an event from a function instead of the window

Everything is functioning as expected, with the event listener successfully capturing the custom event when it is dispatched from the window and listened for as loading, all seems to be working well. const MyLib = mylib(); function mylib() { const re ...

Express, the mongoose package delivers a response of an empty array

I have encountered a common issue regarding pluralization with mongoose default model names. Despite trying various solutions, the problem persists as I am getting an empty array as the result. In my local mongoDB database, there are 2 documents in the "us ...

Comparing Two Arrays in AngularJS and Disabling on Identical Cases

I currently have two tables: "Available subject" and "Added subject" which are populated by ng-repeat with two separate lists of objects. My objective is to accomplish three things: 1 - When the user clicks on the plus sign, a row with identical content s ...