views:

840

answers:

1

In my full text search query, I want to assign particular columns a higher weightage. Consider this query:

SELECT Key_Table.RANK, FT_Table.* FROM Restaurants AS FT_Table
INNER JOIN FREETEXTTABLE(Restaurants, *, 'chilly chicken') AS Key_Table
ON FT_Table.RestaurantID = Key_Table.[KEY]
ORDER BY Key_Table.RANK DESC

Now, I want the Name column to have a higher weightage in the results (Name, Keywords and Location are full-text indexed). Currently, if the result is found in any of the three columns, the ranks are not affected.

For example, I'd like a row with Name "Chilly Chicken" to have higher rank than one with Keywords "Chilly Chicken", but another name.

Thanks!

Edit:

I'm not eager to use ContainsTable, because that would mean separating the phrases (Chilly AND Chicken, etc.), which would involve me having to search all possible combinations - Chilly AND Chicken, Chilly OR Chicken, etc. I would like the FTS engine to automatically figure out which results match best, and I think FREETEXT does a fine job this way.

Apologies if I've misunderstood how CONTAINS/CONTAINSTABLE works.

+3  A: 

The best solution is to use ContainsTable. Use a union to create a query that searches all 3 columns and adds an integer used to indicate which column was searched. Sort the results by that integer and then rank desc.

The rank is internal to sql server and not something you can adjust.

You could also manipulate the returned rank by dividing the rank by the integer (Name would be divided by 1, Keyword and Location by 2 or higher). That would cause the appearance of different rankings.

Here's some example sql
: --Recommend using start change tracking and start background updateindex (see books online)

    SELECT 1 AS ColumnLocation, Key_Table.Rank, FT_Table.* FROM Restaurants AS FT_Table
 INNER JOIN ContainsTable(Restaurant, Name, 'chilly chicken') AS Key_Table ON 
FT_Table.RestaurantId = Key_Table.[Key]

UNION SELECT 2 AS ColumnLocation, Key_Table.Rank, FT_Table.* FROM Restaurants AS FT_Table
 INNER JOIN ContainsTable(Restaurant, Keywords, 'chilly chicken') AS Key_Table ON 
FT_Table.RestaurantId = Key_Table.[Key]

UNION SELECT 3 AS ColumnLocation, Key_Table.Rank, FT_Table.* FROM Restaurants AS FT_Table
 INNER JOIN ContainsTable(Restaurant, Location, 'chilly chicken') AS Key_Table ON 
FT_Table.RestaurantId = Key_Table.[Key]

ORDER BY ColumnLocation, Rank DESC

In a production environment, I would insert the output of the query into a table variable to perform any additional manipulation before returning the results (may not be necessary in this case). Also, avoid using *, just list the columns you really need.

Edit: You're right about using ContainsTable, you would have to modify the keywords to be '"chilly*" AND "chicken*"', I do this using a process that tokenizes an input phrase. If you don't want to do that, just replace every instance of ContainsTable above with FreeTextTable, the query will still work the same.

Dave_H
I wondered if something using UNION was possible, but my brain couldn't process the SQL involved! :) I think both of these are great solutions, and one of them will probably solve the problem! Let me check and get back to you. I didn't understand, however, why you mentioned additional manipulations?
Wild Thing
Dave, the solution sorta works. Problem is, the select returns duplicate results. I guess the best option is to store into a table variable, as you'd suggested?
Wild Thing
The select will return duplicates, use the table variable, then select distinct. The duplicates are returned because the ColumnLocation and Rank values will be different in each select, therefore sql does not see them as duplicates in joining by union.
Dave_H