Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The NetSuite Pro

The NetSuite Pro Logo The NetSuite Pro Logo

The NetSuite Pro Navigation

  • Home
  • About Us
  • Tutorials
    • NetSuite Scripting
    • Advanced PDF Templates in NetSuite
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask A Question
  • Home
  • About Us
  • Tutorials
    • NetSuite Scripting
    • Advanced PDF Templates in NetSuite
  • Blog
  • Contact Us
Home/ NetSuite Scripting/Map/Reduce Script Basics in NetSuite (SuiteScript 2.1 Guide)

Map/Reduce Script Basics in NetSuite (SuiteScript 2.1 Guide)

🔹 Introduction

Map/Reduce scripts are built for handling large volumes of records in NetSuite. They divide work into four stages and automatically handle governance (pause and resume if limits are exceeded).

They’re ideal for:

  • Bulk updates
  • Data transformation
  • Aggregations (grouping data, totals, summaries)
  • Import/export processes

🔹 The 4 Stages of Map/Reduce

  1. getInputData → Define where the data comes from.
  2. map → Process each record individually.
  3. reduce → Combine or group results by key.
  4. summarize → Clean-up, log results, handle errors, send notifications.

🔹 Example 1: Simple Template

/**
 *@NApiVersion 2.1
 *@NScriptType MapReduceScript
 */
define(['N/search'], (search) => {
    // Step 1: Provide input data (customers from a search)
    const getInputData = () => {
        try {
            return search.create({
                type: search.Type.CUSTOMER,
                filters: [['isinactive', 'is', 'F']],
                columns: ['internalid', 'entityid', 'email']
            });
        } catch (e) {
            log.error('Error in getInputData', e.message);
        }
    };

    // Step 2: Runs once per input (each customer)
    const map = (context) => {
        try {
            const result = JSON.parse(context.value);
            log.debug('Map Stage', `Customer ID: ${result.id}, Name: ${result.values.entityid}`);
            // Pass the email forward to the reduce stage
            context.write({
                key: result.id,
                value: result.values.email
            });
        } catch (e) {
            log.error('Error in map', e.message);
        }
    };

    // Step 3: Runs for each unique key from map stage
    const reduce = (context) => {
        try {
            log.debug('Reduce Stage', `Key: ${context.key}, Values: ${context.values}`);
        } catch (e) {
            log.error('Error in reduce', e.message);
        }
    };

    // Step 4: Runs once at the end – summarize results, log usage, handle errors
    const summarize = (summary) => {
        try {
            log.debug('Summary', `Usage consumed: ${summary.usage}`);
            if (summary.inputSummary.error) log.error('Input Error', summary.inputSummary.error);
            summary.mapSummary.errors.iterator().each((key, error) => {
                log.error('Map Error', `Key: ${key}, Error: ${error}`);
                return true;
            });
            summary.reduceSummary.errors.iterator().each((key, error) => {
                log.error('Reduce Error', `Key: ${key}, Error: ${error}`);
                return true;
            });
        } catch (e) {
            log.error('Error in summarize', e.message);
        }
    };

    return { getInputData, map, reduce, summarize };
});

🔹 Example 2: Mass Update Email Domain

/**
 *@NApiVersion 2.1
 *@NScriptType MapReduceScript
 */
