Showing posts with label SOQL. Show all posts
Showing posts with label SOQL. Show all posts

Tuesday, March 26, 2013

Benchmark Testing: maps vs for-loop

Speed and efficiency should always be in the forefront of a developers mind when writing code. This, most agree on. What is not agreed upon is how that should be accomplished. Each developer has their our own preferences on how it should be written for various reasons. Usually it's due to how they were taught or trained, maybe its because it is more aesthetically pleasing or reads cleaner. Regardless of the cause, the effect is that the code could have performed more efficiently or at a faster rate. There is always more than one way to accomplish a task, benchmark testing is used to prove what methodology is the faster one, although might not be the prettiest. 

The Argument:

Jim thinks that it is faster to collect data via a for-loop:

Set<Id> accountIds = new Set<Id>();

for (Account a : [select Id from Account]) {
  accountIds.add(a.Id);
}
someMethod(accountIds);

Jane however insists that it is faster to do it via SOQL to Map:

Map<Id, Account> accounts = new Map<Id, Account>(
  [select Id from Account]
);
someMethod(accounts.keySet());

After much debate, a few harsh words and a broken pen, a bet was struck.

The Test:

A test class can be written, alternatively this could be done in 'execute anonymous' but with a test class the real data wont be effected as it would the other way. The tests will run different amounts of data through each pattern and record the time it took for the comparison. First the data needs to be created that they will iterate over. 


private static void createAccounts(String pName) {
  Account a;
  List<Account> accounts = new List<Account>();
  for (Integer i = 0; i < 200; i++) {
    accounts.add(a = new Account(Name = pName + i));
  }
  insert accounts;
}

The name needs to be unique for each Account created, to help accomplish this a string will be passed in to add to it when the account is being created. Since Salesforce has govonerner limits in place, only 200 records can be created at a time and so requiring a second method to call createAccounts() and allow more than 200 to be made at a time. 

private static void setupTests(Integer pLimit) {
  for (Integer i = 0; i < pLimit; i++) {
    createAccounts(String.valueOf(i));
  }
}

To test the for-loop and map, a timestamp is logged at the start and end of the method in milliseconds (this makes the comparison easier). Since each is using the data created in the test, the amount being iterated over is known and no limit is needed in the SOQL statement.


private static Long runViaForLoop() {
  Long lStart = DateTime.now().getTime();
  Set<Id> accountIds = new Set<Id>();
  for (Account a : [select Id from Account]) {
    accountIds.add(a.Id);
  }
  return DateTime.now().getTime() - lStart;
}

private static Long runViaMap() {
  Long lStart = DateTime.now().getTime();
  Map<Id, Account> accounts = new Map<Id, Account>(
    [select Id from Account]
  );
  return DateTime.now().getTime() - lStart;
}

Its time to actually do some testing. The first test should be over a low number of records like 1 or 5 and slowly working up to larger numbers like 10k. Debug statements are necessary to see the output of the numbers in the logs after the tests complete. This will show which pattern took less time to iterate over the various record sizes, and thus show which one is better to use, and who wins the bet!

private static testmethod void compaireTime() {
  setupTests(1);
  system.debug('for-loop: ' + runViaForLoop()  + 'ms');
  system.debug('map: ' + runViaMap() + 'ms');
}

After the tests run, the output will show how long each took to complete the same task. 

DEBUG| for-loop: 36ms
DEBUG| map: 10ms

Looks like the Map is the winner. Unfortunately though, Jim is a sore loser and since there are other factors that can effect the output, its best to run the tests a few times along with expanding it to larger sets of records such as 4k and 10k:

private static testmethod void compaireTime() {
  setupTests(20);
  system.debug('4k for-loop: ' + runViaForLoop() + 'ms');
  system.debug('4k map: ' + runViaMap() + 'ms');
  setupTests(30);
  system.debug('10k for-loop: ' + runViaForLoop() + 'ms');
  system.debug('10k map: ' + runViaMap() + 'ms');
}


DEBUG| 4k for-loop: 876ms
DEBUG| 4k map: 138ms

DEBUG| 10k for-loop: 1777ms
DEBUG| 10k map: 246ms

DEBUG| 4k for-loop: 787ms
DEBUG| 4k map: 107ms

DEBUG| 10k for-loop: 2234ms
DEBUG| 10k map: 340ms

DEBUG| 4k for-loop: 1580ms
DEBUG| 4k map: 113ms

DEBUG| 10k for-loop: 1740
DEBUG| 10k map: 220


DEBUG| 4k for-loop: 796
DEBUG| 4k map: 115

DEBUG| 10k for-loop: 1898
DEBUG| 10k map: 279



The graph above shows the average of 20 tests, and it shows that as the record size increased, the time saved using maps speaks for itself. Jane is clearly the victor here, and Jim has to pay up. Numbers always win arguments.


