Full-Text Search (MS SQL Server) pro internetové obchody a jiné aplikace.
25 března 10 12:44 odp. | MichalJezek | 7 Comments   

    Úvod
    Co očekává uživatel od hledání?
    Karsa Search Manager
    Testovací databáze
    Vytvoření fulltextového katalogu
    Kvalita hledání 
    Váha sloupců 
    iFTS Predicates and Functions
    Vyhledávací vzory
    Personalizované hledání
    Přímé volání výsledné uložené procedury
    Závěr

Úvod
Cílem článku je příblížit problematiku vytvoření a správu relevantního full-textového hledání ve webových projektech - internetový obchodech. Informace zde uvedené vycházejí ze zkušeností při tvorbě aplikace Karsa SQL fulltext search manager a následných implementacích hledání. V článku se zaměřujeme na problematiku hledání v internetových obchodech, ale postup lze samozřejmě aplikovat na libovolnou webovou, desktopovou aplikaci. 

 
Co očekává uživatel od hledání?

Uživatel očekává, že pokud zadá hledáný výraz, ihned se zobrazí relevantní výsledky. Nepřemýšlí nad tím, že Vaše aplikace není google. Pokud se nezobrazí relevantní výsledky zpravidla odchází. Tím klesá konverzní poměr – nákupu.
Obecně uživatelé nedůvěřují hledání na stránkách mimo hlavní portály, právě z důvodu nízké relevantnosti výsledků. Cílem by tedy mělo být vytvořit co nejvíce relevantní hledaní a s MS SQL Server iFTS (Integrated Full Text Search) tato možnost rozhodně je.

 

Karsa Search Manager

Karsa Fulltext Search Manager je desktopová aplikace, díky  které se zvýšuje efektivita práce a redukují se chyby při tvorbě fulltextového hledání. Desktopový manager slouží k nastavení
hledání a následnému vygenerování uložených procedur na MS SQL Server.Není tedy potřeba registrace žádných dll ani vkládání .net kódu na SQL Server, vždy se spouští standardní T-SQL script a využívají se široké možnosti SQL Server iFTS.

Vytvoření několik set řádkového scriptu je velice časově náročné, nehledě na jeho následné úpravy dle a/b testování a vyhodnocení logů a právě v tomto aplikace významně vývojáři usnadňuje život. Aplikace neomezuje vývojáře při tvorbě vlastních vyhledávacích scriptů a odvádí “černou práci” při generování pomocných scriptů, výtvoření katalogu, propojení, profilování, aktualizacích.  

Testovací databáze

Pro ukázky hledání budeme používat toto zjednodušené schéma imaginárního internetového obchodu.

DB_schema

Tabulka eshop_product obsahuje jen několik záznamů, na kterých budeme demostrovat hledání

items

 

Vytvoření fulltextového katalogu

K vytvoření Fulltextového katalogu můžeme využít více možností.

1. SQL Server Managment studio
2. T-SQL
3. Karsa Search manager

Zde si uvedeme možnosti 1 a 3.

SQL Server Managment studio
1. V objekt exploreru, otevřete server, rozbalte databáze a otevřete databázi v které chcete vytvořit full-text katalog.
2. Rozbalte Storege, klikněte pravým tlačítkem na Full Text Catalogs
3. Vyberte New Full-Text Catalog
4. V New Full-Text Catalog dialogu uveďte informace o katalogu, který chcete vytvořit.

 

NewFulltextCatalog

Nový full-text katalog

FulltextCatalogProperties

Výběr tabulek a indexu

Při výběru indexu dejte pozor, aby byl vybrán Clustered index. Clustrovaný index je výrazně rychlejší

IndexClustered

Karsa Search manager

Výhodou použití je svázanost návrhu full-textového hledání s katalogem. Pokud přidáte nový sloupec ve kterém chcete hledat a tento sloupec není definován ve full-textovém katalogu je tento sloupce označen červeně.

AddFulltextColumn

Po stisknutí tlačítka Generování pomocných scriptů se na databázi nebo do souboru vygeneruje pomocný script, který sloupce, přidá do full-textového katalogu

GenerovaniPomocnychScriptu

Při návrhu hledaní se těchto změn provádějí desítky a tato funkcionalita šetří velice čas. Pokud není vytvořen full-text katalog je také automaticky vytvořen.

Kvalita hledání

