Tuesday, January 31, 2006

Patterns Patterns Patterns: Filtering Results

Say you have a bookmarks site, like del.icio.us. You've got squintillions of results for a search, and your boss gets a feature request for "being able to organise the information by the time the sun rose when the bookmark was entered".

Your boss, being your boss, decides that this would make a fine feature. You get stuck with it.

The first step is to take advantage of your kick ass indexing (you've done that, haven't you?). You grab a whole lot of search result IDs, we don't care about the rest of it at the moment.


function search($db, $filters) {

//First we get a hell of a lot of id-only results. You do use PEAR::DB don't you?
$sql = "SELECT id FROM tags WHERE someOverlyBroadCriteria = true";
$tags = $db->getAll($sql);

foreach ($filters as $filter) {
$tags = $filter->search($db, $tags);
}

//We've done all of our filtering, now actually retrieve the information
//and pass off responsibility to some rendering component
$sql = sprintf("SELECT * FROM tags WHERE id IN (%s)", implode(', ',$tags));
return $db->query($sql);
}


We've passed in an array of Filter objects ($filters), each which has a search() method defined. The search() method takes in an array of IDs and returns one on the other side.


class EskimoFilter implements iFilter {
function search($db, $input) {
$sql = sprintf("SELECT id
FROM tags
WHERE id IN (%s)
AND tag LIKE `eskimo%%`",
implode(', ',$input));
return $db->getAll($sql);
}
}


The beauty of object oriented programming is that noone cares what's on the inside so long as it works nicely with the others on the outside. So, adding more filters into this workflow is pretty simple: you just have to be careful to pay attention to the order in which you do it.

You can easily store customised filtersets in a database on a user by user basis - and next time someone whinges that they need to look at search results created by users with only 7 pet penguins, you don't panic.

Whoever said design patterns weren't useful, eh?

No comments: