4
public class BOM_Container_TreeController {
    @AuraEnabled
    public static String getTreeData(String acId){
        List<HS_Sales__Bill_Of_Materials__c> boms = [Select Id , Name from HS_Sales__Bill_Of_Materials__c WHERE Id =: acId];
        List<HS_Sales__Bill_Of_Materials_Item__c>bomItems = [Select Id, HS_Sales__Bill_Of_Materials__c,  HS_Sales__Child_BOM__c, Name from HS_Sales__Bill_Of_Materials_Item__c WHERE HS_Sales__Bill_Of_Materials__c =: boms[0].Id];
       // Map<Id , Contact> opps =new Map<Id , Contact>( [Select Id , Name,(Select Id ,Name From Opportunities) from Contact]);

        List<BomWrapper> aooo = new List<BomWrapper>();
        for(HS_Sales__Bill_Of_Materials__c a : boms){
            BomWrapper aWraper = new BomWrapper() ; 
            aWraper.name =a.Name ;
            aWraper.label =a.Name ;
            aWraper.expanded =false ;
            List<Items> co = new List<Items>();
            for(HS_Sales__Bill_Of_Materials_Item__c c : bomItems){
                Items conWrapp = new Items();
                conWrapp.name =c.Name ;
                conWrapp.label =c.Name ;
                conWrapp.expanded =false ;

                List<Items> wrapperOooo = new List<Items>();
                if(c.HS_Sales__Child_BOM__c != null && c.HS_Sales__Child_BOM__c != acId)
                {
                    //Items item = new Items();
                   // Items ooo = new Items(); 
                    System.debug('child accounts are'+getTreeData(c.HS_Sales__Child_BOM__c));
                    wrapperOooo = (List<Items>)System.JSON.deserialize(getTreeData(c.HS_Sales__Child_BOM__c), List<Items>.class);
                   //wrapperOooo.add(ooo);

                }

                conWrapp.items =wrapperOooo ;
                co.add(conWrapp);
            }
            aWraper.items = co;
            aooo.add(aWraper);

        }
        return JSON.serialize(aooo) ;
    } 
    public Class BomWrapper{
        @AuraEnabled
        public String name {get;set;}
        @AuraEnabled
        public String label {get;set;}
        @AuraEnabled
        public Boolean expanded {get;set;}
        @AuraEnabled
        public List<Items> items {get;set;}

    }
    public Class Items{
        @AuraEnabled
        public String name {get;set;}
        @AuraEnabled
        public String label {get;set;}
        @AuraEnabled
        public Boolean expanded {get;set;}
        @AuraEnabled
        public List<Items> items {get;set;}
    }

}
2
  • use SOQL query in FOR loop to get higher limit which will process batch wise Dec 27 '18 at 13:36
  • I used bom and bomItems query in FOR loop but it doesn't work @SantanuBoral Dec 27 '18 at 13:45
1

From Salesforce docs:

The Lightning framework queues up actions before sending them to the server. This mechanism is largely transparent to you when you’re writing code but it enables the framework to minimize network traffic by batching multiple actions into one request (XHR). The batching of actions is also known as boxcar’ing, similar to a train that couples boxcars together.

The framework uses a stack to keep track of the actions to send to the server. When the browser finishes processing events and JavaScript on the client, the enqueued actions on the stack are sent to the server in a batch.

That means if 200 times getTreeData() method is called, only one Apex transaction will be queued and that will have the limit of 100 SOQL.

Had it been Visualforce we could set buffer as false, and that would stop batching of Remoting request.

Unfortunately, as its Aura Lightning, you have to create a bulk method, that will do the whole parsing and return BIG fat JSON that you will parse and display in Javascript.

You can bulkify as,

@AuraEnabled
    public static String getTreeData(List<Id> acIdList){
        List<HS_Sales__Bill_Of_Materials__c> boms = [Select Id , Name from HS_Sales__Bill_Of_Materials__c 
       WHERE Id IN: acIdList];
        //Bulk code


    }

Read more about bulkification here.

3
  • that is i am asking how to bulkify it. Dec 27 '18 at 13:50
  • Added Pseudo code on how to bulkify your code, Use a List<Id> instead of individual id and use SOQL IN Clause Dec 27 '18 at 13:53
  • It does not work because i am using component on a bom record page and passing only the current page record id, and i am using lightning tree to show parent->child->parent lookup->child and so on branches Dec 27 '18 at 14:19
0

You're going to need to change the logical structure of your code a bit to do a breadth-first sweep of this hierarchy before you descend another level. That will keep your SOQL use in check. At the same time, you'll need to make small tweaks along the way to prevent overusing SOQL you don't need.

But let's look at the overarching change first.

General Principle