Wednesday, February 1, 2012

APEX Trigger to Prevent Duplicate Object Records

One of the biggest issues in Org's that I have seen is with duplicate records. But what is the best way to prevent this? With an easy trigger of course! 


Let take the Contact object, in my Org, everything is keyed off email, so we shouldn't have more than one contact with the same email address. And we need to not only protect against new Contacts, but we also need to ensure that if a User updates the email on the Contact that it too is not already in use before the update occurs. In order for the User to know that they are trying to Insert or Update incorrectly we will have a field error show next to the email of the contact stating that one already exists.


Lets make(or update) a Contact trigger for before insert and before update. 






We are going to use a Map to store the id and email of each contact that comes into the trigger so we can use that to do a SOQL query later when its time to see if it already exists.  add the 'trigger.isBefore' even though we know this will only occur before, so that this can be easily extended in the future and we wont have to go back and change code.




Now we need to populate the map with the Contact info so we can use it in the SOQL query next. We should also send up an error if the batch of Contacts that is being iterated over in the trigger contains more than one of the same email. 




Now that we have the Map populated, or at least in theory it should be, we will query the DB to see if a Contact already exists with that email, and if so, show an error to the User, other wise do nothing and allow it to be inserted/updated. 






And thats it. You can of course make this much more robust, so it not only compares email but also name and phone number too. 


Questions?  
Twitter: @SalesForceGirl  or Facebook

Wednesday, December 28, 2011

easy double-click to edit related-list VF page

Often enough I am asked to make a custom VisualForce of a related list that is editable in a similar way to salesforce's native double click to edit. I have made them in a variety of ways, including adding images like salesforce's to indicate if a field is editable or locked etc. and some simpler ways like the one I am going to do now.

The Problem:
a sObject under Account(or any other obj) needs show on the Account page layout, but it also needs to be double-click to edit to make it easy for the reps to update key information on the fly without having to click into each sObject. (for this example we will use the Contact obj)


Setup:
First we need to create a controller extension so that it can sit in the page layout of the Account obj, and even though we are showing the contact obj, the extension needs to point to account otherwise it wont show up as an available VisualForce page to add on the layout.
ContactInLineEdit.cls
ContactInLineEdit.page

This is a standard controller extension setup, this allows the class to use the account id referenced in the URL when the page is accessed. Example page URL will be 'apex/ContactInLineEdit?id={!account.id}'

Now we should add the Contact list to the class so we can access it on the page in a apex:repeat, when we add it to the class, we only need to get the list on load of the page, and then again when we update the fields.
ContactInLineEdit.cls

On the page we will need to add the apex:form tag, along with an apex:outputpanel with an id so we can call it later in a rerender attribute on the update call. We will also add the CSS and jQuery files that will help us control the double-click to edit functionality. We then place the jQuery inside the outputpanel so when it rerenders it can reapply the jQuery to the page. I also prefer to use the jQuery.noConflict(); since I often run into issues with salesforce's JS conflicting with what I am trying to implement.
ContactInLineEdit.page

Now we need to add the apex:pagemessages so we can do error handling and we need to add the table for the apex:repeat to sit in. We will also add the apex:outputfiled's that will display in the related list on the page, we need to make sure that any field we reference here is also in the select query on the class.
ContactInLineEdit.page

Lets add in the loading icon for the update and a JS function to control it, and we should also add the edit fields to the table. To help control the conditional hiding and showing of the fields for the edit, we should add some div's with classes to reference in jQuery.
ContactInLineEdit.page
ContactInLineEdit.page

Lets add the double click functionality, first we need to add a class to the edit text in the action column  <span class="editLink">Edit</span> then we need to add a function so that everytime the nonEdit field (outputField) is double-clicked it will hide the outputField and show the inputField. We will also change the Edit text to Cancel and add a class to it so we know that row is in edit mode.

Now lets add the edit row/cancel row edits functionality, this will allow all editable fields in the row to be edited and if cancel is hit, it will undo all edits by retrieving the text from the hidden outputfield otherwise it will continue to show the updated text since we are just hiding and showing. We don't want to clear the fields cause that will potentially clear out the saved info if it gets saved by mistake. So by retrieving the info from the outputfield, we are able to set it back to what it was originally.
ContactInLineEdit.page

We will also need to update our edit action to use the new function, it will now look like this: <span class="editLink" onclick="EditMe(jQuery(this));">Edit</span>
At this point we need to make the update method and add it to the page, this will allow any edits made by the user to be saved, and then the list will rerender with the new information.
ContactInLineEdit.cls

ContactInLineEdit.page

And that it, simple. I didn't add it here, but field validation could be added to ensure its a proper email or phone number etc. In the style i made so that fields that were editable when the mouse hovered over it, the cursor would change to a pointer like it would when the mouse hovers over a link. This way the user knows the field is editable instead of having to add an image that could bog down the page load.

Questions?  
Twitter: @SalesForceGirl  or Facebook

Tuesday, May 31, 2011

What makes Elegant Code?

One of the things i love about programming is that there is always several different ways to accomplish a task. If you gave 10 programmers the same task, each would go about it in there own way, sure the result would be the same but the code will be completely unique.

I find that when i look at other programmers code, its almost like looking at an artist work, not to say that the code is a work of art, just unique and sometimes beautiful. Every programmer has there own way of doing things, tendencies that all make up the big picture of the code. The naming convention used, structure of the methods and even the way they went about accomplishing the task. It all contributes to the look and feel of the code. It also can show the skill of the programmer or artist, or lack there of. 

Code should be simplistic, elegant, beautiful. It should also be structured, and still 'flow' like a picture. So many times i look at code and often think of spaghetti, and cringe at the over complication of something that should only be one or two lines and not ten. And please don't get me wrong, i am always trying to learn new ways to improve my code, trying to find smarter ways to go about a problem, keeping an open mind to new ideas and concepts.

An example of what i mean, below is two lines, both of the same SOQL statement, both get the job done. But one, while not wrong, also isn't quite right.

Contact[] objContact = [select id, name from Contact where email ='someemail@email.com' limit 1];

Contact objContact = [select id, name from Contact where email = :sInEmail limit 1];

In the first SOQL statement, it is creating a list of Contacts even though the query is only returning one. It also has a 'hard-coded' variable which is never a good practice.  In the second, since we are only querying for one obj, it only creates a single object, also it uses a variable instead of string text. 

Now i know what you may be thinking, does that really make a difference in the elegance of the code? Yes. If we left it as the first statement, where it is returning a List of objs instead of a single obj, and all the proceeding methods would then have to accommodate for it, even thought it isn't necessary. Its like adding lines and logic that only complicate and so the code tends to look messy or clustered when it doesn't need to be. Keeping code simplistic is key. As a rule of thumb, I try to keep my methods under 10 lines total, and that includes all the curly brackets.

Here is another example of doing things different ways while still accomplishing the same task:

      public string mystring;

      public string getMystring()
      {
            return mystring;
      }

      public void setMyString(string s)
      {
            this.s = mystring;
      }

vs

          public string sInStringName{get;set;}

I hope I don't need to explain why the second is cleaner, but if you notice i use the variable name of 'sInStringName' instead of 'mystring', i do this for most variable's: 'lst' for list, 'obj' for sObjects, etc. this helps me identify what it is, along with what type. So if i am looking for a string, i know it will start with 's'. I also like to follow the camel hump approach where the first word in the name is lowercase and all words after have the first letter capitalized. When it comes to booleans i am still trying to decide if i like 'b' as in 'bInBoolean' or 'is' since it is a true false statement, which would look like 'isBoolean'. Its important that no matter what naming convention you choose, that you stick with it, for the whole project, if you don't like it, try tweaking it in the next project.

Questions? 

Twitter: @SalesForceGirl  or Facebook

Thursday, March 31, 2011

SalesForce Knowledge, a look Underneath

A little while ago I finished work on a support website that was housed on force.com sites and powered by knowledge, Salesforce's new KB solution. While it was fun to figure everything out, I thought I would share to help other avoid that same headache. 

Like all newly released software, it has some bugs and some issues that you will want to look out for, but overall, Salesforce did a great job on this one. Hopefully with some more updates it will reach its full potential and there will be less need for customization.

Creating articles in knowledge can be very troublesome, and the trouble can start sooner rather than later, so its important to carefully think out the best way to implement it based on the company's needs. Knowledge allows multiple 'DataCategories', which is what is used to help filter the search and organize the content. In the old Knowledge version(before 21.0) they didn't give much consideration to Language, so if multilingual support was needed, you would need to make sure you set theDataCategories appropriately before starting. Also if you wanted multilingual, you needed to figure out how to do the urls -> each doc requires a unique URL field(we ended up appending the lang code to the end). Otherwise you would have to go back thought and edit each doc you made to accommodate for that. With the newest release of knowledge (v21.0), it will have a required Language field that 'fixes' the need for it for so much for thought for such an otherwise simple option.


 The DataCategories themselves have their own issues, for one you can only edit / access them though the website, it is not available in the schema browser except though the article types, but only access based on that Artical type for filtering the query / search. On top of that, the interface to edit DataCategories in is, as far as i can tell, an attempt at improving the old edit pages with an ajax-ified 'high-tech' looking one. The page seems to be overloaded with onHovers, double-click to edit, ajax-rerenders and modals, to a point that loses usability, making it had to get at the data you need, thus rendering the page unusable. It does look nice though.

