• Adam Franklin
  • NEWBIE
  • 30 Points
  • Member since 2014

  • Chatter
    Feed
  • 0
    Best Answers
  • 0
    Likes Received
  • 0
    Likes Given
  • 4
    Questions
  • 7
    Replies
Hi,

I'm working to identify some options around how to improve our knowledge search performance.

At the moment, our knowledge search is coming from a custom controller where the query seems to be something like this:
 
public PageReference searchArticle(){
    
        //Set<Id> articleIds = new Set<Id>();
            
        SearchArticlesByCategory sabc = new SearchArticlesByCategory();
    
        allArticlesBySearch = sabc.searchArticlesByCategory(artTypes, visibleCats, searchString, language, prefix, null);
        
        Integer listSize = allArticlesBySearch.size();
        
        Integer i;
        
        for (i = 0; i < listSize; i++){
        
            articleIds.add(allArticlesBySearch[i].Id);
        }
        
        articleListBySearch = [SELECT Title,UrlName,Summary FROM KnowledgeArticleVersion WHERE Id IN :articleIds AND Title LIKE :'%' + String.escapeSingleQuotes(searchString) + '%'];
        
        if(articleListBySearch.size() == 0){
        
            articleListBySearch = allArticlesBySearch;
        }
                
        CheckAttInArticle caia = new CheckAttInArticle();
        
        attInArticle = caia.checkAttInArticle(articleListBySearch, mapContainsAtt);
        
        setPageState(method_search, articleListBySearch.size());
        
        return null;
    }

I didn't write this code, but in researching the knowledge search functionality, I've learned that the native Salesforce Knowledge search offers a lot of value in terms of things like tf/idf relevancy, lemmatization, and synonym expansion.   I also know that the newest edition includes default and/or and search term highlighting.

What I'm working to figure out is :
  1. What could be done to improve our existing custom search controller?
  2. Does the search controller as outlined in the knowledge developers guide provide the functionality mentioned above?
  3. Are there any ways to embed the salesforce knowledge search in a custom page without using the knowledge tab or completely recoding?
  4. Would using the connect api and the getSuggestions class be a way to provide more 'instant' results and utilize SFDC's native search power?
Thanks!
I'm trying to determine what path I should be pursuing for this one:

I have a custom object, Service_Utilization__c which has a lookup relationship to both the Contact and Account objects.  

Each record in the object represents a Contact who is currently using a service (such as a project).   A single contact may be utilizing more than one service, and a service may be utilized by more than one contact (a project might have more than contact associated to it).

What I'd like to do is place this information on a related list or other table inside the case page layout.   I'm thinking it will be some kind of embedded visualforce page, but I'm open to suggestions.

What I seem to be running into is the issue of trying to traverse UP to the contact, and then down to the Contact's child object (Service Utilization) to get the records.    It seems relatively simple, but I've been trying a variety of solutions and none seem to do the job.
   
What I'd like is some direction on where I should be looking to figure out the solution.

Thanks!
 
Hi,

I was working on bulkifying a trigger that I'm writing and found another trigger (not mine!) that is breaking when I run my bulk tests.

I want to re-write it, but I'm finding that I cannot get the SOQL query in the trigger to return any results.

Here is the original trigger:
 
trigger UpdateAccountContactL1 on Case (before insert) {

    for(Case c : Trigger.new){    
        // Set profile IDs to exclude
        Set<Id> proIds = new Set<Id>{'00ea0000001p149', '00ea0000001p15L', '00ea0000001p14A'};       
        //Only process changes for Support record type with Account Name and Contact Name fields are blank        
           if(c.RecordTypeId == '012a00000018Ens' &&  (c.AccountId == null || c.ContactId == null)) {
                  // Get profile ID of Case Owner if Owner is not a Queue
                    User u = [SELECT ProfileId FROM User WHERE Id = :c.OwnerId]; 
                    // Process Account and Contact update if the User is one of the L1 profiles
  
            if(proIds.contains(u.ProfileId)){
                 List<Account> accList = [SELECT Id FROM Account WHERE Name = 'Generic Supplier'];         
                List<Contact> conList = [SELECT Id FROM Contact WHERE Name = 'Supplier Issue'];
                      c.AccountId = accList.isEmpty() ? null : accList[0].Id;
                     c.ContactId = conList.isEmpty() ? null : conList[0].Id;
            }
        }
    }
}

And here is what I've tried to rewrite it to:
 
