Chidi Okwudire IT Professional. ERP Enthusiast. NetSuite Certified (Administrator, SuiteCloud Developer II, and ERP Consultant). Celigo Certified (Level 4+). Passionate About Empowerment Through Knowledge Sharing. Always Eager to Learn.

Understanding SuiteScript 2. x Joins

5 min read

NetSuite supports the concept of joins, i.e., accessing data from a linked record. Have you ever found yourself second-guessing what syntax to use to produce your desired results? I have! And, in this article, I’ll share some tips to help you get your joins right.

TL;DR

  • NetSuite supports two join syntaxes: the “dot” notation of join.name and the more verbose notation of { "join": "field_on_current_record", "name": "field_on_joined_record" }
  • The key to doing proper joins is to correctly interpret the keywords: “join” is always a field on the base record that you’re searching; “name” is a field on the joined record.
  • When using the search.lookupFields function, only the dot notation is supported. See the summary at the end of this article for which syntax is supported where.

NetSuite Joins By Example

While the concept itself is straightforward, especially if you have any experience working with relational databases, there is something about the way joins are expressed in NetSuite that keeps getting me confused. This article is intended to help you (and my future self) better understand NetSuite’s join syntaxes.

In the previous section, I already explained the key to thinking about joins correctly and it is worth reiterating:

Independent of which join syntax you use (dot vs. verbose), "join" always refers to a field on the base record and "name" always refers to a field on the joined record.

Let’s now look at a few examples to drive this point home and highlight some nuances. Our example scenario is that we are searching for purchase orders and want to get some information from the vendor associated with a purchase order. In this scenario:

  • Base record: Purchase Order (technically, Transaction)
  • Joined record: Vendor

Note that in the examples below, I use the require function [I]NetSuite (October 2, 2015). Require function. Available at: https://netsuite.custhelp.com/app/answers/detail/a_id/45054. [Accessed on: June 11, 2021] so that you’re able to run the examples directly in the console. Be sure to switch the define function [II]NetSuite (October 2, 2015). Define Function. Available at: https://netsuite.custhelp.com/app/answers/detail/a_id/43801. [Accessed on: June 11, 2021] if you’re working in a script file.

Get all purchase orders for vendors with a given category. As output, we want to see both the PO # and the Vendor name.

require(["N/search"], function (search) {
  var poSearchObj = search.create({
    type: search.Type.PURCHASE_ORDER,
    filters: [{
      name: 'mainline',
      operator: search.Operator.IS,
      values: 'T'	  
    },{
      join: 'vendor',
	  name: 'category',
      operator: search.Operator.ANYOF,
      values: 5
    }],
    columns: [
	  search.createColumn({
        name: 'tranid'
	  }),
      search.createColumn({
		join: 'vendor',
        name: 'companyname'
      })
    ]
  });
  
  poSearchObj.run().each(function(result){
     console.log('PO #: ' + result.getValue({name: 'tranid'}) + ' | '
		+ result.getValue({join: 'vendor', name: 'companyname'}));
     return true;
  });
});

The above example is as verbose as it can get; we’ll show a more compact syntax shortly.

Notice that in lines 9 and 19, the join is vendor. As per our convention, this refers to a field on the base record that we’re running our search on. To avoid confusing myself, I like to write the join line before the name line as opposed to most examples where the name is written first. (Remember: join.name is the winning formula.)

However, if you look at any PO in the NetSuite UI, the internal ID of the Vendor field is actually entity not vendor!

Finding a Join Field Name

In most cases, the field ID in the UI is the same as the join table name but, as illustrated by this example, that is not always the case! This is a common mistake when doing joins: Do not assume the join table name. Instead, go to the SuiteScript Records Browser[III]NetSuite (June 15, 2018). The SuiteScript Records Browser. Available at: https://netsuite.custhelp.com/app/answers/detail/a_id/74610. [Accessed on: June 11, 2021], find the record in question, and see the name listed in the “Search Join” section.

For this example, when we look up the joins for the Transaction type (the base type from which Purchase Orders derive) in the records browser, we see that the Vendor join is called vendor not entity. Please note that the list of joins in the records browser might now always be complete. For instance, item is not listed as a join for the Transaction record though, in fact, it is a valid join as explained here.

Lesson 1: Do not depend on field IDs to discover join names. Instead, refer to the “Search Join” section of the corresponding record type in the SuiteScript Records Browser.

Example 2: Search Using Dot Notation

The following search is a more compact equivalent of the search in the previous example. It applies the dot notation and other shorthand formats, yet produces the exact same results:

require(["N/search"], function (search) {
  var poSearchObj = search.create({
    type: search.Type.PURCHASE_ORDER,
    filters: [
	  ['mainline', search.Operator.IS, 'T'],
      'AND',
      ['vendor.category', search.Operator.ANYOF, 5]
    ],
    columns: [
	  'tranid',
      'vendor.companyname'
    ]
  });
  
  poSearchObj.run().each(function(result){
     console.log('PO #: ' + result.getValue('tranid') + ' | '
		+ result.getValue({'join': 'vendor', 'name': 'companyname'}));
     return true;
  });
});