define(['N/search', 'N/record'], (search, record) => {
    // Step 1: Search for customers with old test.com emails
    const getInputData = () => {
        try {
            return search.create({
                type: search.Type.CUSTOMER,
                filters: [['email', 'contains', '@test.com']],
                columns: ['internalid', 'email']
            });
        } catch (e) {
            log.error('Error in getInputData', e.message);
        }
    };

    // Step 2: Send each customer record forward to reduce
    const map = (context) => {
        try {
            const result = JSON.parse(context.value);
            context.write({
                key: result.id,
                value: result.values.email
            });
        } catch (e) {
            log.error('Error in map', e.message);
        }
    };

    // Step 3: Update each customer’s email in reduce stage
    const reduce = (context) => {
        try {
            const custId = context.key;
            let email = context.values[0];
            const newEmail = email.replace('@test.com', '@example.com');

            // More efficient than load+save
            record.submitFields({
                type: record.Type.CUSTOMER,
                id: custId,
                values: { email: newEmail }
            });

            log.debug('Updated', `Customer ${custId} email changed to ${newEmail}`);
        } catch (e) {
            log.error('Error in reduce', e.message);
        }
    };

    // Step 4: Summarize results
    const summarize = (summary) => {
        try {
            log.debug('Summary', `Total usage: ${summary.usage}, Yields: ${summary.yields}`);
        } catch (e) {
            log.error('Error in summarize', e.message);
        }
    };

    return { getInputData, map, reduce, summarize };
});

🔹 Example 3: Count Sales Orders by Customer

/**
 *@NApiVersion 2.1
 *@NScriptType MapReduceScript
 */
define(['N/search'], (search) => {
    // Step 1: Search for Sales Orders (mainline only)
    const getInputData = () => {
        try {
            return search.create({
                type: search.Type.SALES_ORDER,
                filters: [['mainline', 'is', 'T']],
                columns: ['entity']
            });
        } catch (e) {
            log.error('Error in getInputData', e.message);
        }
    };

    // Step 2: For each order, write out the customer ID with value = 1
    const map = (context) => {
        try {
            const result = JSON.parse(context.value);
            const customerId = result.values.entity.value;
            context.write({
                key: customerId,
                value: 1
            });
        } catch (e) {
            log.error('Error in map', e.message);
        }
    };

    // Step 3: Reduce groups orders by customer and count them
    const reduce = (context) => {
        try {
            let totalOrders = 0;
            context.values.forEach(() => {
                totalOrders++;
            });
            log.debug('Orders by Customer', `Customer ${context.key} has ${totalOrders} orders.`);
        } catch (e) {
            log.error('Error in reduce', e.message);
        }
    };

    // Step 4: Final summary logging
    const summarize = (summary) => {
        try {
            log.debug('Summary', `Script completed. Usage: ${summary.usage}`);
        } catch (e) {
            log.error('Error in summarize', e.message);
        }
    };

    return { getInputData, map, reduce, summarize };
});

🔹 Best Practices

  • Keep getInputData lightweight — use searches, not huge in-memory arrays.
  • Use map for record-level processing, reduce for grouping/aggregation.
  • Use submitFields() for fast updates, avoid full load + save when possible.
  • Handle errors with try–catch and log them in summarize.
  • Don’t worry about governance — NetSuite auto-yields and resumes.

✅ Key Takeaway

Map/Reduce scripts are the workhorses for scaling in NetSuite. With the four stages — getInputData, map, reduce, summarize — you can safely process thousands of records without hitting limits.

Share
  • Facebook

Sidebar

Ask A Question

Stats

  • Questions 6
  • Answers 6
  • Best Answers 0
  • Users 2
  • Popular
  • Answers
  • Rocky

    Issue in running a client script in NetSuite SuiteScript 2.0 ...

    • 1 Answer
  • admin

    How can I send an email with an attachment in ...

    • 1 Answer
  • admin

    How do I avoid SSS_USAGE_LIMIT_EXCEEDED in a Map/Reduce script?

    • 1 Answer
  • admin
    admin added an answer The issue is usually caused by following Wrong script file… September 14, 2025 at 10:33 pm
  • admin
    admin added an answer Steps to send an Invoice PDF by email: define(['N/email', 'N/render',… August 28, 2025 at 3:05 am
  • admin
    admin added an answer This error means your script hit NetSuite’s governance usage limit… August 28, 2025 at 3:02 am

Top Members

Rocky

Rocky

  • 1 Question
  • 21 Points
Begginer
admin

admin

  • 5 Questions
  • 2 Points

Trending Tags

clientscript netsuite scripting suitescript

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help

Footer

© 2025 The NetSuite Pro. All Rights Reserved