Saturday, February 6, 2010

Updated Sorting

‹prev | My Chain | next›

Today, I continue my quest to upgrade EEE Cooks to CouchDB 0.10 and couchdb-lucene 0.5. There are still 2 Cucumber scenarios failing the recipe search feature alone. Hopefully few, if any are still failing elsewhere.

First up is the sorting scenario (originally described way back in April):
cstrom@whitefall:~/repos/eee-code$ cucumber ./features/recipe_search.feature:76 -s
Feature: Search for recipes

So that I can find one recipe among many
As a web user
I want to be able search recipes

Scenario: Sorting (name, date, preparation time, number of ingredients)
Given 50 "delicious" recipes with ascending names, dates, preparation times, and number of ingredients
And a 1 second wait to allow the search index to be updated
When I search for "delicious"
Then I should see 20 results
When I click the "Name" column header
Then the results should be ordered by name in ascending order
expected following output to contain a <tr:nth-child(3) a>delicious recipe 10</tr:nth-child(3) a> tag:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html>
<head>
<title>EEE Cooks: Recipes</title>
<link href="/stylesheets/style.css" rel="stylesheet" type="text/css">
<link href="/main.rss" rel="alternate" title="EEE Cooks RSS" type="application/rss+xml">
<link href="/recipes.rss" rel="alternate" title="EEE Cooks Recipe RSS" type="application/rss+xml">
</head>
<html><body>
...
<div id="refine-search">
<form action="/recipes/search" id="search-form" method="get">
<input maxlength="2048" name="q" size="31" type="text" value="delicious"><input name="s" type="submit" value="Search">
</form>
</div>
<table>
<tr>
<th>
<a href="/recipes/search?q=delicious&sort=sort_title&order=desc" id="sort-by-name">Name</a>
</th>
<th>
<a href="/recipes/search?q=delicious&sort=sort_date&order=desc" id="sort-by-date">Date</a>
</th>
<th>
<a href="/recipes/search?q=delicious&sort=sort_prep" id="sort-by-prep">Prep</a>
</th>
<th>
<a href="/recipes/search?q=delicious&sort=sort_ingredient" id="sort-by-ingredients">Ingredients</a>
</th>
</tr>
<tr class="row0">
<td>
<a href="/recipes/2008/04/28/delicious_1">delicious recipe 1</a>
</td>
<td>
<span class="date">2008-04-29</span>
</td>
<td class="numeric">
<span class="prep">1</span>
</td>
<td>
<span class="ingredients">ingredient 1, ingredient 2, ingredient 3, ingredient 4, ingredient 5, ingredient 6, ingredient 7, ingredient 8, ingredient 9, ingredient 10, ingredient 11, ingredient 12, ingredient 13, ingredient 14, ingredient 15, ingredient 16, ingredient 17, ingredient 18, ingredient 19, ingredient 20, ingredient 21, ingredient 22, ingredient 23, ingredient 24, ingredient 25, ingredient 26, ingredient 27, ingredient 28, ingredient 29, ingredient 30, ingredient 31, ingredient 32, ingredient 33, ingredient 34, ingredient 35, ingredient 36, ingredient 37, ingredient 38, ingredient 39, ingredient 40, ingredient 41, ingredient 42, ingredient 43, ingredient 44, ingredient 45, ingredient 46, ingredient 47, ingredient 48, ingredient 49, ingredient 50</span>
</td>
</tr>
<tr class="row1">
<td>
<a href="/recipes/2008/04/28/delicious_2">delicious recipe 2</a>
</td>
...
</tr>
...
</table>
<div class="pagination">
<span class="inactive">« Previous</span><span class="current">1</span><a href="/recipes/search?q=delicious&sort=sort_title&page=2">2</a><a href="/recipes/search?q=delicious&sort=sort_title&page=3">3</a><a href="/recipes/search?q=delicious&sort=sort_title&page=2">Next »</a>
</div>
</body></html>
</html>
(Spec::Expectations::ExpectationNotMetError)
./features/step_definitions/recipe_search.rb:287:in `/^the results should be ordered by name in ascending order$/'
./features/recipe_search.feature:83:in `Then the results should be ordered by name in ascending order'
When I click the "Name" column header
Then the results should be ordered by name in descending order
When I click the next page
Then I should see page 2
And the results should be ordered by name in descending order
When I click the "Date" column header
Then I should see page 1
And the results should be ordered by date in descending order
When I click the next page
Then I should see page 2
When I click the "Date" column header
Then the results should be ordered by date in ascending order
And I should see page 1
When I click the "Prep" column header
Then the results should be ordered by preparation time in ascending order
When I click the "Ingredients" column header
Then the results should be ordered by the number of ingredients in ascending order