Notice how we’ve removed a lot of boilerplate code by switching to filter expressions in the filters section instead of using search.createFilter(). Also, we’ve expressed our columns by name only instead of using search.createColumn(). This is much more readable (at least to me)! If you’re not familiar with filter expressions, I highly recommend this article [IV]Eric T. Grubaugh (July 3, 2017). Search Filters in SuiteScript 2.0. Available at: https://stoic.software/effective-suitescript/10-search-filters/ [Accessed on June 11 , 2021] by Eric T. Grubaugh on searching in SuiteScript 2.0 where, among others, he covers filter expressions.

Did you notice that although we were able to get away with the dot notation on lines 7 and 11, we were not able to use it on line 17 when accessing the results via result.getValue()?

At the time of writing, the result.getValue() function does not support the dot notation for joins. You could try the following result.getValue('vendor.companyname') but it will return null. This is one of those API inconsistencies that we have to live with.

Lesson 2: The dot notation (join.name) does not work with the result.getValue() function. Use the { "join": "field_on_current_record", "name": "field_on_joined_record" } syntax instead.

Finally, let’s look at a lookup example. search.lookupFields()[V]NetSuite (August 7, 2015. search.lookupFields(). Available at: https://netsuite.custhelp.com/app/answers/detail/a_id/43711 [Accessed on: June 11, 2021] offers a quick (and cheap, in terms of governance units) way to search for one or more body fields on a record. This function supports joins via the dot syntax.

Going back to our previous example, let’s suppose we know the internal ID of the target purchase order and we want to get the vendor’s company name and transaction # as before. The lookup search could look like this:

require(["N/search"], function (search) {
  var poLookup = search.lookupFields({
    type: search.Type.PURCHASE_ORDER,
	id: 805757,
    columns: [
	  'tranid',
      'vendor.companyname'
    ]
  });
  
  console.log(JSON.stringify(poLookup)); // poLookup is a JavaScript object
  // Sample result: {"tranid":"PO19","vendor.companyname":"MyCom LLC."}
  
  if (poLookup.tranid) {
    console.log('PO #: ' + poLookup['tranid'] + ' | '
		+ poLookup['vendor.companyname']);
  }
});

Notice that, as with regular searches, we can express the join in the columns section using the dot notation (the verbose notation does not work here). Also, since the output of the lookup is a JavaScript object, we must use the dot notation to retrieve the joined field.

Lesson 3: When using search.lookupFields(), the dot notation is the only supported syntax for performing joins.

Wrap Up

The following table summarizes which join syntax can be used in what parts of a search:

Search FiltersSearch ColumnsSearch Results
Regular SearchBoth dot and verbose join syntaxesBoth dot and verbose join syntaxesOnly verbose join syntax
Lookup SearchNot applicableOnly dot join syntaxOnly dot join syntax
Summary of Join Syntax Support

As I stated earlier, my personal preference is to use the dot syntax as much as possible as I find it more compact and easier to understand. Moreover, it enjoys wider support than the verbose syntax as you can see in the preceding table.

Finally, while we’re at it, one of the long-standing limitations of NetSuite’s search module is that joins are limited to one level. If you need multi-level joins and/or control over the kind of join to do (e.g. inner vs. outer), the N/query module [VI]NetSuite (August 28, 2018). Scripting with the N/query Module. Available at: https://netsuite.custhelp.com/app/answers/detail/a_id/77545 [Accessed on: June 11, 2021] is your friend. This article provides a good comparison of N/search and N/query.


I hope you found this article insightful. Keep learning, keep sharing and remember to subscribe to get an email notification as soon as we publish new NetSuite Insights and freebies.


Related Posts

Further Reading[+]

Chidi Okwudire IT Professional. ERP Enthusiast. NetSuite Certified (Administrator, SuiteCloud Developer II, and ERP Consultant). Celigo Certified (Level 4+). Passionate About Empowerment Through Knowledge Sharing. Always Eager to Learn.

2 Replies to “Understanding SuiteScript 2. x Joins”

  1. Amazing article, once again. I have one comment:

    “At the time of writing, the result.getValue() function does not support the dot notation for joins. You could try the following result.getValue(‘vendor.companyname’) but it will return null. This is one of those API inconsistencies that we have to live with.”

    The standard approach I take in this situation is to reference the column by index. (Which also comes with its risks.) So, I would replace line 17 with:
    result.getValue(pOSearchObj.columns[1]);

    Keep it up!

    1. Thanks, Elie! The columns approach will also work indeed. Though, knowing myself, I’ll probably forget, do some refactoring and break things 🙂

      I appreciate your engagement and valuable feedback.

Leave a Reply

Your email address will not be published. Required fields are marked *

×