Week 4 • Module 8

N/search Module

NetSuite's native search API for programmatic data retrieval

🔍 search.create() vs Saved Searches

N/search offers two approaches: create dynamic searches in code, or load existing saved searches from the UI.

Approach Pros Cons
search.create() Version controlled, dynamic filters More code, can't preview in UI
search.load() Testable in UI, non-dev can modify External dependency, migration needed

🔧 Creating a Search

const search = require('N/search');

const customerSearch = search.create({
    type: search.Type.CUSTOMER,
    filters: [
        ['isinactive', 'is', 'F'],
        'AND',
        ['balance', 'greaterthan', 0]
    ],
    columns: [
        search.createColumn({ name: 'entityid' }),
        search.createColumn({ name: 'companyname' }),
        search.createColumn({ name: 'email' }),
        search.createColumn({ name: 'balance', sort: search.Sort.DESC })
    ]
});

// Execute and iterate
customerSearch.run().each(result => {
    const name = result.getValue({ name: 'companyname' });
    const balance = result.getValue({ name: 'balance' });
    log.audit('Customer', `${name}: $${balance}`);
    return true; // Continue to next result
});

🎯 Filters

Filters use a three-part array: [fieldId, operator, value]

// Simple filter
['isinactive', 'is', 'F']

// Multiple filters with AND/OR
[
    ['isinactive', 'is', 'F'],
    'AND',
    ['subsidiary', 'anyof', [1, 2, 3]],
    'AND',
    [
        ['email', 'isnotempty', ''],
        'OR',
        ['phone', 'isnotempty', '']
    ]
]

Common Operators

OperatorUse Case
isExact match
anyofMatch any in list
containsText contains
greaterthan / lessthanNumeric comparison
withinDate ranges
isnotemptyHas a value

📊 Columns

// Basic column
search.createColumn({ name: 'companyname' })

// With sorting
search.createColumn({ name: 'balance', sort: search.Sort.DESC })

// Join column (related record)
search.createColumn({ name: 'city', join: 'billingaddress' })

// Summary/aggregate
search.createColumn({ name: 'amount', summary: search.Summary.SUM })

🔗 Joins

Access fields from related records using the join parameter:

// Get customer's primary contact email
const customerSearch = search.create({
    type: search.Type.CUSTOMER,
    columns: [
        search.createColumn({ name: 'companyname' }),
        search.createColumn({ name: 'email', join: 'contact' })
    ]
});

⚠️ Finding Join Names

Use the Records Browser or existing saved searches to discover valid join names. Common joins: entity, customer, item, transaction, contact, billingaddress.

📖 Loading Saved Searches

// Load by internal ID
const savedSearch = search.load({ id: 'customsearch_active_customers' });

// Modify filters dynamically
savedSearch.filters.push(
    search.createFilter({
        name: 'datecreated',
        operator: search.Operator.ONORAFTER,
        values: ['1/1/2024']
    })
);

// Execute
savedSearch.run().each(result => {
    // Process results
    return true;
});

🔄 Iterating Results

run().each() Pattern

// Return true to continue, false to stop
search.run().each(result => {
    const id = result.id;
    const value = result.getValue({ name: 'companyname' });
    return true; // Process up to 4000 results
});

runPaged() for Large Results

const pagedData = customerSearch.runPaged({ pageSize: 1000 });

pagedData.pageRanges.forEach(pageRange => {
    const page = pagedData.fetch({ index: pageRange.index });
    page.data.forEach(result => {
        // Process each result
    });
});

📖 Finding This in the Docs

To look up N/search methods:

  1. Go to docs.oracle.com → SuiteScript 2.x Modules
  2. Click on N/search Module
  3. Review search.create(), search.load(), Filter, and Column objects

Key pages to bookmark:

🎯 Key Takeaways