Reducing the key in MongoDB to a single value

I am trying to create a MapReduce program that counts the number of orders by clients within the last year, month, and week.

var mapOrders = function() {
    var v_order = {
        order_date : this.dt_order
        ...
    }; 

     emit(this.clientid, v_order);
};

var reduceOrders = function(p_clientid, p_orders) {
    // Initialize counters for output format
    var r_result = { orders_count : {
        total: {
            1year: 0,
            1month: 0,
            7day: 0
        }
        ...
    }}

    for (var c_order = 0; c_order < p_orders.length; c_order++) {
        // Increment counters
    }

    return (r_result);
};

db.orders.mapReduce(
    mapOrders,
    reduceOrders,
    { 
        out: { merge: "tmp_orders_indicators" }
    }
)

The resulting collection contains two types of records:

{
    "_id": 80320,
    "value": {
        "order_date": ISODate("2015-10-30T11:09:51.000Z")
        ...
    }
}

{
    "_id": 80306,
    "value": {
        "orders_count": {
            "total": {
                "count_1year": 18,
                "count_1month": 6,
                "count_7day": 1
            }
            ...
        }
}

Clients with only one order do not pass through the reduce function, as per MongoDB documentation's explanation:

MongoDB will not call the reduce function for a key that has only a single value.

To ensure all records go through the reduce function and have a consistent output structure like this, is there a way to achieve that?

{
    "_id": 80306,
    "value": {
        "orders_count": {
            "total": {
                "count_1year": 18,
                "count_1month": 6,
                "count_7day": 1
            }
            ...
        }
}

Answer №1

To seamlessly achieve the desired outcome, you can utilize aggregation functions in MongoDB. Take a look at the pipeline below:

var dateSevenDaysAgo = new Date();
dateSevenDaysAgo.setDate(dateSevenDaysAgo.getDate()-7);

var dateMonthAgo = new Date();
dateMonthAgo.setMonth(dateMonthAgo.getMonth()-1);

var dateYearAgo = new Date();
dateYearAgo.setFullYear(dateYearAgo.getFullYear()-1);

var pipeline = [
    { "$match": { "$dt_order": { "$gte": dateYearAgo } } },
    {
        "$group": {
            "_id": "$id_client",
            "count_1year": {
                "$sum": {
                    "$cond": [ 
                        { "$gte": [ "$dt_order", dateYearAgo ] }, 
                        1, 0 
                    ]
                }
            },
            "count_1month": {
                "$sum": {
                    "$cond": [ 
                        { "$gte": [ "$dt_order", dateMonthAgo ] }, 
                        1, 0 
                    ]
                }
            },
            "count_7day": {
                "$sum": {
                    "$cond": [ 
                        { "$gte": [ "$dt_order", dateSevenDaysAgo ] }, 
                        1, 0 
                    ]
                }
            }
        }
    },
    { "$out": "tmp_indicators" }
];

db.orders.aggregate(pipeline);
db.tmp_indicators.find();

Answer №2

Discovered a resolution by utilizing the finalize utility.

var processOrdersMap = function() {
    var orderVariant = {
        order_date : this.dt_order
        ...
    }; 

     emit(this.clientid, orderVariant);
};

var reduceOrdersList = function(clientId, ordersList) {
    // Initializing the structure for output counters
    var resultObject = { orders_count : {
        total: {
            1year: 0,
            1month: 0,
            7day: 0
        }
        ...
    }}

    for (var currentOrder = 0; currentOrder < ordersList.length; currentOrder++) {
        // Increase counters
    }

    return (resultObject);
};


var completeOrdersData = function(clientId, reducedOrders) {

    if (typeof reducedOrders.orders_count === 'undefined' )
        // Initializing the structure for output counters
        var resultObject = { orders_count : {
            total: {
                1year: 0,
                1month: 0,
                7day: 0
            }
        ...
        }}

        // Perform same operations as in the loop within reducer
    }
    else {
        resultObject = reducedOrders
    }

    return (resultObject);
};

db.orders.mapReduce(
    processOrdersMap,
    reduceOrdersList,
    { 
        out: { merge:  "tmp_orders_indicators" },
        finalize : completeOrdersData
    }
)

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

Transitioning from using a jQuery get request to utilizing Vue.js

Looking to transition from JQuery-based js to Vue as a newcomer to JavaScript. Seeking guidance on making a get request and displaying the retrieved data. What approach would you recommend for this task? Here's the HTML snippet: <div> <di ...

Leverage JavaScript variables within PHP

Seeking assistance on using javascript variable in php. After researching various resources such as stackoverflow and google, I have yet to find a suitable solution. Although aware of the distinction between client-side scripting in js and server-side proc ...

Issue with Vue.js: The input value text is not being updated

In my current setup, I am dealing with an input component that is linked to a variable called 'searchText' in the parent component. This variable stores the text value of the search input. The {{searchText}} in the template updates accurately bas ...

I am looking to send data to an API whenever the user closes the browser tab

Hey guys, I need some help with a problem I'm facing in my current scenario. I am working on a feature to lock bookings for other admins, and I have created a field in the bookings table to store the admin's ID when they access the booking. Howev ...

What are some methods for deactivating linked clicks while activating alternative links?

I am trying to prevent a link from being clickable after it has been clicked. Here is the code I have: <a class="cmd-7" href="?cmd=7" style="color:#00F; margin-left:15px; text-decoration:underline">Past 7 Days</a> <a class="cmd-14" href="? ...

Customize your MUI Select to show the selected value in a different way within the Input field

I have a collection of objects and I am looking to link multiple attributes of the object in MenuItem, but I only want one attribute to be displayed in Select https://i.sstatic.net/kDKLr.png The image above displays "10-xyz" in the select display, when i ...

The integration of Material-UI Autocomplete and TextField causes google autocomplete to activate

I am currently working on integrating the Autocomplete component into my project. However, I am facing an issue where the browser's autofill/autocomplete feature kicks in after some time. Is there a way to disable this behavior? ...

The Bootstrap 4 Modal has a one-time activation limit

It seems that I mistakenly created two modals. The issue arises when I open either of them for the first time and then close it, as from that point on, neither modal is able to be opened again by performing the same action that initially worked. https://i ...

Node.js encounters an error when the response is undefined

I'm a beginner programmer and I'm facing some issues with the server's response. Essentially, I am using Express to handle it, but unfortunately, it returns 'undefined' in request.body on the client server file. Here is the route ...

Updating the innerHTML of a frameset

I have a unique challenge that involves loading the frameset of www.example.com/2.html when accessing www.example.com/en/test/page.html. Successfully accomplished this task. Now, after loading 2.html in the frameset, I want to modify ".html" to ".html" t ...

Using Java to write scripts - executing JavaScript from a server-side Java class file in version 1.5

I receive three different types of GET requests from a mobile device to a class file on my web application. Since the mobile device does not provide any cookies, the log file only captures: in.ter.nal.ip ser.ver.i.p:port 2009-06-05 09:14:44 GET / ...

React: I am looking to incorporate two API calls within a single function

I am currently learning ReactJS and focusing on implementing pagination. My goal is to fetch page data from a server using an API, with Loopback being the tool for API integration. The initial setup involves displaying a list of 10 entries on the first pag ...

Hover over elements to reveal D3.js tool tips

Currently, I am utilizing a tutorial for graph tooltips and finding it to be very effective: http://bl.ocks.org/d3noob/c37cb8e630aaef7df30d It is working seamlessly for me! However, I have encountered a slight problem... In the tutorial, the graph disp ...

Modify the array dynamically within the Factory

I am currently working on a simple app where I want to display embed videos with the click of a button. It was quite challenging for me to dynamically bind my embed players, but I managed to do it successfully. I created a factory that contains my data in ...

There was a TypeError that occurred because the object did not have the 'ajax' method available

When attempting to initialize the quoteResults Javascript function on my Wordpress site, I encounter the following error in my console: Uncaught TypeError: Object #<Object> has no method 'ajax' I have successfully utilized the code below ...

Loading data into a database using JSON format with JavaScript

I need to extract data from a JSON String stored in a JavaScript variable and use it to generate an SQL script. How can I loop through the JSON string to produce an output like the following? INSERT INTO table VALUES ('$var1', '$var2', ...

blur event triggered on a cell within a table

Currently, I am using the code snippet below to populate data using a data table. My goal is to be able to edit the data in one of the columns and then validate the value after editing using the onblur event. I attempted to call the onblur event on the t ...

What is the process for sending data to a node server using POST and receiving a responseText from it through GET?

Here is an example where I'm trying to send data from a JavaScript client to an Express Node server, validate the data against the database on the server, and respond once the verification process is complete. The data object in newHttpRequest.send(da ...

Do asynchronous tasks in Node.js follow a synchronous path of execution?

Here is a code snippet to consider: var some_expensive_task = function(i) { setTimeout(function() { var a = 1; while (a < 100000000) { Math.sqrt(a); ++a; } console.log('finished set&apo ...

When trying to access data from a MongoDB query using Node.js HTTP GET method, the information does not show up in either Post

Recently, I've been working on a node.js backend project that involves interacting with a MongoDb database. However, I encountered an issue where the data retrieved from the query doesn't display in my browser when using res.send() or res.json(). ...