trigger UpdateAccountContactL1 on Case (before insert) {     
	//Get record type ID
    String rt;
    List<RecordType> rts = [Select Id, Name FROM RecordType Where Name = 'Support' limit 1];
    if(rts.isEmpty()==false){
        rt = rts[0].id;
    }
  
    //List of users where this could apply.. goes against 
    List<User> t1Agents = [SELECT id, ProfileId from User where User.Profile.Name like '%Support Tier 1%'];
    Set<Id> agents = new Set<Id>(); 
    if(t1Agents.isEmpty()==false){
        for(User agent: t1Agents){
           agents.add(agent.id); 
        }
    }
   
    // Set ids to assign
        String accId;
       	String conId;
    	 List<Contact> cons = [Select Id,  AccountId From Contact WHERE Contact.Name = 'Supplier Issue'  AND Account.Name = 'Generic Supplier'];
    	if(cons.isEmpty()==false){
        System.debug(cons[0]);
        accId = cons[0].AccountId;
        conId = cons[0].id
         }
  
    for(Case c : Trigger.new){
        
       if(c.RecordTypeId == rt &&  (c.AccountId == null || c.ContactId == null)) {
            // Get profile ID of Case Owner if Owner is not a Queue
              
            // Process Account and Contact update if the User is one of the Tier 1 profiles
           if(agents.contains(c.OwnerId)){
                c.AccountId = accId;
                c.ContactId = conId;
              }
        }
    }
}

But what I'm seeing is that the List<Contact> cons =[].....   doesn't populate.   Why would this happen?   What can I do about it?

If i run in Execute anonymous - it works.  Same with Just entering the query in the query editor.   
List<Contact> cons = [Select Id,  AccountId From Contact WHERE Contact.Name = 'Supplier Issue'  AND Account.Name = 'Generic Supplier'];
System.debug(cons);

 
Hi,

I'm working on a calculator to dynamically determine milestone times based on various attributes of cases.  I have it working in the sandbox, and 95% code coverage, but I am having trouble generating a test to hit that last line of code.  Instead, my testmethod fails and I get the error message referenced above.

Right now, I have three milestones: First Response, Update Customer, and Solution delivered.  Each calculates dynamically based on the milestone and severity level.   

Here is my calculator:
global class myMilestoneTimeCalculator implements Support.MilestoneTriggerTimeCalculator {   
     global Integer calculateMilestoneTriggerTime(String caseId, String milestoneTypeId){
        Case c = [SELECT RecordType.Name, Severity_level__c, Priority FROM Case WHERE Id=:caseId];
        String rt = c.RecordType.Name;
        String sev = c.Severity_Level__c;
        String pLev = c.Priority;
        
         //this is only necessary for now, to prevent any stepping on toes, also we don't want to calc w/o a sev level
         If(rt.equals('Product Support') && c.Severity_Level__c != null){
             //start by checking milestone
           MilestoneType mt = [SELECT Name FROM MilestoneType WHERE Id=:milestoneTypeId];
             if (mt.Name != null && mt.Name.equals('First response from agent')) {
            //now sev levels 
                 if(sev != null && sev.equals('Severity 2')){return 240;}
                 else if(sev !=null && sev.equals('Severity 3')){return 480;}
                 else {return 960;}
                 
             }
             else if(mt.Name !=null && mt.Name.equals('Update Customer')){ 
                 //we're in the holding phase, providing customer updates
                 if(sev != null && sev.equals('Severity 2')){return 180;}
                 else if(sev !=null && sev.equals('Severity 3')){return 480;}
                 else {return 960;}
             }
             //our SLA requires a solution or action plan within certain timeframes.  we can do that here.
             else if(mt.Name != null && mt.Name.equals('Solution or Action Plan Delivered')){
                 if(sev != null && sev.equals('Severity 2')){return 960;}
                 else if(sev !=null && sev.equals('Severity 3')){return 1440;}
                 else {return 2400;}
             }
             else {return 960;}         
         }
         else {return 960;}
     }
}

I wrote a few helper methods, which I've tested and can confirm work.  Here is the one for getting the milestone :
public static MilestoneType msType(String MilestoneName){
        List<MilestoneType> mtLst = [SELECT Id, Name FROM MilestoneType Where Name =:MilestoneName LIMIT 1];
        if(mtLst.isEmpty() || mtLst.size() > 1) {return null;}
        else{
        MilestoneType mt = mtLst[0];
        return mt;  
        }
     }

And here is a testmethod that fails :
@isTest static void nullMilestone(){
        Case c = msTestDataFactory.cGen('Product Support','Severity 4','Low');
        myMilestoneTimeCalculator calculator = new myMilestoneTimeCalculator();
        MilestoneType mt = msTestDataFactory.msType('');
        Integer actualTriggerTime = calculator.calculateMilestoneTriggerTime(c.Id, mt.Id);
        System.debug('no milestone : '+actualTriggerTime);
        System.assertEquals(960, actualTriggerTime);
        }

So basically, I'm trying to test for an unlikely case (probably an impossible case) of a Case with no milestones invoking the milestone calculator.   I'm not sure how to do this without getting the error above.  

