nlapi allows to use joins to refer to values in another record or a sublist, when searching for a record. E.g. if there is a record FOO
FOO: {
type: 'rectype',
field1: '111',
sublists:
bar: [
{value: 1,}, {value: 2,}
]
}
I can search for this record with a query, provided there is a join, let's say, bar to the bar sublits:
nlapiSearchRecord('rectype', null,
[
['field', 'equalto', 'bar',],
'and',
['bar.value', 'equalto', '1',],
],
[ new nlobjSearchColumn('anotherfield', null, null), ]
);
This works. And the question is: is there a way to find a record (the one from the example) by specifying more sublist values, like:
nlapiSearchRecord('rectype', null,
[
['field', 'equalto', 'bar',],
'and',
['bar.value', 'equalto', '1',],
'and',
['bar.value', 'equalto', '2',],
],
[ new nlobjSearchColumn('anotherfield', null, null), ]
);
I've tried numerous approaches with different expressions, but it finds nothing with such a query.
A more concrete example is to find a bomrevision, which has specific components and bomquantities (the commented code is just for demonstration purposes of some approaches with the filter expression I've tried):
nlapiSearchRecord('bomrevision', null, [
['isinactive', 'equalto', 'F',],
'and',
['component.item', 'is', '4942',],
'and',
['component.item', 'is', '4936',],
//[
// 'and',
// ['component.bomquantity', 'equalto', '38',],
//],
//'and',
//[
// ['component.item', 'anyof', '4936',],
// 'and',
// ['component.bomquantity', 'equalto', '38',],
//],
],
[ new nlobjSearchColumn('name', null, null), ]
);
Thank you
I understand your question to be "How can I find items where the component list includes multiple specified items?"
The challenge here is that when you join to the component list, NetSuite returns a result for each line in the list. This means any line that contains 'Item 1' will not contain 'Item 2' and vice versa, so no result meets the criteria.
You can work around this using the following approach:
Group the results by 'main' or 'parent' record ('FOO' or 'bomrevision' in your examples).
Use NS_CONCAT to give a field in each result that contains values from all the sublist results. (Note that NS_CONCAT is undocumented, so use at your own risk).
Use summary criteria formulae to inspect the concatenated field for each value of interest.
Example (tested with assembly items and their member items as I don't have access to an account with BOM revisions):
[
["type","anyof","Assembly"],
"AND",
["min(formulatext: NS_CONCAT({memberitem.name}) )","contains","ITEM 1"],
"AND",
["min(formulatext: NS_CONCAT({memberitem.name}))","contains","ITEM 2"]
]
Or, for your 'bomrevision' (untested, adapted from above):
[
["min(formulatext: NS_CONCAT({component.item}) )","contains","4942"],
"AND",
["min(formulatext: NS_CONCAT({component.item}))","contains","4936"]
]
Be aware that under some circumstances NetSuite will default to returning the text of a list field rather than the value. If this is the case (and the values shown in your example are internal IDs as I'm assuming), you may need to replace component.item with component.item.id, or change the search values to the text name instead.
There may be a more straightforward way of doing this so I'd be glad to hear from anyone with a better solution.
Edit in response to request in comments:
Here is the full code of a working example in my account:
var assemblyitemSearch = nlapiSearchRecord("assemblyitem",null,
[
["type","anyof","Assembly"],
"AND",
["min(formulatext: NS_CONCAT({memberitem.name}) )","contains","ITEM 1"],
"AND",
["min(formulatext: NS_CONCAT({memberitem.name}))","contains","ITEM 2"]
],
[
new nlobjSearchColumn("itemid",null,"GROUP").setSort(false),
new nlobjSearchColumn("displayname",null,"GROUP"),
new nlobjSearchColumn("salesdescription",null,"GROUP"),
new nlobjSearchColumn("formulatext",null,"MIN").setFormula("NS_CONCAT({memberitem})")
]
);
This is an Item search filtering by Member Items, as the closest thing in my account to the BOM Revisions in the example. The only change I have made is to replace the item names with "ITEM 1" and "ITEM 2". You would need to replace these with relevant items from your system.
I can go to a Sales Order page and open developer tools on my browser and add the above code to create a search, then append the following additional code to retrieve results and print out to the console:
var data = [];
assemblyitemSearch.forEach(r => {
var vals = {};
r.rawValues.forEach(v => {
vals[v.name] = v.value
});
data.push(vals);
});
console.table(data);
This returns a table with results exactly as expected.
Edit 2: Adding example using Sales Orders and Items as possibly more consistent between different accounts.
var salesorderSearch = nlapiSearchRecord("salesorder",null,
[
["type","anyof","SalesOrd"],
"AND",
["trandate","within","thismonth"],
"AND",
["min(formulatext: NS_CONCAT({item}))","contains","ITEM 1"],
"AND",
["min(formulatext: NS_CONCAT({item}))","contains","ITEM 2"]
],
[
new nlobjSearchColumn("trandate",null,"GROUP"),
new nlobjSearchColumn("tranid",null,"GROUP"),
new nlobjSearchColumn("formulatext",null,"MIN").setFormula("NS_CONCAT({item})")
]
);
Is this possible somehow to get a file history (all related changesets) with API request if the file was branched or/and renamed?
For example, if I need to find a history of the object in Azure DevOps UI I can search this object in the project, in a certain path like this:
And if I need to find the first appearance of the object in a repository, I can get it by expanding a "branch and rename" history
There is a need to get this information via API requests.
I had tried to find some API requests which can do it, but found only the requests which can return only the changesets which are on the first picture, where the object has the same name and is located under the path defined in the search parameter - there is no information about renaming/branching operations.
GET https://dev.azure.com/Contoso/_apis/tfvc/changesets?api-version=6.0&searchCriteria.itemPath=$/Contoso/Trunk/Main/Metadata/Application_Ext_Contoso/Application_Ext_Contoso/AxSecurityPrivilege/Entity.xml
returns only 3 changesets - 2162, 2161, 391
POST https://dev.azure.com/Contoso/Contoso/_api/_versioncontrol/history?api-version=6.0
With the body request
{
"repositoryId":"",
"searchCriteria":"{\"itemPaths\":[\"$/Contoso/Trunk/Main/Metadata/Application_Ext_Contoso/Application_Ext_Contoso/AxSecurityPrivilege/Entity.xml\" ], \"followRenames\" : true ,\"top\":50}",
"includeSourceRename" : true
}
Also returns only 3 changesets, it only finds a specific item path, I tried to experiment with includeSourceRename and followRenames , but they do not work as I expected.
POST https://almsearch.dev.azure.com/Contoso/Contoso/_apis/search/codesearchresults?api-version=6.0-preview.1
with the body
{
"searchText": "Entity.xml",
"$skip": 0,
"$top": 25,
"filters": {
"Project": [
"Contoso"
],
"Repository": [
"$/Contoso"
],
"Path": [
"$/Contoso/"
]
},
"$orderBy": [
{
"field": "filename",
"sortOrder": "ASC"
}
],
"includeFacets": true
}
Also returns information only about 3 changesets.
Are there some approaches to get this information from the API request?
I can able to search the case by company name
var mySearch = search.create({
type: search.Type.SUPPORT_CASE,
columns: [{
name: 'title'
}, {
name: 'company'
}],
filters: [{
name: 'company',
operator: 'is',
values: 'Test'
}]
});
return mySearch.run({
ld: mySearch.id
}).getRange({
start: 0,
end: 1000
});
But I am not able to search case by company id.
companyId is 115
Below are not working
i)
filters: [{
name: 'company',
operator: 'is',
values: 115
}]
ii)
filters: [{
name: 'companyid',
operator: 'is',
values: 115
}]
According to the Case schema company is a Text filter, meaning you would have to provide it with the precise Name of the company, not the internal ID.
Instead you may want to use the customer.internalid joined filter to provide the internal ID. Also, Internal ID fields are nearly always Select fields, meaning they do not accept the is operator, but instead require the anyof or noneof operator.
You can find the valid operators by field type on the Help page titled Search Operators
First, you can try this :
var supportcaseSearchObj = search.create({
type: "supportcase",
filters:
[
["company.internalid","anyof","100"]
],
columns:
[
search.createColumn({
name: "casenumber",
sort: search.Sort.ASC
}),
"title",
"company",
"contact",
"stage",
"status",
"profile",
"startdate",
"createddate",
"category",
"assigned",
"priority"
]
});
Second : how did I get this ? The answer is hint that will make your life easier :
Install the "NetSuite Saved Search Code Export" chrome plugin.
In Netsuite UI, create your saved search (it is always easier that doing it in code).
After saving the search, open it again for edition.
At the top right corner (near list, search menu in the netsuite page), you will see a link "Export as script" : click on it and you will get your code ;)
If you can not install the chrome plugin :
In Netsuite UI, create your saved search (it is always easier that doing it in code).
In your code, load your saved search
Add a log.debug to show the [loadedesearchVar].filters
You can then copy what you will see in the log to use it as your search filters.
Good luck!
I'm trying to understand if it would actually be more efficient to read the entire document from Azure DocumentDb than it is to read a property that may have multiple objects in it?
Let's use this basketball team object as an example:
{
id: 123,
name: "Los Angeles Lakers",
coach: "Byron Scott",
players: [
{ id: 24, name: "Kobe Bryant" },
{ id: 3, name: "Anthony Brown" },
{ id: 4, name: "Ryan Kelly" },
]
}
If I want to get only a list of players, is it more efficient/faster for me to read the entire team document from which I can extract the players OR is it better to send SQL statement and try to read only the players from the document?
Returning only the players will be more efficient on the network, as you're returning less data. And, you should also be able to look at the Request Units burned for your query.
For example, I put your document into one of my collections and ran two queries in the portal (and if you do the same, and look at the bottom of the portal, you'll see the resulting Request Unit cost). I slightly modified your document with unique ID and quotes around everything, so I could load it via the portal:
{
"id": "basketball123",
"name": "Los Angeles Lakers",
"coach": "Byron Scott",
"players": [
{ "id": 24, "name": "Kobe Bryant" },
{ "id": 3, "name": "Anthony Brown" },
{ "id": 4, "name": "Ryan Kelly" }
]
}
I first selected just player data:
SELECT c.players FROM c where c.id="basketball123"
with an RU cost of 2.2:
I then asked for the entire document:
SELECT * FROM c where c.id="basketball123"
with an RU cost of 2.24:
Note: Your document size is very small, so there's really not much difference here. But at least you can see that returning a subset costs less than returning the entire document.
I am using MongoDB and Node.js to display a record set in a page. I have got as far as displaying them on the page alphabetically, but I would like to display one row (the "default" row) at the top, and all the others alphabetically beneath it.
I know, I know, Mongo is definitely not SQL, but in SQL I would have done something like this:
SELECT *
FROM themes
ORDER BY name != "Default", name ASC;
or perhaps even
SELECT * FROM themes WHERE name = "Default"
UNION
SELECT * FROM themes WHERE name != "Default" ORDER BY name ASC;
I have tried a few variations of Mongo's sorting options, such as
"$orderby": {'name': {'$eq': 'Default'}, 'name': 1}}
but without any luck so far. I have been searching a lot for approaches to this problem but I haven't found anything. I am new to Mongo but perhaps I'm going about this all wrong.
My basic code at the moment:
var db = req.db;
var collection = db.get('themes');
collection.find({"$query": {}, "$orderby": {'name': 1}}, function(e, results) {
res.render('themes-saved', {
title: 'Themes',
section: 'themes',
page: 'saved',
themes: results
});
});
You cannot do that in MongoDB, as sorting must be on a specific value already present in a field of your document. What you "can" do is $project a "weighting" to the record(s) matching your condition. As in:
collection.aggregate(
[
{ "$project": {
"each": 1,
"field": 1,
"youWant": 1,
"name": 1,
"weight": {
"$cond": [
{ "$eq": [ "$name", "Default" ] },
10,
0
]
}
}},
{ "$sort": { "weight": -1, "name": 1 } }
],
function(err,results) {
}
);
So you logically inspect the field you want to match a value in ( or other logic ) and then assign a value to that field, and a lower score or 0 to those that do not match.
When you then $sort on that "weighting" first in order ( decending from highest in this case ) so that those values are listed before others with a lower weighting.