LookupOrderedRows
→ object[]Returns sorted rows from a Data Extension with a maximum count limit and filter criteria.
Syntax
Platform.Function.LookupOrderedRows(deName, count, orderBy, whereFieldNames, whereFieldValues)
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
deName |
string | Yes | Data Extension Name (the external key / CustomerKey is not accepted — runtime-verified) |
count |
string | number | Yes | Maximum number of rows to return; values below 1 return up to 2,000 |
orderBy |
string | Yes | Sort expression using "ColumnName ASC" or "ColumnName DESC" syntax (e.g. "LastName ASC, FirstName ASC") |
whereFieldNames |
string|string[] | Yes | Filter field name, or an array of field names connected with AND logic |
whereFieldValues |
string|array | Yes | Filter field value matching whereFieldNames; must be an array of equal length when whereFieldNames is an array |
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: Parameters
* Proves:
* 1. The documented fixed five-argument call works; the binding is not variadic.
* 2. count limits positive results; 0 and negative values remove the effective
* limit (subject to the documented 2,000-row platform maximum).
* 3. count accepts a number and a numeric string with the same limited row set.
* 4. filter names/values accept strings or positionally aligned arrays using AND.
* 5. Unequal arrays, invalid count values, bad identifiers and wrong arities throw.
* 6. The DE is resolved by Name, not CustomerKey.
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
function addField(de, name, type, len, isKey) {
var f = Platform.Function.CreateObject("DataExtensionField");
Platform.Function.SetObjectProperty(f, "Name", name);
Platform.Function.SetObjectProperty(f, "FieldType", type);
if (len) { Platform.Function.SetObjectProperty(f, "MaxLength", len); }
Platform.Function.SetObjectProperty(f, "IsPrimaryKey", isKey);
Platform.Function.SetObjectProperty(f, "IsRequired", isKey);
Platform.Function.AddObjectArrayItem(de, "Fields", f);
}
function createDE(name, key) {
var de = Platform.Function.CreateObject("DataExtension");
Platform.Function.SetObjectProperty(de, "CustomerKey", key);
Platform.Function.SetObjectProperty(de, "Name", name);
addField(de, "Id", "Text", "50", "true");
addField(de, "Grp", "Text", "50", "false");
addField(de, "Code", "Text", "50", "false");
addField(de, "Active", "Boolean", null, "false");
var st = [0, 0];
return String(Platform.Function.InvokeCreate(de, st, null));
}
function dropDE(key) {
var de = Platform.Function.CreateObject("DataExtension");
Platform.Function.SetObjectProperty(de, "CustomerKey", key);
var st = [0, 0];
return String(Platform.Function.InvokeDelete(de, st, null));
}
function ids(rows) {
var s = "", i;
if (rows === null) { return "null"; }
for (i = 0; i < rows.length; i++) { s += (i ? "," : "") + String(rows[i]["Id"]); }
return s;
}
var deName = "ssjsg_lor_parameters_name";
var deKey = "ssjsg_lor_parameters_key";
assert("control: Name and CustomerKey differ", deName === deKey ? "same" : "different", "different");
assert("setup: throwaway DE created", createDE(deName, deKey), "OK");
assert("setup: row a inserted", Platform.Function.InsertData(deName, ["Id", "Grp", "Code", "Active"], ["a", "all", "beta", "true"]), 1);
assert("setup: row b inserted", Platform.Function.InsertData(deName, ["Id", "Grp", "Code", "Active"], ["b", "all", "alpha", "false"]), 1);
assert("setup: row c inserted", Platform.Function.InsertData(deName, ["Id", "Grp", "Code", "Active"], ["c", "all", "gamma", "true"]), 1);
assert("setup: row d inserted", Platform.Function.InsertData(deName, ["Id", "Grp", "Code", "Active"], ["d", "all", "delta", "false"]), 1);
var limited = Platform.Function.LookupOrderedRows(deName, 2, "Code ASC", "Grp", "all");
assert("documented five-argument call returns two limited rows", limited.length, 2);
assert("positive number count keeps the first two sorted rows", ids(limited), "b,a");
var limitedStr = Platform.Function.LookupOrderedRows(deName, "2", "Code ASC", "Grp", "all");
assert("numeric string count '2' yields the same rows as number count 2", ids(limitedStr), "b,a");
var zero = Platform.Function.LookupOrderedRows(deName, 0, "Code ASC", "Active", "true");
assert("count 0 removes the effective limit for the matching set", ids(zero), "a,c");
var negative = Platform.Function.LookupOrderedRows(deName, -1, "Code ASC", "Active", "false");
assert("negative count removes the effective limit for the matching set", ids(negative), "b,d");
var zeroStr = Platform.Function.LookupOrderedRows(deName, "0", "Code ASC", "Active", "true");
assert("numeric string count '0' matches number count 0 for the matching set", ids(zeroStr), "a,c");
var oneArray = Platform.Function.LookupOrderedRows(deName, 10, "Code ASC", ["Grp"], ["all"]);
assert("single-element filter arrays behave like string filters", ids(oneArray), "b,a,d,c");
var multi = Platform.Function.LookupOrderedRows(deName, 10, "Code ASC", ["Grp", "Active"], ["all", "true"]);
assert("multiple filter arrays apply AND logic", ids(multi), "a,c");
var swapped = Platform.Function.LookupOrderedRows(deName, 10, "Code ASC", ["Grp", "Active"], ["true", "all"]);
assert("filter arrays are positionally aligned", swapped === null ? "null" : ids(swapped), "null");
assertThrows("more filter names than values throws", function () { return Platform.Function.LookupOrderedRows(deName, 10, "Code ASC", ["Grp", "Active"], ["all"]); });
assertThrows("more filter values than names throws", function () { return Platform.Function.LookupOrderedRows(deName, 10, "Code ASC", ["Grp"], ["all", "true"]); });
assertThrows("DEV CustomerKey form throws (docs do not restrict the identifier to Name)", function () { return Platform.Function.LookupOrderedRows(deKey, 10, "Code ASC", "Grp", "all"); });
assertThrows("a nonexistent filter column throws", function () { return Platform.Function.LookupOrderedRows(deName, 10, "Code ASC", "NoSuchColumn", "x"); });
assertThrows("a nonexistent DE throws", function () { return Platform.Function.LookupOrderedRows("ssjsg_lor_no_such_de", 10, "Code ASC", "Grp", "x"); });
assertThrows("null count throws", function () { return Platform.Function.LookupOrderedRows(deName, null, "Code ASC", "Grp", "all"); });
assertThrows("undefined count throws", function () { var u; return Platform.Function.LookupOrderedRows(deName, u, "Code ASC", "Grp", "all"); });
assertThrows("non-numeric count text throws", function () { return Platform.Function.LookupOrderedRows(deName, "nope", "Code ASC", "Grp", "all"); });
assertThrows("NaN count throws", function () { return Platform.Function.LookupOrderedRows(deName, NaN, "Code ASC", "Grp", "all"); });
assertThrows("arity 0 throws", function () { return Platform.Function.LookupOrderedRows(); });
assertThrows("arity 4 throws", function () { return Platform.Function.LookupOrderedRows(deName, 10, "Code ASC", "Grp"); });
assertThrows("arity 6 throws", function () { return Platform.Function.LookupOrderedRows(deName, 10, "Code ASC", "Grp", "all", "extra"); });
assertThrows("AMPscript-style repeated filter pairs are not variadic", function () { return Platform.Function.LookupOrderedRows(deName, 10, "Code ASC", "Grp", "all", "Active", "true"); });
assert("cleanup: throwaway DE deleted", dropDE(deKey), "OK");
</script>
Description
LookupOrderedRows is the sorted, count-limited version of LookupRows. Use it when you need:
- Results in a specific order
- Only the top N rows (e.g., most recent 10 orders)
- Pagination patterns
At runtime LookupOrderedRows returns null (not an empty array []) when no row matches — guard before reading .length. Each matched row also carries undocumented system fields _CustomObjectKey (number) and _CreatedDate (string), and the DE is resolved by Name only. A NULL Text field in a returned row is normalized to an ordinary empty string rather than the hazardous CLR null returned by scalar Lookup.
Also like LookupRows, most fields are returned as their typed/native JS value (Number and Decimal columns come back as number, Boolean columns as boolean) — useful when you need those types without manual casting. Date columns are the exception: they come back as an ISO-8601 string (e.g. "2024-01-15T00:00:00.000"), not a Date object — unlike Lookup, which returns a real Date (runtime-verified). A NULL Text field in a returned row is normalized to an ordinary empty string: strict and loose null comparisons are false, a truthiness test is safely falsy, and String() yields "". This differs from scalar Lookup, whose NULL field is a hazardous CLR null. This still contrasts with DataExtension.Rows.Retrieve(), which stringifies every field, including Number/Boolean (but gives you an empty array [] instead of null on no match).
Field type → returned JavaScript type (runtime-verified)
Result of probing a Data Extension containing one column of each valid field type via Platform.Function.LookupOrderedRows:
| DE field type | Returned type | Notes |
|---|---|---|
| Text | string |
|
| EmailAddress | string |
|
| Locale | string |
e.g. "en-US" |
| Phone | string |
|
| Number | number |
|
| Decimal | number |
|
| Boolean | boolean |
true / false |
| Date | string |
ISO-8601 string (e.g. "2024-01-15T00:00:00.000") — not a Date |
Identical to LookupRows: Date columns are stringified, unlike Lookup which returns a real Date.
Show test script — row shape, ordering, field types, nulls, and query caching
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: Description and field types
* Proves:
* 1. A match returns an indexed row array with .length and property access;
* no match returns genuine JavaScript null, not [].
* 2. ASC/DESC, comma-separated tie-breakers and case-insensitive sort tokens work.
* 3. Text/Number/Decimal/Boolean keep native types; Date is an ISO string.
* 4. Rows expose _CustomObjectKey and _CreatedDate.
* 5. A NULL Text field is returned as an ordinary empty string here — unlike
* Lookup's hazardous CLR null — while String() remains safe.
* 6. An invalid sort column/direction throws; omitted direction is accepted
* but does not provide a documented deterministic direction.
* 7. Identical repeated queries are request-cached; changing query shape sees
* a row inserted after the first query.
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function assertThrows(id, fn) {
var threw = false, msg = "";
try { fn(); } catch (ex) { threw = true; msg = ex.message; }
Platform.Response.Write((threw ? "PASS " : "FAIL ") + id + " -> " + (threw ? "threw: " + msg : "did NOT throw") + "\n");
}
function addField(de, name, type, len, scale, isKey) {
var f = Platform.Function.CreateObject("DataExtensionField");
Platform.Function.SetObjectProperty(f, "Name", name);
Platform.Function.SetObjectProperty(f, "FieldType", type);
if (len) { Platform.Function.SetObjectProperty(f, "MaxLength", len); }
if (scale) { Platform.Function.SetObjectProperty(f, "Scale", scale); }
Platform.Function.SetObjectProperty(f, "IsPrimaryKey", isKey);
Platform.Function.SetObjectProperty(f, "IsRequired", isKey);
Platform.Function.AddObjectArrayItem(de, "Fields", f);
}
function createDE(name, key) {
var de = Platform.Function.CreateObject("DataExtension");
Platform.Function.SetObjectProperty(de, "CustomerKey", key);
Platform.Function.SetObjectProperty(de, "Name", name);
addField(de, "Id", "Text", "50", null, "true");
addField(de, "Grp", "Text", "50", null, "false");
addField(de, "Code", "Text", "50", null, "false");
addField(de, "Rank", "Number", null, null, "false");
addField(de, "Amount", "Decimal", "18", "2", "false");
addField(de, "Active", "Boolean", null, null, "false");
addField(de, "WhenVal", "Date", null, null, "false");
addField(de, "NullCol", "Text", "50", null, "false");
addField(de, "BlankCol", "Text", "50", null, "false");
var st = [0, 0];
return String(Platform.Function.InvokeCreate(de, st, null));
}
function dropDE(key) {
var de = Platform.Function.CreateObject("DataExtension");
Platform.Function.SetObjectProperty(de, "CustomerKey", key);
var st = [0, 0];
return String(Platform.Function.InvokeDelete(de, st, null));
}
function ids(rows) {
var s = "", i;
if (rows === null) { return "null"; }
for (i = 0; i < rows.length; i++) { s += (i ? "," : "") + String(rows[i]["Id"]); }
return s;
}
var deName = "ssjsg_lor_description_name";
var deKey = "ssjsg_lor_description_key";
assert("setup: throwaway DE created", createDE(deName, deKey), "OK");
assert("setup: row a inserted", Platform.Function.InsertData(deName, ["Id", "Grp", "Code", "Rank", "Amount", "Active", "WhenVal", "BlankCol"], ["a", "sort", "beta", "2", "2.20", "true", "2024-01-15", ""]), 1);
assert("setup: row b inserted", Platform.Function.InsertData(deName, ["Id", "Grp", "Code", "Rank", "Amount", "Active", "WhenVal", "BlankCol"], ["b", "sort", "alpha", "1", "1.10", "false", "2024-01-16", "filled"]), 1);
assert("setup: row c inserted", Platform.Function.InsertData(deName, ["Id", "Grp", "Code", "Rank", "Amount", "Active", "WhenVal", "BlankCol"], ["c", "sort", "gamma", "2", "3.30", "true", "2024-01-17", "filled"]), 1);
assert("setup: row d inserted", Platform.Function.InsertData(deName, ["Id", "Grp", "Code", "Rank", "Amount", "Active", "WhenVal", "BlankCol"], ["d", "sort", "delta", "1", "4.40", "false", "2024-01-18", "filled"]), 1);
var asc = Platform.Function.LookupOrderedRows(deName, 10, "Rank ASC, Code DESC", "Grp", "sort");
assert("matched result has typeof object", String(typeof asc), "object");
assert("matched result exposes .length", asc.length, 4);
assert("zero-based indexing and property access work", String(asc[0]["Code"]), "delta");
assert("multi-field ASC sort discriminates ties", ids(asc), "d,b,c,a");
var desc = Platform.Function.LookupOrderedRows(deName, 10, "Rank DESC, Code ASC", "Grp", "sort");
assert("DESC plus secondary ASC reverses the rank groups deterministically", ids(desc), "a,c,b,d");
var lower = Platform.Function.LookupOrderedRows(deName, 10, "rank desc, code asc", "Grp", "sort");
assert("sort column names and ASC/DESC tokens are case-insensitive on this BU", ids(lower), "a,c,b,d");
assert("Text returns string", String(typeof asc[0]["Code"]), "string");
assert("Number returns number and supports arithmetic", String(typeof asc[0]["Rank"]) + ":" + String(asc[0]["Rank"] + 1), "number:2");
assert("Decimal returns number", String(typeof asc[0]["Amount"]), "number");
assert("Boolean returns boolean", String(typeof asc[0]["Active"]), "boolean");
assert("DEV Date returns an ISO string, not a Date object", String(typeof asc[0]["WhenVal"]) + ":" + String(asc[0]["WhenVal"]), "string:2024-01-18T00:00:00.000");
assert("DEV row exposes numeric _CustomObjectKey", String(typeof asc[0]["_CustomObjectKey"]), "number");
assert("DEV row exposes string _CreatedDate", String(typeof asc[0]["_CreatedDate"]), "string");
assert("explicit empty string remains an ordinary string", String(typeof asc[3]["BlankCol"]) + ":" + String(asc[3]["BlankCol"]), "string:");
assert("NULL field is normalized to an ordinary string in row results", String(typeof asc[0]["NullCol"]), "string");
assert("NULL field is not strict null", asc[0]["NullCol"] === null ? "true" : "false", "false");
assert("String() safely yields empty text for the normalized NULL field", String(asc[0]["NullCol"]), "");
assert("normalized NULL field loose-null comparison is false without throwing", asc[0]["NullCol"] == null ? "true" : "false", "false");
assert("normalized NULL field is safely falsy", asc[0]["NullCol"] ? "true" : "false", "false");
var none = Platform.Function.LookupOrderedRows(deName, 10, "Code ASC", "Grp", "none");
assert("DEV no match returns genuine JavaScript null", none === null ? "true" : "false", "true");
assert("no match has typeof object", String(typeof none), "object");
assert("no match String() is null", String(none), "null");
var before = Platform.Function.LookupOrderedRows(deName, 10, "Code ASC", "Grp", "cache");
assert("cache control: query before insert returns null", before === null ? "null" : ids(before), "null");
assert("cache setup: later row inserted", Platform.Function.InsertData(deName, ["Id", "Grp", "Code"], ["cache-1", "cache", "cachecode"]), 1);
var repeated = Platform.Function.LookupOrderedRows(deName, 10, "Code ASC", "Grp", "cache");
assert("identical repeated query remains stale in the same request", repeated === null ? "null" : ids(repeated), "null");
var changed = Platform.Function.LookupOrderedRows(deName, 10, "Code ASC", "Id", "cache-1");
assert("varying the query shape sees the newly inserted row", changed === null ? "null" : ids(changed), "cache-1");
var omittedDirection = Platform.Function.LookupOrderedRows(deName, 10, "Rank", "Grp", "sort");
assert("a sort expression with omitted direction is accepted", omittedDirection.length, 4);
assertThrows("a nonexistent sort column throws", function () { return Platform.Function.LookupOrderedRows(deName, 10, "NoSuchColumn ASC", "Grp", "sort"); });
assertThrows("a malformed sort direction throws", function () { return Platform.Function.LookupOrderedRows(deName, 10, "Rank SIDEWAYS", "Grp", "sort"); });
assert("cleanup: throwaway DE deleted", dropDE(deKey), "OK");
</script>
Examples
Get most recent 10 orders
var recentOrders = Platform.Function.LookupOrderedRows(
"Orders",
10, // max rows
"OrderDate DESC", // sort expression
"Status", // filter field
"complete" // filter value
);
for (var i = 0, len = recentOrders.length; i < len; i++) {
Write(recentOrders[i]["OrderID"] + " — " + recentOrders[i]["Total"] + "<br>");
}
Top 5 products by price
var topProducts = Platform.Function.LookupOrderedRows(
"Products",
5,
"Price DESC",
"Active", "1"
);
All rows sorted (no effective limit)
Use 0 for count to retrieve all matching rows in order:
var allRows = Platform.Function.LookupOrderedRows(
"Customers",
0, // returns up to 2,000
"LastName ASC",
"Active", "1"
);
Multiple filters (AND logic)
var rows = Platform.Function.LookupOrderedRows(
"CustomerData",
0,
"LastName ASC",
["PreferredLanguage", "RewardsTier"],
["English", "Silver"]
);
Show test script
<script runat="server">
Platform.Load("core", "1.1.5");
/*
* Chapter: Examples
* Proves all four documented example shapes: a descending recent-order query,
* a top-five descending price query, count 0 for all matches, and two filter
* arrays applying AND logic.
* EXPECTED OUTPUT: every line starts with PASS.
*/
function assert(id, actual, expected) {
Platform.Response.Write((actual === expected ? "PASS " : "FAIL ") + id + " -> [" + actual + "]\n");
}
function addField(de, name, type, len, isKey) {
var f = Platform.Function.CreateObject("DataExtensionField");
Platform.Function.SetObjectProperty(f, "Name", name);
Platform.Function.SetObjectProperty(f, "FieldType", type);
if (len) { Platform.Function.SetObjectProperty(f, "MaxLength", len); }
Platform.Function.SetObjectProperty(f, "IsPrimaryKey", isKey);
Platform.Function.SetObjectProperty(f, "IsRequired", isKey);
Platform.Function.AddObjectArrayItem(de, "Fields", f);
}
function createDE(name, key) {
var de = Platform.Function.CreateObject("DataExtension");
Platform.Function.SetObjectProperty(de, "CustomerKey", key);
Platform.Function.SetObjectProperty(de, "Name", name);
addField(de, "OrderID", "Text", "50", "true");
addField(de, "OrderDate", "Date", null, "false");
addField(de, "Status", "Text", "50", "false");
addField(de, "Price", "Decimal", "18", "false");
addField(de, "Active", "Boolean", null, "false");
addField(de, "LastName", "Text", "50", "false");
addField(de, "PreferredLanguage", "Text", "50", "false");
addField(de, "RewardsTier", "Text", "50", "false");
var st = [0, 0];
return String(Platform.Function.InvokeCreate(de, st, null));
}
function dropDE(key) {
var de = Platform.Function.CreateObject("DataExtension");
Platform.Function.SetObjectProperty(de, "CustomerKey", key);
var st = [0, 0];
return String(Platform.Function.InvokeDelete(de, st, null));
}
function ids(rows) {
var s = "", i;
for (i = 0; i < rows.length; i++) { s += (i ? "," : "") + String(rows[i]["OrderID"]); }
return s;
}
var deName = "ssjsg_lor_examples_name";
var deKey = "ssjsg_lor_examples_key";
assert("setup: throwaway DE created", createDE(deName, deKey), "OK");
assert("setup: order 1 inserted", Platform.Function.InsertData(deName, ["OrderID", "OrderDate", "Status", "Price", "Active", "LastName", "PreferredLanguage", "RewardsTier"], ["1", "2024-01-01", "complete", "10.00", "true", "Zulu", "English", "Silver"]), 1);
assert("setup: order 2 inserted", Platform.Function.InsertData(deName, ["OrderID", "OrderDate", "Status", "Price", "Active", "LastName", "PreferredLanguage", "RewardsTier"], ["2", "2024-01-03", "complete", "30.00", "true", "Alpha", "English", "Silver"]), 1);
assert("setup: order 3 inserted", Platform.Function.InsertData(deName, ["OrderID", "OrderDate", "Status", "Price", "Active", "LastName", "PreferredLanguage", "RewardsTier"], ["3", "2024-01-02", "pending", "20.00", "false", "Mike", "English", "Gold"]), 1);
var recent = Platform.Function.LookupOrderedRows(deName, 10, "OrderDate DESC", "Status", "complete");
assert("recent-orders example returns both complete orders", recent.length, 2);
assert("recent-orders example puts the newest complete order first", ids(recent), "2,1");
var top = Platform.Function.LookupOrderedRows(deName, 5, "Price DESC", "Active", "true");
assert("top-products example sorts active rows by descending price", ids(top), "2,1");
var all = Platform.Function.LookupOrderedRows(deName, 0, "LastName ASC", "Status", "complete");
assert("count-0 example returns every matching row in order", ids(all), "2,1");
var filtered = Platform.Function.LookupOrderedRows(deName, 0, "LastName ASC", ["PreferredLanguage", "RewardsTier"], ["English", "Silver"]);
assert("multiple-filter example applies AND logic and returns both Silver English rows", ids(filtered), "2,1");
assert("cleanup: throwaway DE deleted", dropDE(deKey), "OK");
</script>