NetSuite Map/Reduce: Why getInputData() Should Do Almost Nothing
Need help applying this in your account?
Quick answer
The getInputData() function in a NetSuite Map/Reduce script defines the work to be done, not how to do it. It should return a data source object: a saved search, a SuiteQL query, or a plain array. The Map/Reduce framework reads this data source and distributes each item as an input key-value pair to parallel map() invocations. getInputData() runs once, synchronously, in a single thread before any parallelism begins. Placing record loads, business logic, or data processing inside getInputData() defeats the purpose of the framework: the entire processing cost runs in that one synchronous call rather than being distributed across parallel map() executions. The correct pattern is to return a search.createSearch() result or a SuiteQL result object from getInputData() and let the framework handle pagination and distribution to map() automatically. Returning a raw array from getInputData() is valid for small fixed datasets, but for dynamic data always use a search or SuiteQL result.
What Is the Purpose of getInputData() in Map/Reduce?
Most Map/Reduce documentation focuses on the map() and reduce() stages, where the actual processing happens. But getInputData() is where the most consequential design decisions are made, and where the most common performance mistakes occur.
getInputData() runs in a single execution context, before NetSuite distributes any work. Whatever happens in getInputData() cannot benefit from Map/Reduce's parallel processing. It runs once, sequentially, on one thread.
Its job is simple: tell NetSuite what records need to be processed. Return a data source, and let the framework take it from there.
What Do Many Scripts Do Incorrectly in getInputData()?
A common pattern uses getInputData() to run a search, loop through every result, and build an array before passing it to map():
function getInputData() {
var data = [];
search.load({ id: 'customsearch_orders' })
.run()
.each(function(result) {
data.push(result.id);
return true;
});
return data;
}This works. But by the time map() starts, all the search execution has already happened in a single thread. The script has loaded and iterated every result before NetSuite has had any opportunity to distribute the workload.
If the search returns 10,000 records, getInputData() processes all 10,000 sequentially. Then map() distributes what's left. The stage that was supposed to benefit from parallelism has already done the heavy lifting before parallelism begins.
What Is the Correct Approach for getInputData()?
Instead of executing the search inside getInputData(), return the search object itself:
function getInputData() {
return search.load({
id: 'customsearch_orders'
});
}When you return a Search object, NetSuite's Map/Reduce framework handles execution and distribution. The framework fetches results in pages and hands them to map() workers in parallel. The script no longer pre-executes anything, it simply identifies what needs to be processed.
The same applies when using the N/query module. Return the Query object, not the executed results:
function getInputData() {
return query.create({
type: query.Type.SALES_ORDER
}); // NetSuite handles execution
}Why Does getInputData() Design Matter for Governance?
getInputData() runs under the standard scheduled script governance limits. When you pre-execute a large search inside it, you consume governance units before any parallel processing begins.
By returning a Search or Query object instead, you defer execution to the Map/Reduce framework, which handles pagination and distribution more efficiently. The result is lower governance consumption in getInputData(), more work offloaded to map() workers, and a script that scales to larger datasets without hitting limits.
What Should Stay Out of getInputData()?
Beyond pre-executing searches, these patterns in getInputData() undermine the framework:
- Loading records to inspect them before building the work list: if you need to filter records, add the condition to the search filter, not to a loop in
getInputData() - Calling external APIs to build the input set: external calls block the single
getInputData()execution; move them tomap()where each worker handles its own call - Performing business logic: any transformation or calculation that can be deferred to
map()should be deferred
The simpler getInputData() is, the more work the framework can distribute.
What Analogy Explains the Map/Reduce Pattern?
Think of Map/Reduce as a warehouse operation. getInputData() is the manager who creates the work order list. map() workers are the employees who execute each item on the list independently.
The manager's job is to write the list, not to start working through it before handing it off.
When getInputData() pre-executes the search, it is as if the manager processes half the orders before any workers have arrived. The work that was supposed to be distributed has already been done sequentially.
What Should getInputData() Never Do?
getInputData() should return one of:
- A
Searchobject,search.load()orsearch.create() - A
Queryobject,query.create() - A small, known array when the input set is genuinely fixed and small
It should not execute searches, load records, or perform business logic.
The best getInputData() functions are often the ones that do the least.
Need help applying this in your account?
We work with post-go-live NetSuite accounts every day. Tell us what you're working on.