Failing Scenarios:
cucumber ./features/recipe_search.feature:76 # Scenario: Sorting (name, date, preparation time, number of ingredients)

1 scenario (1 failed)
23 steps (1 failed, 17 skipped, 5 passed)
0m7.953s
The problem is that sorting is not being applied. This is evidenced by the third row in the HTML output (in bold above). The recipes in this scenario are generated with names: Recipe 1, Recipe 2, ..., Recipe 50. When sorted by name they should be Recipe 1, Recipe 10, Recipe 11, ... Recipe 2, Recipe 21, ... Recipe 50. Obviously the number 10 is greater than the number 2, but the string 10 is less than 2. This is because the first character in "10" is a "1", which is less than "2". String comparison would only move onto the second character if the first two are equal. As seen, the second row of results (the third row including the header) has "Recipe 2"—not "Recipe 10"—after sorting has been applied.

Fortunately, the HTTP interface for sorting has not changed between version 0.2 and 0.5-I still need to supply the sort query parameter. The reason that it is not working right now is simple enough: I have yet to tell my couchdb-lucene index how to build the sort fields. So it is back to my fulltext index document
{
"_id": "_design/test",
"_rev": "1-93d99ffe0bddccd4b1aa521f3a569a50",
"fulltext": {
"all": {
"index": "function(rec) { // couchdb-lucene index code here }\n",
"analyzer": "perfield:{default:\"porter\"}"
}
}
}
To get a sortable field for the title into my couchdb-lucene index I need to add a non-analyzed field:
function(rec) {
if (rec.type == 'Recipe' && rec.published) {
var doc = new Document();

doc.add(rec.title);
doc.add(rec.title, {"field":"title", "store":"yes"});
doc.add(rec.title, {"field":"sort_title", "index":"not_analyzed"});

// index other fields ...

return doc;
}
}
Yah, I'm indexing title three times at this point. The one that I just added goes into a separate field in the indexed that is "not analyzed". This means that the words are not split into tokens before being added to the index (a good thing when sorting titles). The other two indexed fields go into the default field and into a title-only field so that I can find the Salmon Mummy recipe by searching for "mummy" or "title: mummy".

Another few more additions to my updated indexing function and I have my entire sorting scenario passing:
cstrom@whitefall:~/repos/eee-code$ cucumber ./features/recipe_search.feature:76
Feature: Search for recipes

So that I can find one recipe among many
As a web user
I want to be able search recipes

Scenario: Sorting (name, date, preparation time, number of ingredients) # ./features/recipe_search.feature:7
6
Given 50 "delicious" recipes with ascending names, dates, preparation times, and number of ingredients # features/step_definitions/recipe_s
earch.rb:155
And a 1 second wait to allow the search index to be updated # features/step_definitions/recipe_s
earch.rb:212
When I search for "delicious" # features/step_definitions/recipe_s
earch.rb:216
Then I should see 20 results # features/step_definitions/recipe_s
earch.rb:260
When I click the "Name" column header # features/step_definitions/recipe_s
earch.rb:246
Then the results should be ordered by name in ascending order # features/step_definitions/recipe_s
earch.rb:284
When I click the "Name" column header # features/step_definitions/recipe_s
earch.rb:246
Then the results should be ordered by name in descending order # features/step_definitions/recipe_s
earch.rb:291
When I click the next page # features/step_definitions/recipe_s
earch.rb:238
Then I should see page 2 # features/step_definitions/recipe_search.rb:278
And the results should be ordered by name in descending order # features/step_definitions/recipe_search.rb:291
When I click the "Date" column header # features/step_definitions/recipe_search.rb:246
Then I should see page 1 # features/step_definitions/recipe_search.rb:278
And the results should be ordered by date in descending order # features/step_definitions/recipe_search.rb:305
When I click the next page # features/step_definitions/recipe_search.rb:238
Then I should see page 2 # features/step_definitions/recipe_search.rb:278
When I click the "Date" column header # features/step_definitions/recipe_search.rb:246
Then the results should be ordered by date in ascending order # features/step_definitions/recipe_search.rb:312
And I should see page 1 # features/step_definitions/recipe_search.rb:278
When I click the "Prep" column header # features/step_definitions/recipe_search.rb:246
Then the results should be ordered by preparation time in ascending order # features/step_definitions/recipe_search.rb:319
When I click the "Ingredients" column header # features/step_definitions/recipe_search.rb:246
Then the results should be ordered by the number of ingredients in ascending order # features/step_definitions/recipe_search.rb:326

1 scenario (1 passed)
23 steps (23 passed)
0m7.362s
With that, and a couple of other minor changes, I have my entire search feature description passing. Tomorrow, I will check the other features to see if anything else needs updating.

Day #6

No comments:

Post a Comment