I'd appreciate any suggestions or general feedback on my actual code - I started with a snippet from the Salesforce resources, but it has obviously grown into it's own thing.
Hi,

I'm working to identify some options around how to improve our knowledge search performance.

At the moment, our knowledge search is coming from a custom controller where the query seems to be something like this:
 
public PageReference searchArticle(){
    
        //Set<Id> articleIds = new Set<Id>();
            
        SearchArticlesByCategory sabc = new SearchArticlesByCategory();
    
        allArticlesBySearch = sabc.searchArticlesByCategory(artTypes, visibleCats, searchString, language, prefix, null);
        
        Integer listSize = allArticlesBySearch.size();
        
        Integer i;
        
        for (i = 0; i < listSize; i++){
        
            articleIds.add(allArticlesBySearch[i].Id);
        }
        
        articleListBySearch = [SELECT Title,UrlName,Summary FROM KnowledgeArticleVersion WHERE Id IN :articleIds AND Title LIKE :'%' + String.escapeSingleQuotes(searchString) + '%'];
        
        if(articleListBySearch.size() == 0){
        
            articleListBySearch = allArticlesBySearch;
        }
                
        CheckAttInArticle caia = new CheckAttInArticle();
        
        attInArticle = caia.checkAttInArticle(articleListBySearch, mapContainsAtt);
        
        setPageState(method_search, articleListBySearch.size());
        
        return null;
    }

I didn't write this code, but in researching the knowledge search functionality, I've learned that the native Salesforce Knowledge search offers a lot of value in terms of things like tf/idf relevancy, lemmatization, and synonym expansion.   I also know that the newest edition includes default and/or and search term highlighting.

What I'm working to figure out is :
  1. What could be done to improve our existing custom search controller?
  2. Does the search controller as outlined in the knowledge developers guide provide the functionality mentioned above?
  3. Are there any ways to embed the salesforce knowledge search in a custom page without using the knowledge tab or completely recoding?
  4. Would using the connect api and the getSuggestions class be a way to provide more 'instant' results and utilize SFDC's native search power?
Thanks!
I'm trying to determine what path I should be pursuing for this one:

I have a custom object, Service_Utilization__c which has a lookup relationship to both the Contact and Account objects.  

Each record in the object represents a Contact who is currently using a service (such as a project).   A single contact may be utilizing more than one service, and a service may be utilized by more than one contact (a project might have more than contact associated to it).

What I'd like to do is place this information on a related list or other table inside the case page layout.   I'm thinking it will be some kind of embedded visualforce page, but I'm open to suggestions.

What I seem to be running into is the issue of trying to traverse UP to the contact, and then down to the Contact's child object (Service Utilization) to get the records.    It seems relatively simple, but I've been trying a variety of solutions and none seem to do the job.
   
What I'd like is some direction on where I should be looking to figure out the solution.

Thanks!
 
Hi,

I was working on bulkifying a trigger that I'm writing and found another trigger (not mine!) that is breaking when I run my bulk tests.

I want to re-write it, but I'm finding that I cannot get the SOQL query in the trigger to return any results.

Here is the original trigger:
 
trigger UpdateAccountContactL1 on Case (before insert) {

    for(Case c : Trigger.new){    
        // Set profile IDs to exclude
        Set<Id> proIds = new Set<Id>{'00ea0000001p149', '00ea0000001p15L', '00ea0000001p14A'};       
        //Only process changes for Support record type with Account Name and Contact Name fields are blank        
           if(c.RecordTypeId == '012a00000018Ens' &&  (c.AccountId == null || c.ContactId == null)) {
                  // Get profile ID of Case Owner if Owner is not a Queue
                    User u = [SELECT ProfileId FROM User WHERE Id = :c.OwnerId]; 
                    // Process Account and Contact update if the User is one of the L1 profiles
  
            if(proIds.contains(u.ProfileId)){
                 List<Account> accList = [SELECT Id FROM Account WHERE Name = 'Generic Supplier'];         
                List<Contact> conList = [SELECT Id FROM Contact WHERE Name = 'Supplier Issue'];
                      c.AccountId = accList.isEmpty() ? null : accList[0].Id;
                     c.ContactId = conList.isEmpty() ? null : conList[0].Id;
            }
        }
    }
}

And here is what I've tried to rewrite it to:
 