Right now, you're pulling a BOM and its Items

    List<HS_Sales__Bill_Of_Materials__c> boms = [Select Id , Name from HS_Sales__Bill_Of_Materials__c WHERE Id =: acId];
    List<HS_Sales__Bill_Of_Materials_Item__c>bomItems = [Select Id, HS_Sales__Bill_Of_Materials__c,  HS_Sales__Child_BOM__c, Name from HS_Sales__Bill_Of_Materials_Item__c WHERE HS_Sales__Bill_Of_Materials__c =: boms[0].Id];

Note: this should be rewritten as a single parent-child SOQL query.

and iterating over the Items, where for each one you recurse

wrapperOooo = (List<Items>)System.JSON.deserialize(getTreeData(c.HS_Sales__Child_BOM__c), List<Items>.class);

That recursion ends up dramatically increasing the cost of your operation, because you run

(2 + (2 * number of child BOMs) + (2 * number of grandchild BOMs) + ...)

total queries. Because you also have a recursive System.debug() call, there's even an extra multiplier in there that makes it massively worse (doubling the total cost at each level).

Note: removing your System.debug() statement and writing a parent-child query at the top of your method might be pragmatically enough to move forward, although it won't be limits-proof.

Instead of doing that, what I suggest is that you accumulate all of the child BOM Ids in a Set<Id>:

for (HS_Sales__Bill_Of_Materials_Item__c c : bomItems) {
    if (c.HS_Sales__Child_BOM__c != null) {
        childBomIds.add(c.HS_Sales__Child_BOM__c);
    }
}

Then, you write a single query against the child BOMs:

    List<HS_Sales__Bill_Of_Materials__c> childBoms = [Select Id , Name from HS_Sales__Bill_Of_Materials__c WHERE Id =: acId];

Then, finally, you iterate again over the child BOMs to create the appropriate wrapper objects.

Specific Application

If you're descending multiple plies (not just parent to child, but parent to child to grandchild to great-grandchild), you'll still need an outer loop around this logic, but it'll be a little more complex.

Here's an example in pseudocode of what that might look like. Note that this is a rough sketch and doesn't include your wrapper creation steps. ALL CAPS means I'm glossing over details to show structure.

Map<Id, HS_Sales__Bill_Of_Materials__c> completeMap = new Map<Id, HS_Sales__Bill_Of_Materials__c>();
Set<Id> bomIds = new Set<Id>([LIST OF STARTING BOM IDS]);

while (bomIds.size() > 0) {
    List<HS_Sales__Bill_Of_Materials__c> boms = [QUERY BOM AND CHILDREN];
    bomIds = new Set<Id>(); // clear the set to walk a new level.
    // Pull all the child BOMs for this level.
    // Add the current-level BOMs to the global map.
    for (HS_Sales__Bill_Of_Materials__c b : boms) {
        completeMap.put(b.Id, b);
        for (HS_Sales__Bill_Of_Materials_Item__c c : BOM_ITEMS) {
            if (c.HS_Sales__Child_BOM__c != null) {
                bomIds.add(c.HS_Sales__Child_BOM__c);
            }
        }
     }
 } 

Thus, you iterate down the hierarchy (rather than recursing) and only run a single SOQL query per level of descent, rather than one per child record.

When you're done, you have a Map (completeMap) that contains all the BOM records you need, keyed by record Id.

You can then write your logic that generates JSON, but instead of making a query when you need to pull the child BOM for an Item, you can just source it from your Map. If necessary, your JSON-generating method can be recursive (it could be written either recursively or iteratively); but the key is that your SOQL is not run in a recursive fashion that exhausts your governor limits.

12
  • but the map will only contain BOM record but i also need to show BOM item in tree, ok its like this first there is one BOM and this BOM will be having muliple BOM items , then i have to check if any of BOM item is having a child BOM(Which is lookup to BOM) , then that child BOM(kind of BOM) will be having another BOM items and so on, so basically i have to check recursively and yes one more thing BOM is not having any lookup or master detail field of BOM item, only BOM item has BOM lookup field Dec 27 '18 at 14:47
  • @AjayChoudhary Yes, I understand the problem - I'm trying to show you how to break out your data acquisition from your JSON build, and how to do the data acquisition in an iterative, limits-efficient way. You'd need to handle the tree-building after this step.
    – David Reed
    Dec 27 '18 at 14:53
  • @sfdcfox i have already used that approach, not working because i am having structure like this parent->child->parent lookup(can be any record not the same parent record) ->child and so on, your answer is adequate only for parent->child->grandchild not for recursive relation Dec 27 '18 at 14:54
  • @AjayChoudhary once you have the data the way you want, the solution is practically the same. Query the data on the server side, as a map, as David said, then build the data on the client. Note that there's no such thing as "showing a parent record" in a tree like you propose; you will have to transform the parent record as a child record from the tree perspective.
    – sfdcfox
    Dec 27 '18 at 14:58
  • @AjayChoudhary Actually, look at this question, it shows a recursive tree building technique.
    – sfdcfox
    Dec 27 '18 at 15:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.