1. Let’s first add the WordCount function. Navigate to the BalloonShop connection in Database Explorer, right-click Functions, and choose Add New ➤ Scalar-valued Function. Change the function skeleton Visual Web Developer created for you with the following code:
CREATE FUNCTION dbo.WordCount (@Word VARCHAR(15),
@Phrase VARCHAR(1000)) RETURNS SMALLINT AS
BEGIN
/* If @Word or @Phrase is NULL the function returns 0 */
IF @Word IS NULL OR @Phrase IS NULL RETURN 0
/* @BiggerWord is a string one character longer than @Word */
DECLARE @BiggerWord VARCHAR(21) SELECT @BiggerWord = @Word + 'x'
/* Replace @Word with @BiggerWord in @Phrase */
DECLARE @BiggerPhrase VARCHAR(2000)
SELECT @BiggerPhrase = REPLACE (@Phrase, @Word, @BiggerWord) /* The length difference between @BiggerPhrase and @phrase is the number we're looking for */
RETURN LEN(@BiggerPhrase) - LEN(@Phrase) END
2. Now add the SearchCatalog stored procedure. This stored procedure uses WordCount to calculate the search results. Using the steps you already know, add this stored procedure to your BalloonShop database:
CREATE PROCEDURE SearchCatalog (@DescriptionLength INT, @PageNumber TINYINT, @ProductsPerPage TINYINT, @HowManyResults SMALLINT OUTPUT, @AllWords BIT,
@Word1 VARCHAR(15) = NULL, @Word2 VARCHAR(15) = NULL, @Word3 VARCHAR(15) = NULL, @Word4 VARCHAR(15) = NULL, @Word5 VARCHAR(15) = NULL) AS
/* Create the table variable that will contain the search results */
DECLARE @Products TABLE
(RowNumber SMALLINT IDENTITY (1,1) NOT NULL, ProductID INT,
Name VARCHAR(50),
Description VARCHAR(1000), Price MONEY,
Image1FileName VARCHAR(50), Image2FileName VARCHAR(50), Rank INT)
/* Populate @Products for an any-words search */
IF @AllWords = 0
INSERT INTO @Products SELECT ProductID, Name,
SUBSTRING(Description, 1, @DescriptionLength) + '...' AS Description, Price, Image1FileName, Image2FileName,
3 * dbo.WordCount(@Word1, Name) + dbo.WordCount(@Word1, Description) + 3 * dbo.WordCount(@Word2, Name) + dbo.WordCount(@Word2, Description) + 3 * dbo.WordCount(@Word3, Name) + dbo.WordCount(@Word3, Description) + 3 * dbo.WordCount(@Word4, Name) + dbo.WordCount(@Word4, Description) + 3 * dbo.WordCount(@Word5, Name) + dbo.WordCount(@Word5, Description) AS Rank
FROM Product ORDER BY Rank DESC
/* Populate @Products for an all-words search */
IF @AllWords = 1
INSERT INTO @Products SELECT ProductID, Name,
SUBSTRING(Description, 1, @DescriptionLength) + '...' AS Description,
Price, Image1FileName, Image2FileName,
(3 * dbo.WordCount(@Word1, Name) + dbo.WordCount (@Word1, Description)) *
CASE
WHEN @Word2 IS NULL THEN 1
ELSE 3 * dbo.WordCount(@Word2, Name) + dbo.WordCount(@Word2, Description)
END * CASE
WHEN @Word3 IS NULL THEN 1
ELSE 3 * dbo.WordCount(@Word3, Name) + dbo.WordCount(@Word3, Description)
END * CASE
WHEN @Word4 IS NULL THEN 1
ELSE 3 * dbo.WordCount(@Word4, Name) + dbo.WordCount(@Word4, Description)
END * CASE
WHEN @Word5 IS NULL THEN 1
ELSE 3 * dbo.WordCount(@Word5, Name) + dbo.WordCount(@Word5, Description)
END AS Rank FROM Product ORDER BY Rank DESC
/* Save the number of searched products in an output variable */
SELECT @HowManyResults = COUNT(*) FROM @Products
WHERE Rank > 0
/* Send back the requested products */
SELECT ProductID, Name, Description, Price, Image1FileName, Image2FileName, Rank
FROM @Products WHERE Rank > 0
AND RowNumber BETWEEN (@PageNumber-1) * @ProductsPerPage + 1 AND @PageNumber * @ProductsPerPage
ORDER BY Rank DESC
How It Works: WordCount and SearchCatalog
In the first step of the exercises, you wrote the WordCount function. This function returns the number of times the
@Word string appears in @Phrase (@Word and @Phrase are its input parameters). We discussed how this function works earlier in this chapter.
■Note The function is completely unaware of the fact that you’re searching for words; it simply searches for substrings. The effect is that if you search for “love,” it finds a match on “lovely,” for example. If you don’t like this behavior, there are a number of ways to change it. One of them is to append a space before and after the word to search for, do some preparation on the phrase (such as add leading and trailing spaces), and then perform the search with these.
SearchCatalog is a bit more complex. First of all, let’s analyze its parameters:
• @DescriptionLength is the maximum length of the product description.
• @PageNumber specifies the page of results the visitor has requested.
• @ProductsPerPage specifies how many records to return. If @PageNumber is 3 and
@ProductPerPage is 5, the procedure will return the 11th to 15th records from the search results.
• @HowManyResults is an output parameter, which you’ll set to the total number of search results. This will be read from the C# code to calculate the number of search results pages.
• @AllWords is a bit input parameter that specifies whether you should do an all-words or any-words search.
• @Word1 to @Word5 are the words to be searched for. They all have a default value of NULL.
The stored procedure starts by creating the @Products table variable:
/* Create the table variable that will contain the search results */
DECLARE @Products TABLE
(RowNumber SMALLINT IDENTITY (1,1) NOT NULL, ProductID INT,
Name VARCHAR(50),
Description VARCHAR(1000), Price MONEY,
Image1FileName VARCHAR(50), Image2FileName VARCHAR(50), Rank INT)
The RowNumber field is used for paging, as you learned in Chapter 4. This time it’s also an IDENTITY column, to make its calculation a bit easier later on.
We also have a field called Rank that contains the search result ranking (products with higher values are listed first).
After creating the table variable, you populate it by searching the product catalog. For this, the stored procedure reads the @AllWords bit parameter and decides what kind of search to do depending on its value (an all-words search versus an any-words search). The logic of searching the catalog was explained earlier in this chapter.
8213592a117456a340854d18cee57603
After searching the catalog and populating @Products, you set the value of the @HowManyResults output parameter (which will be read from the business tier), by counting the number of rows in @Products:
/* Save the number of searched products in an output variable */
SELECT @HowManyResults = COUNT(*) FROM @Products
WHERE Rank > 0
Finally, you get back the portion of records from @Products based on the @PageNumber and @ProductsPerPage input parameters:
/* Send back the requested products */
SELECT ProductID, Name, Description, Price, Image1FileName, Image2FileName, Rank
FROM @Products WHERE Rank > 0
AND RowNumber BETWEEN (@PageNumber-1) * @ProductsPerPage + 1 AND @PageNumber * @ProductsPerPage
ORDER BY Rank DESC