Fulltext v prostředí Windows Azure

Tomáš Herceg       11.08.2014       SQL, Azure, Databáze       13355 zobrazení

Jedním z omezení Azure SQL databází je nemožnost používat SQL Server Fulltext. Je nutné použít některou z alternativ, jako například Lucene.Net, což je open source knihovna, která fulltextové hledání implementuje. Data si tato knihovna standardně ukládá do filesystému, ale díky rozšíření Lucene.Net.Store.Azure umožňuje ukládat index i do blob storage, díky čemuž je snadno použitelná v cloudu i bez nutnosti vytváření virtual machine.

Nejprve je třeba přidat knihovny Lucene do projektu – stačí pomocí NuGetu nainstalovat balíček Lucene.Net.Store.Azure, NuGet si už dohledá všechny potřebné závislosti včetně Lucene samotného a přidá je do projektu.

Přidání Lucene do projektu

 

indexování dat

Data, ve kterých chcete hledat, se v terminologii Lucene nazývají dokumenty. Vzhledem k tomu, že data držíme primárně v SQL databázi a z Lucene chceme používat jen fulltext, stačí nám indexovat pouze primární klíč každého dokumentu, který se používá v SQL Serveru, a dále pak všechny textové sloupce, které chceme prohledávat.

Pro přidávání dat do indexu slouží v Lucene třída IndexWriter. Přidání dokumentu do indexu může vypadat nějak takto:

public void AddToIndex()
{
    var document = new Document();
    document.Add(new Field("Id", "id z databáze", Field.Store.YES, Field.Index.NO, Field.TermVector.NO));
    document.Add(new Field("Title", "tohle je název", Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.NO));
    document.Add(new Field("Text", "tohle je text", Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.NO));

    using (var indexWriter = CreateIndexWriter())
    {
        indexWriter.AddDocument(document);
        indexWriter.Optimize();
    }
}

private IndexWriter CreateIndexWriter()
{
    var account = CloudStorageAccount.Parse("connection string do blob storage");
    var directory = new AzureDirectory(account, "nazev-storage-kontejneru");
    try 
    {
        // open existing index
        return new IndexWriter(directory, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30), false, Lucene.Net.Index.IndexWriter.MaxFieldLength.UNLIMITED);
    }
    catch (Exception ex) 
    {
        // create a new index
        return new IndexWriter(directory, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30), true, Lucene.Net.Index.IndexWriter.MaxFieldLength.UNLIMITED);
    }
}

Každý dokument má vlastnost Id, kam uložíme primární klíč z databáze, a dále všechny textové sloupce, které chceme prohledávat – všimněme si, že mají Field.Index.ANALYZED, což znamená, že se fulltextově prohledávají – ve zkratce Lucene hodnotu projde a zaindexuje každé slovo.

Takto musíme zaindexovat všechny dokumenty, ve kterých chceme hledat.

Dotazování

Možnosti dotazování v Lucene jsou velmi široké, my si ukážeme jen to nejzákladnější.

/// <summary>
/// Searches the specified text.
/// </summary>
public IList<int> Search(string text, int skip, int take, out int totalRows)
{
    using (var indexSearcher = CreateIndexSearcher())
    {
        var multiParser = new MultiFieldQueryParser(Lucene.Net.Util.Version.LUCENE_30, new[] { "Title", "Text" }, new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30));

        var results = indexSearcher.Search(multiParser.Parse(text), skip + take);
        totalRows = results.TotalHits;

        return results.ScoreDocs
            .Skip(skip)
            .Select(d => indexSearcher.Doc(d.Doc).GetField("Id").StringValue)
            .Select(int.Parse)
            .ToList();
    }
}

private IndexSearcher CreateIndexSearcher()
{
    var account = CloudStorageAccount.Parse("connection string do blob storage");
    var directory = new AzureDirectory(account, "nazev-storage-kontejneru");
    return new IndexSearcher(directory);
}

Nejprve se vytvoří instance třídy MultiFieldQueryParser, které musíme prozradit názvy sloupců, nad nimiž chceme hledat. Na ní pak zavoláme multiParser.Parse a předáme tam hledané slovo nebo výraz (např. cat AND mouse). Tento dotaz předáme do funkce indexSearcher.Search, která provede samotné hledání. Druhý parametr značí, kolik nejlepších výsledků má vrátit.
Pokud bychom chtěli stránkovat, tedy např. přeskočit prvních 40 záznamů a vzít následujících 20 (parametry skip a take), tak vězme, že Lucene to nijak snadno neumí (nebo jsem to nikde v dokumentaci nenašel) a musíme tedy vyzobnout všechny záznamy a těch prvních 40 zahodit. Nutno podotknout, že funkce Search vrací jen pole interních ID v Lucene a každý dokument, který nás zajímá, je nutné stejně dotáhnout přes funkci indexSearcher.Doc, které se toto ID předá, takže i kdybychom si řekli o 1000 záznamů, nebude se přenášet zase tolik dat, na výkon by to nemělo mít takový vliv.

Zbytek té funkce transformuje výsledky a vytáhne z nich jen ID dokumentů, které vyhovovaly dotazu. Víc toho potřeba není, ID vezmeme, převedeme na integer a vrátíme.

Pokud chceme výsledky zkombinovat s nějaký SQL dotazem, pak využijeme toho, že Entity Framework chápe funkci Contains na kolekcích – uděláme tedy něco takového:

int totalRows;
List<int> fulltextIds = TemplateItemIndex.Search(ErrorListFilters.Text, 0, 20, out totalRows);
return dbContext.Documents.Where(t => fulltextIds.Contains(t.Id));

Entity Framework to pak převede na něco ve stylu SELECT * FROM Documents WHERE Id IN (1,3,5,18,253…), takže data získáme na jeden dotaz.

 

Doporučuji do storage kontejneru, kde má Lucene svá data, nedávat nic jiného, vypadá to tam nějak takhle.

Data ve storage containeru

 

hodnocení článku

0       Hodnotit mohou jen registrované uživatelé.

 

Mohlo by vás také zajímat

Stopařův průvodce po databázích - díl 3.: Správa služeb a komponent Microsoft SQL Serveru

Tento díl pojednává o komponentách instalovaných se SQL Serverem. Jejich pochopení je důležité pro správnou instalaci a správu celého systému. Zároveň vysvětluji princip přidělování portů službám a popisuji postup ja jej konfigurovat.

Windows Azure - díl 4.: SQL Database – Nasazení databáze, dostupnost a škálovatelnost

Tento díl seriálu je věnován migraci databáze do Windows Azure a také dostupnosti a škálovatelnosti této služby.

představení nového seriálu o ASP .net MVc - from zero to hero

 

 

Nový příspěvek

 

Příspěvky zaslané pod tento článek se neobjeví hned, ale až po schválení administrátorem.

Výkon

Dokážeš otestovat výkon? Například rychlost zápisu/vyhledávání při 500MB/1GB/5GB/10GB.

nahlásit spamnahlásit spam 0 / 2 odpovědětodpovědět
                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

Příspěvky zaslané pod tento článek se neobjeví hned, ale až po schválení administrátorem.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback