5

I began my journey to speed up jQuery's autocomplete earlier this afternoon, and decided it was probably a good idea to begin memcaching everything. As suggested in this article: Speeding up autocomplete.

However, I am still dealing with slow response time even after installing and using Memcached.

The problem in my case is that I am dealing with extraordinarily long lists, in my case, over 6700 individual members. (All genera or genuses of all plants)

The bottleneck seems to be constructing the table and populating the client-side list, and it is not caused by retrieving the information from Memcached.

If anybody else has run into this particular problem, I would love to hear of a clever way to solve it. I will post my code below.

Note: This particular page is unavailable to the general public, and I am aware there are a few gaping security holes.


require_once 'oo/Database.php';

$mysqldb = new Database;

$memcache = new Memcache;
$memcache->connect('localhost', 11211) or die ("Could not connect to memcache");

$sql = "SELECT DISTINCT `Genus` FROM importlist.plants";

$key = md5('query'.$sql);

$result = $memcache->get($key);


//check if we got something back
if($result == null) {

    //fetch from database
    $result = $mysqldb->rawSelect($sql)->getResult();

    //set to memcache, expires after 1 hour
    $memcache->set($key,$result,0,3600); 
}

//Result array
$Genera = ($memcache->get($key));

//Add required "quotation marks" for autocomplete
foreach ($Genera as &$Genus){
    $Genus = '"'.$Genus[Genus].'"';
} 
$Genera = implode($Genera,',');

//PHP to generate jQuery    
echo <<< EOT

    <script>
    $(function() {
        var availableTags = [$Genera];
        $( "#tags" ).autocomplete({
            source: availableTags
        });
    });
    </script>
EOT;

?>

<input id="tags" />
Community
  • 1
  • 1
Ryan Ward Valverde
  • 6,458
  • 6
  • 37
  • 48
  • 2
    Don't know that this would speed it up at all, but you should be able to set `$Genera = $result` instead of making the second call to the memcache. Or better yet, just use `$result` in your `foreach()` rather than `$Genera` – jprofitt Feb 29 '12 at 01:54
  • 2
    Can you use an ajax request to populate the data after the page loads or before the user tries to use the auto-complete? – jamesmortensen Feb 29 '12 at 01:54
  • 4
    It looks like you're emulating creating a json notation of an array. Probably `json_encode` will give you better speed than the foreach. However, you should first find out which part of that chunk of code consumes so much time. Locate the bottleneck first by running some metrics. – hakre Feb 29 '12 at 02:01
  • XDebug might be able to help you find what is bottle necking it. – John V. Feb 29 '12 at 02:02

2 Answers2

5
$(document).ready(function() {

    // once page loads, make AJAX request to get your autocomplete list and apply to HTML
    $.ajax({ url: '/path-to-get-tags-as-json.php',
        type: "GET",
        contentType: "application/json",
        success: function(tags) {
            $( "#tags" ).autocomplete({
                source: tags
            });
        }
    });
});

Place the URL to your PHP file at generates the autocomplete list in the above AJAX placeholder url parameter. In your PHP code, modify the list generation so that it returns a JSON array of values like so:

[ "first" , "second" , "anotherEntry" , "in" , "the" , "array" ]    

This will definitely not speed up the process server side, but it will protect your users from some of the delays in applying the autocomplete list. This largely assumes that the user doesn't instantly go perform an action that requires autocomplete, you can still load the page and allow the user to perform other actions. The loading of the autocomplete list should for the most part appear silent and seamless.

This is great for load times that are less than a few to several seconds, but if it's taking you longer than that then your users may still run into usability trouble.

If there are still server-side delays, consider using some timing statements to try and determine where the bottleneck is.

jamesmortensen
  • 33,636
  • 11
  • 99
  • 120
  • If you use this you could just replace your foreach loop with json_encode. – John V. Feb 29 '12 at 02:09
  • 4
    Exactly what I was thinking. It wouldn't be hard to make the change, and it would make it look more Web 2.0 and possibly a little faster. I should also add that Afonso may need to set the content type in PHP to "application/json" before returning the JSON array so that the browser recognizes it as JSON. – jamesmortensen Feb 29 '12 at 02:11
  • Thank you jmort, I am going to dive into the code for a while, I really appreciate your suggestions! – Ryan Ward Valverde Feb 29 '12 at 03:53
  • I really like this idea, but I am still working on `json_encode`, the data doesn't seem to want to get formatted that way. I guess it'll just take some tinkering. – Ryan Ward Valverde Feb 29 '12 at 05:25
  • 1
    It looks like you're getting an array out of memcached. json_encode should encode the array into a JSON string for you automatically. So you shouldn't need to wrap it in quotes or do anything to it. Once retrieved in the jQuery callback function, you'll be able to loop through that array, but in JavaScript. Check out the [json_encode](http://us.php.net/json_encode) page for examples and usage. You're really close. – jamesmortensen Feb 29 '12 at 07:21
  • 1
    This is exactly what I need. Thank you so much. – theGreenCabbage Jul 24 '15 at 21:16
1

Since you can't lookup anything until the user types at least 1 character, you can create 26 different lists. Each autocomplete list only contains the items that begin with that letter. Your lists will be significantly smaller and faster to load.

To make it even faster, create more lists. You probably only need the first 30-40 items to display. If the item isn't in the shortened list, the user will very likely type another letter. You can then divide your lists up into 26*26 unique lists. Each list containing only items that begin with the first 2 letters.

You can divided your items up into as many lists as needed. We do this on a site I manage where we have over 500K items available in our typeahead.

Brent Baisley
  • 12,641
  • 2
  • 26
  • 39