Creating different Articles Types for your knowledge base raises some more considerations that must be thought about before getting started. One nice thing is that you can create any number of Article Types, for example, my implementation has 9 including:
  • Alerts
  • Internal
  • Videos
  • Documents
  • FAQs
  • Release Notes
etc. On the surface, in the 'Article Management' tab, it gives you another 'ajax-ified' looking UI, that doesn't allow for easy sorting or viewing of the Articles. The table's columns are mostly sortable except by Type, they also allow you to filter by Data Category OR allow Queuing , making it impossible to manage anything more than a hundred articles. My Org has somewhere around  5000 articles across 3 languages and growing I might add. 


Unlike other sObject records in Salesforce, the Article Type obj has a required 'URL name' field that is also required to be unique. They also added attachments as a field type to the Article sObjects, but it can cause some unexpected errors saying that the DML limit has exceeded in certain use cases, and it had a few other quirks that just made working with them a headache. Since the out-of-the box fields on the sObject is minimal, its key to plan out each ArticalTypes fields carefully, and thoroughly to avoid having to back track later.
In making  an Article, the first thing to note is that Salesforce enforces the save draft, then publish principal, which would be good but without queues to sort the articles, it does little more than become a performance issue. Its also not possible to create and then publish Articles via apex, you can create a draft article, but not to publish or make it 'public'. 

The rich text editor that Salesforce uses is limiting since they are using a 'lite' version of another rich text editor called FCKeditor, making it necessary to compose the Article in HTML else where and copy / paste it into the editor. But as you would expect there is issue with this as well, since it was not designed for such use.

Looking at the Article in the schema browser you will notice that there are 5 sObject for every Artical Type, and each has a unique extension:

  • ArticalType__ka
  • ArticalType__kav
  • ArticalType__ViewStat
  • ArticalType__VoteStat
  • ArticalType__DataCategorySelection

The __ka sObject is the top level article, but it doesn't hold any of the actually article info like Body, Title, etc, that is housed in the __kav obj, which has access to the  __DataCategorySelection obj for searching. But if you want to see __ViewStat or __VoteStat, you need to go through the __ka object. This may seem like a pratical set up but the __ka object almost holds no value except to make it harder to transverse the layers, ideally it should be the __kav with everything underneath it. The __ViewStat and __VoteStat sObjects have a few drawbacks as well, if you were to have it on a public site, only users that login could rate Articles (__VoteStat), which means a Salesforce license for every potential user on the site. The __ViewStat however doesn't require log in, but with testing over time, we found that it would stop working periodically, and so wasn't reliable.

Example SOQL code with Data Categories:

List<FAQ__kav> f = Database.query('Select f.ranking__c,f.VideoUrl__c,MacVideoUrl__c,f.Localized_Title__c,f.KnowledgeArticleId From FAQ__kav f WHERE f.ranking__c != null and PublishStatus = \'online\' and IsVisibleInPkb = true WITH DATA CATEGORY Languages__c BELOW '+lang+'__c AND Products__c BELOW '+prod+'__c order by f.ranking__c DESC limit 10');

Example SOSL:

List<List<SObject>> qry = search.query('FIND \'access code\' IN ALL FIELDS Returning Documentation__kav(ID, knowledgearticleid, Title, Localized_Title__c, UrlName, LastPublishedDate WHERE PublishStatus = \'Online\' LIMIT 5),FAQ__kav(ID, knowledgearticleid, Title, Localized_Title__c, UrlName, LastPublishedDate WHERE PublishStatus = \'Online\' LIMIT 5) WITH DATA CATEGORY Products__c BELOW (lmipro2__c,lmifree__c) AND Languages__c BELOW (en__c,es__c)');

Example in accessing Data Categories:

               for (FAQ__kav f : faqList)
               {
                        FAQ__DataCategorySelection[] dc =  f.DataCategorySelections;
                        for(FAQ__DataCategorySelection d : dc)
                         {
                              if(d.DataCategoryGroupName == 'products')
                              {
                                    if(GlobalHelper.CheckForNotNull(dataCProd))
                                    {
                                          dataCProd = d.DataCategoryName;
                                    }else{
                                          dataCProd += ', '+ d.DataCategoryName;
                                    }
                              }
                              if(d.DataCategoryGroupName == 'languages')
                              {
                                    if(GlobalHelper.CheckForNotNull(dataCLang))
                                    {
                                          dataCLang = d.DataCategoryName;
                                    }else{
                                          dataCLang += ', '+ d.DataCategoryName;
                                    }
                              }
                        }
                  }

Again this was not meant to 'Bash' salesforce, rather it is meant to be a guide around the mishaps that I encountered when doing my implementation. But on a side note, because some of the issues are too big to ignore, I am now tasked with re-creating the whole site in .Net :-P 

Questions? 

Twitter: @SalesForceGirl  or Facebook