trigger UpdateAccountContactL1 on Case (before insert) {     
	//Get record type ID
    String rt;
    List<RecordType> rts = [Select Id, Name FROM RecordType Where Name = 'Support' limit 1];
    if(rts.isEmpty()==false){
        rt = rts[0].id;
    }
  
    //List of users where this could apply.. goes against 
    List<User> t1Agents = [SELECT id, ProfileId from User where User.Profile.Name like '%Support Tier 1%'];
    Set<Id> agents = new Set<Id>(); 
    if(t1Agents.isEmpty()==false){
        for(User agent: t1Agents){
           agents.add(agent.id); 
        }
    }
   
    // Set ids to assign
        String accId;
       	String conId;
    	 List<Contact> cons = [Select Id,  AccountId From Contact WHERE Contact.Name = 'Supplier Issue'  AND Account.Name = 'Generic Supplier'];
    	if(cons.isEmpty()==false){
        System.debug(cons[0]);
        accId = cons[0].AccountId;
        conId = cons[0].id
         }
  
    for(Case c : Trigger.new){
        
       if(c.RecordTypeId == rt &&  (c.AccountId == null || c.ContactId == null)) {
            // Get profile ID of Case Owner if Owner is not a Queue
              
            // Process Account and Contact update if the User is one of the Tier 1 profiles
           if(agents.contains(c.OwnerId)){
                c.AccountId = accId;
                c.ContactId = conId;
              }
        }
    }
}

But what I'm seeing is that the List<Contact> cons =[].....   doesn't populate.   Why would this happen?   What can I do about it?

If i run in Execute anonymous - it works.  Same with Just entering the query in the query editor.   
List<Contact> cons = [Select Id,  AccountId From Contact WHERE Contact.Name = 'Supplier Issue'  AND Account.Name = 'Generic Supplier'];
System.debug(cons);

 
Hi,

I'm working on a calculator to dynamically determine milestone times based on various attributes of cases.  I have it working in the sandbox, and 95% code coverage, but I am having trouble generating a test to hit that last line of code.  Instead, my testmethod fails and I get the error message referenced above.

Right now, I have three milestones: First Response, Update Customer, and Solution delivered.  Each calculates dynamically based on the milestone and severity level.   

Here is my calculator:
global class myMilestoneTimeCalculator implements Support.MilestoneTriggerTimeCalculator {   
     global Integer calculateMilestoneTriggerTime(String caseId, String milestoneTypeId){
        Case c = [SELECT RecordType.Name, Severity_level__c, Priority FROM Case WHERE Id=:caseId];
        String rt = c.RecordType.Name;
        String sev = c.Severity_Level__c;
        String pLev = c.Priority;
        
         //this is only necessary for now, to prevent any stepping on toes, also we don't want to calc w/o a sev level
         If(rt.equals('Product Support') && c.Severity_Level__c != null){
             //start by checking milestone
           MilestoneType mt = [SELECT Name FROM MilestoneType WHERE Id=:milestoneTypeId];
             if (mt.Name != null && mt.Name.equals('First response from agent')) {
            //now sev levels 
                 if(sev != null && sev.equals('Severity 2')){return 240;}
                 else if(sev !=null && sev.equals('Severity 3')){return 480;}
                 else {return 960;}
                 
             }
             else if(mt.Name !=null && mt.Name.equals('Update Customer')){ 
                 //we're in the holding phase, providing customer updates
                 if(sev != null && sev.equals('Severity 2')){return 180;}
                 else if(sev !=null && sev.equals('Severity 3')){return 480;}
                 else {return 960;}
             }
             //our SLA requires a solution or action plan within certain timeframes.  we can do that here.
             else if(mt.Name != null && mt.Name.equals('Solution or Action Plan Delivered')){
                 if(sev != null && sev.equals('Severity 2')){return 960;}
                 else if(sev !=null && sev.equals('Severity 3')){return 1440;}
                 else {return 2400;}
             }
             else {return 960;}         
         }
         else {return 960;}
     }
}

I wrote a few helper methods, which I've tested and can confirm work.  Here is the one for getting the milestone :
public static MilestoneType msType(String MilestoneName){
        List<MilestoneType> mtLst = [SELECT Id, Name FROM MilestoneType Where Name =:MilestoneName LIMIT 1];
        if(mtLst.isEmpty() || mtLst.size() > 1) {return null;}
        else{
        MilestoneType mt = mtLst[0];
        return mt;  
        }
     }

And here is a testmethod that fails :
@isTest static void nullMilestone(){
        Case c = msTestDataFactory.cGen('Product Support','Severity 4','Low');
        myMilestoneTimeCalculator calculator = new myMilestoneTimeCalculator();
        MilestoneType mt = msTestDataFactory.msType('');
        Integer actualTriggerTime = calculator.calculateMilestoneTriggerTime(c.Id, mt.Id);
        System.debug('no milestone : '+actualTriggerTime);
        System.assertEquals(960, actualTriggerTime);
        }

So basically, I'm trying to test for an unlikely case (probably an impossible case) of a Case with no milestones invoking the milestone calculator.   I'm not sure how to do this without getting the error above.  

I'd appreciate any suggestions or general feedback on my actual code - I started with a snippet from the Salesforce resources, but it has obviously grown into it's own thing.