Kvalitou hledání rozumíme počet výsledků, které jsou relevantní k hledanému výrazu. Nejčastější chybou hledání v aplikacích je přiliš nízká míra relavantních výsledků v hledání. Výsledk obsahuje příliš irelevantních záznamů, nebo neobsahuje žádné záznamy. To je způsobeno přiliš striktním full-textovým dotazem. Například hledaná fráze: Mountains bike pedals

select  * from eshop_product where  contains([Description], ' "mountains" and "bike" and "pedals"') 

Výsledkem je nalezený jeden záznam

3slovacontains

Pokud by jsme použili tento dotaz ve výsledku se nezobrazí záznam, který
v popisu obsahuje jen slova bike and pedals

Mužeme změnit dotaz například takto

select  * from eshop_product where  contains([Description], ' ("mountains" and "bike" and "pedals") or ("bike" and "pedals")')

Výsledkem jsou dva záznamy

3slovacontainsAND

tak, aby vyhledal dvou i tříslovný výraz.
Pokud bude tabulka eshop_product obsahovat dva zaznamy jeden v popisu obsahující všechna tři slova a druhý jen dvě, musíme toto zohlednit ve výsledku hledání, kdy první záznam obsahující všechna tři slova dostane výšší rank. Stejně by jsme měli zohlednit to pokud slova jsou nalezena v jiném tvaru při použítí

FORMSOF(INFLECTIONAL, bikes near mountains )

nebo v jiném pořadí.

 

Duležité
Obdobně by záznam měl dostat vyšší rank pokud se hledaný výraz vyskytuje v názvu produktu v našem případě sloupce Name něž pokud se nalézne shoda v popisu.

Karsa full-text manager toto řeší pomocí vyhledávacích vzorů, které lze přiřadit ke každému sloupci a vyhledávacímu vzoru lze nastavit váhu, která se započte do výsledného ranku záznamu. Také je možno nastavit váhu celého sloupce. Vyhledávací vzory jsou základním nástrojem, kterým je možné detailně vyladit váhy hledání a docílit tak relevantnějších výsledků hledání.

FulltextPatern

Nastavení váhy sloupce a vyhledávacích vzorů

iFTS Predikáty a funkce (Predicates and Functions)

Pomocí predikátů T-SQL, které se používají v klauzulích WHERE, HAVING nebo ve spojovacích podmínkách můžeme prohledávat nestrukturovaná textová data. V textových datech můžeme hledat gramatické tvary slov, synonyma, přesnou shodu. Hledání vrací relevantnější výsledky a je rychlejší než použítí LIKE, protože se textová data prohledávají podle významu a ne podle shody s hledaným výrazem.

FREETEXT - FREETEXTTABLE

Predikát FREETEXT automaticky hledá shodu s gramatickými tvary slov, přesné shody a synonyma. Automaticky také do ranku započítává blízkost hledaných slov odsebe.

select  * from eshop_product where  freetext([Description], ' bike pedals')

Výsledkem jsou tři záznamygramatickyTvar

FREETEXTTABLE

Syntaxe a funkcionalita predikátů FREETEXT a FREETEXTTABLE je velmi podobná

select id, f.rank, Name, Description   from eshop_product inner join  freetextTable(eshop_product,Description, ' bike pedals' ) f on eshop_product.id = f.KEY

Zobrazení ranku záznamů
 2slovaFREETEXTTABLE

FREETEXTTABLE přidává možnost definovat arumenty, název tabulky, sloupce, které se mají prohledávat. Vrací také rank, podle kterého lze omezit počet výsledků.

FREETEXT – FREETEXTTABLE jsou méně precizní způsoby hledání než predikát CONTAINS. Právě pro svoji “automatičnost„ si uživatelé často stěžují na nerelevantní výsledky. Z tohoto důvodu se v karsa full-text manageru predikát FREETEXT nepoužívá.

 

CONTAINS - CONTAINSTABLE

Nabízí rozšířené možnosti oproti predikátu FREETEXT. CONTAINSTABLE se používá jako základní predikát při vytváření vyhledávacích vzorů v Karsa full-text search manageru.

Můžeme použít hledání pomocí:

Gramatických tvarů
Fráze
Operátorů AND OR NOT
Blízkosti slov (proximity)
Váhy slov (Weighted terms)
Prefixů

Gramatických tvarů

Pokud chceme najít slovo bike i v jiném tavaru použijeme predikát FORMSOF(INFLECTIONAL, bikes) v ukázce doplněno o hledání v THESAURUS souboru

