Take Advantage of Map/Reduce Script

One of the coolest new feature introduced with SuiteScript 2.0 is the Map/Reduce script type.  Map/Reduce provides parallel processing, dynamic governance handling and yielding and ability to break out business process into separate logical segments.  Only downfall to Map/Reduce script is that it doesn’t make that big of a difference if you don’t have SuiteCloud Plus License.  NetSuite SuiteCloud Plus License grants multiple script queues (1 SC+ License grants 5 script processing queues) which allows multiple scripts to run simultaneously.  However, recently NetSuite announced that they will grant one additional complimentary queue for those without SC+ license starting 2018.1 release.  With 2 processing queues, it is time to take advantage of Map/Reduce script!!!

In this blog post, we’d like to give you sample business use case which can be implemented with simple Map/Reduce script.

Sample Business Use Case:
You have large number of Sales Orders that needs to be invoiced on a scheduled basis.

Code Implementation

1. Create Map/Reduce Script code
This sample script only use Map stage since the use case only requires that we generate an invoice from each eligible sales order.

/**
 * @NApiVersion 2.x
 * @NScriptType MapReduceScript
 * @NModuleScope SameAccount
 */
define(['N/error', 
        'N/record', 
        'N/runtime',
        'N/search'],
/**
 * @param {email} email
 * @param {error} error
 * @param {record} record
 * @param {runtime} runtime
 * @param {search} search
 */
function(error, record, runtime, search) 
{
   
	/**
	 * Map/Reduce Script:
	 * Sample Map/Reduce script for blog post.  
	 */
	
	
    /**
     * Marks the beginning of the Map/Reduce process and generates input data.
     *
     * @typedef {Object} ObjectRef
     * @property {number} id - Internal ID of the record instance
     * @property {string} type - Record type id
     *
     * @return {Array|Object|Search|RecordRef} inputSummary
     * @since 2015.1
     */
    function getInputData() 
    {   
    	//Dynamically create Saved Search to grab all eligible Sales orders to invoice
    	//In this example, we are grabbing all main level data where sales order status are 
    	//any of Pending Billing or Pending Billing/Partially Fulfilled
    	return search.create({
    		'type':search.Type.SALES_ORDER,
    		'filters':[
    		           	['mainline', search.Operator.IS, true],
    		           	'and',
    		           	['status', search.Operator.ANYOF, ['SalesOrd:E', 'SalesOrd:F']]
    		          ],
    		'columns':[
    		           	'internalid',
    		           	'transactionnumber',
    		           	'statusref',
    		           	'entity',
    		           	search.createColumn({
    		           		'name':'trandate',
    		           		'sort':search.Sort.ASC
    		           	})
    		          ]
    	});
    }

    /**
     * Executes when the map entry point is triggered and applies to each key/value pair.
     *
     * @param {MapSummary} context - Data collection containing the key/value pairs to process through the map stage
     * @since 2015.1
     */
    function map(context) 
    {
    	log.debug('context', context.value);
    	
    	//Text value of context.value that was passed in to map stage from getInputData stage.
    	//	Each result from search is returned as JSON object. 
    	//	Depending on what you are returning from search, your JSON object will look different
    	/**
    	{
    		"recordType":"salesorder",
    		"id":"171566",
    		"values":{
    			"internalid":{
    				"value":"171566",
    				"text":"171566"
    			},
    			"transactionnumber":"21210",
    			"statusref":{
    				"value":"pendingBilling",
    				"text":"Pending Billing"
    			},
    			"entity":{
    				"value":"8113",
    				"text":"xxxxxx"
    			},
    			"trandate":"12/21/2017"
    		}
    	}
    	*/
    	
    	var rowJson = JSON.parse(context.value);
    	
    	//Transform salesorder into an invoice
    	var invrec = record.transform({
    		'fromType':record.Type.SALES_ORDER,
    		'fromId':rowJson.values['internalid'].value,
    		'toType':record.Type.INVOICE
    	});
    	
    	//Let's save it 
    	var invoiceid = invrec.save({
    		'enableSourcing':true,
    		'ignoreMandatoryFields':true
    	});
    	
    	log.debug('generated invoice id', invoiceid);
    }

    /**
     * Executes when the summarize entry point is triggered and applies to the result set.
     *
     * @param {Summary} summary - Holds statistics regarding the execution of a map/reduce script
     * @since 2015.1
     */
    function summarize(summary) 
    {
    	log.debug('Summary Time','Total Seconds: '+summary.seconds);
    	log.debug('Summary Usage', 'Total Usage: '+summary.usage);
    	log.debug('Summary Yields', 'Total Yields: '+summary.yields);
    	
    	log.debug('Input Summary: ', JSON.stringify(summary.inputSummary));
    	log.debug('Map Summary: ', JSON.stringify(summary.mapSummary));
    	log.debug('Reduce Summary: ', JSON.stringify(summary.reduceSummary));
    	
    	//Grab Map errors
    	summary.mapSummary.errors.iterator().each(function(key, value) {
			log.error(key, 'ERROR String: '+value);
			
			
			return true;
		});
    	
    }

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

1. Create Map/Reduce Script code
Upload your script to NetSuite and it will automatically identify it as Map/Reduce script.

You can now deploy your script and set additional setting for this Map/Reduce script.

This is just a simple example of using Map/Reduce script.  We only used Map stage in this example since we only had to create an invoice off of sales order.  If you have more complex business logic, you can utilize Reduce stage as well.

Even with one additional queue, Map/Reduce script will allow you to process data twice as fast.  We are certainly looking forward to 2018.1 so that all of our customers can take advantage of this feature.

Do you have large number of complex business process automated? or Improve existing ones? Give us a call!

About Abaci
Abaci is a premier NetSuite Solution Provider with deep knowledge in both business process and technology architecture. Our team is passionate about NetSuite and has an exclusive commitment to NetSuite and to providing the highest level of system expertise on its full suite of cloud-based business management applications.