SELECT * FROM eshop_product  WHERE contains([Description], ' FORMSOF(INFLECTIONAL, bikes) or  FORMSOF(THESAURUS, bicycle)' )

 

Fráze

Hledání přesné fráze

SELECT * FROM eshop_product  WHERE CONTAINS ([Description], '"mountains  bike pedals"');

3slovacontains

Operátor AND OR NOT

Hledáme slova bike a pedals a nechceme ve výsledku zobrazit silniční kola

SELECT * FROM eshop_product  WHERE contains([Description], '"bike" and "pedals" and not (FORMSOF(INFLECTIONAL, "road") or "road bicycles") ' )

3slovacontainsAND

Blízkost slov

NEAR zohledňuje vzdálenost slov v hledaném textu od sebe. Bližším slovům přiděluje vyšší rank.

SELECT * FROM eshop_product  WHERE contains([Description], 'bike NEAR mountains ')

near

Váha slov

Pomocí ISABOUT lze pridělit slovům výšší váhu. Tato váha se promítne u nalezených záznamů do výsledného ranku.

select id, f.rank, Name, Description   from eshop_product inner join  containstable(eshop_product,Description, 'ISABOUT(bike weight(.9), pedals weight(.3) )  ' ) f on eshop_product.id = f.KEY

isabout 

Prefixy

Použitím zástupného symbolu hvězdička *, který prezentuje nula nebo více znaků
lze docílit obdobného hledání jako při použití LIKE. Zde, ale těžíme z výšší rychlosti full-textového hledání. Toto hledání se často používá při hledání kódů produktů.

SELECT * FROM eshop_product  WHERE CONTAINS ([Description], '"moun*"');

hvezdicka

 

Vyhledávací vzory

Vyhledávací vzory umožňují přesné vyladění vah hledání. Předdefinováno je celkem 9 vzorů hledání. Vzory je možné také vytvářet vlastní 

vyhledavaciVzory

U každého vzoru je možné nastavit jeho název, defaultní váhu a samotný obsah vzoru. Vyhledávací vzory je možné aplikovat také na vazební tabulky a tím docílit vyšší relevance hledání. V naši ukázce například prohledávaním tabulky eshop_forum. Obsah vzoru je definován pomocí těla C# metody (vb.net se připravuje), který je možno editovat. Například

public string Test(string text, string[] words){
//Tento obsah je možné editovat
}
//String text vyhladávaný text
//string[] words - pole s vyhledávaným textem, rozděleným na slova.

 

Zjednodušeně platí: words = text.Split(" ")
Tělo metody musí vrátit validní SQL fulltextový vzor vhodný pro zadání vyhladávaných slov. V těle metody lze použít jakýkoliv validní C# výraz. Můžete například odstranit nechtěné znaky, přeházet slova atd. Tato funkce se ukládá na SQL server jako text do pomocné tabulky a dodávaná knihovna dll filtering provider ji načíta. Není tedy využíván .Net kód na straně SQL Serveru.  Pokud se vyhledávání neaplikuje, není odesíláné na SQL server a nesnižuje se tak rychlost  

hledání. Ve výsledném SQL dotazů může být použit jakýkoliv validní SQL výraz, například FORMSOF INFLECTIONAL

Ukázeme si krátký postup jak vytvořit vlastní vzor. Budeme vytvářet nový vzor hledání, který bude zohledňovat váhu pořadí slov v hledaném výrazu, pomocí predikátu ISABOUT. Tento vzor není standardně definován.
Vzor nazveme IsAboutMoreWords. Vzor bude aplikován pokud v hledaném výrazu je použito tři a více slov. Dle pořadí slov v hledaném výrazu přiřadí těmto slovům váhu.

if (words.Length < 3)
{
return string.Empty;
}
System.Text.StringBuilder result = new System.Text.StringBuilder();
result.Append("'ISABOUT(");
double weight = 1.0;
for (int i = 0; i < words.Length; i++)
{
string weightText = weight.ToString().Replace(",", ".");
result.AppendFormat("\"{0}\" weight({1})", wordsIdea, weightText);
if (i < words.Length - 1)
{
result.Append(", ");
}
weight -= 0.1;
if (weight < 0.1)
{
weight = 0.1;
}
}
result.Append(")'");
return result.ToString();

Pokud budeme hledat výraz „mountains  bike pedals“ výsledkem je tento string 'ISABOUT("mountains" weight(1), "bike" weight(0.9), "pedals" weight(0.8))' , který se použije v predikatu CONTAINSTABLE. Vzor stačí uložit a můžete jej přiřadit k libovolným sloupcům, kde jej chcete použít.

fulltextpaternEdit

Editace vyhledávacího vzoru. Editor kontroluje validnost kódu.

 

Personalizované hledání

Pomocí rersonalizovaného hledání můžeme uživateli nabídnout ješte více relevantnější výsledky. Můžeme měnit váhu sloupců a vyhledávacích vzorů při každém hledání a to pro konkrétního uživatele.
Tím se nám nabízejí šíroké možnosti použítí. Můžeme například logovat pohyb uživatele  po obchodu a dle stráveného času v určitých kategoriích preferovat určité sloupce, vyhledávací vzory nebo modifikovat hledaný výraz.   
Personalizované hledání může být velice silným nástrojem.

Ukázka nastavení váhy sloupce Name na 100

FilterService service = new
FilterService(@"Server=.\SQLEXPRESS;Database=KarsaFulltextSample;Trusted_Connection=True");

FilterCriteria filterCriteria = new FilterCriteria();
filterCriteria.FulltextSearchText = "bike";

IEnumerable<FulltextColumnsPattern> fulltextPatterns;
fulltextPatterns = service.GetDefaultFulltextColumnsPatterns();

foreach (FulltextColumnsPattern fulltextPattern in fulltextPatterns)
{
if ((fulltextPattern.NodeName == "eshop_product") &&
(fulltextPattern.Columns.Contains("Name")))
{
fulltextPattern.Weight = 100;
}
}

FilterResult result = service.GetItems(1, 10,
filterCriteria, fulltextPatterns);
foreach (ResultItem item in result.ItemsOnPage)
{
Console.WriteLine(item.Identification["ID"]);
}












FilterService service = new
FilterService(@"Server=.\SQLEXPRESS;Database=KarsaFulltextSample;Trusted_Connection=True");

FilterCriteria filterCriteria = new FilterCriteria();
filterCriteria.FulltextSearchText = "bike";

IEnumerable<FulltextColumnsPattern> fulltextPatterns;
fulltextPatterns = service.GetDefaultFulltextColumnsPatterns();

foreach (FulltextColumnsPattern fulltextPattern in fulltextPatterns)
{
if ((fulltextPattern.NodeName == "eshop_product") &&
(fulltextPattern.Columns.Contains("Name")))
{
fulltextPattern.Weight = 100;
}
}

FilterResult result = service.GetItems(1, 10,
filterCriteria, fulltextPatterns);
foreach (ResultItem item in result.ItemsOnPage)
{
Console.WriteLine(item.Identification["ID"]);
}

 

Přímé volání výsledné uložené procedury

Možnost volat výslednou uloženou procedůru z karsa fulltext search manageru především ocení vývojáři, kteří nepoužívají C#, vb.net, ale třeba PHP, Java, Python, C++ a chtějí vytvořit relevantnější hledání.

Ukázka přímého volaní.

EXEC karsa_generated_list_selection
-- limit result to only first 100 items
@minIndex = 0, -- items with smaller index are skipped
@maxIndex = 99, -- items with bigger index are skipped

-- text to search for
@fulltextSearchText = 'sony',
-- table with fulltext patterns associated with columns
@fulltextColumnPatternTable = '#inputFulltextColumnPattern',
-- minimal required rank
@fulltextMinRank = 1,
-- If not null, this table will contain debug data. If null, debug data are not provided.
@fulltextDebugTable = '#outputFulltextDebugData',

-- Sorters are comma-separated and prefix with "+" (ascending) or "-" (descending)
@sorters = '+Name,-Manufacturer',
-- This table will be filled with result items
@resultTable = '#outputResult',
-- If not null, this table will contain profile data. If null, profile data are not provided.
@profileTable = '#outputProfileData'

 

Závěr

Karsa fulltext manager je ke stažení v české i anglické verzi.
V článku nejsou uvedený další možnosti zvyšování relevance, jako jsou například.

Vyhledávání v relačních tabulkách
Stoplist  
Thesaurus file 
Zvýraznění výsledků hledání  / Highlighting
Našeptávač
Historie hledání   
Logování hledáných výrazů
Cachování hledaných výrazů
Logování konverze | A/B testování
Parametrické hledání

o těchto možnostech případně přístě.

Vyvojar.cz na prodej!