Jak se Laravel a Elasticsearch stávají přáteli

Laravel je zatím můj oblíbený rámec. Líbí se mi, že nabízí mnoho možností hned po vybalení. Tým Laravel přemýšlí nad každou částí svého produktu, aby během své rutiny narazil na nástroj, který řeší všechny běžné úkoly, které vývojáři webových stránek narazili.

Dokonce vytvořili skvělý nástroj, který vám pomůže vytvořit vyhledávač na vašem webu. Říká se tomu Laravel Scout. Jedinou nepříjemnou věcí je ovladač, který je dodáván s výchozí instalací Laravelu. Je to řidič Algolie. Algolia je samozřejmě dobrý a rychlý způsob, jak do modelů přidat vyhledávací funkce. Ale já a myslím, že mnoho dalších vývojářů by dalo přednost Elasticsearch jako výchozímu ovladači.

Naštěstí nám tým Laravel dal příležitost vytvořit si vlastní ovladače. Je to docela snadné a všechny potřebné informace o tom najdete v dokumentech Scout. V tomto článku budu používat své vlastní řešení, které jsem vytvořil jako nespokojený se stávajícími. Snažil jsem se sledovat Laravelovu ideologii, abych vytvořil nástroj, který vám umožní snadno a pohodlně jednat s Elasticsearch.

V tomto tutoriálu vás provedu nudnými věcmi, jako je nastavení a konfigurace jiného softwaru, k zajímavé části provádění vyhledávacích dotazů. Naučíme se, jak hledat data v Elasticsearch, jak filtrovat data bez zadání vyhledávacího řetězce a jak provést dotaz pomocí souvisejících polí modelu.

Usedlost

Raději používám Homestead pro vývoj, virtuální stroj poskytovaný týmem Laravel. Pro mě je to pohodlný způsob, jak oddělit pracovní prostředí od mého osobního softwaru a dat, aby se zabránilo nepořádku na mém počítači.

V tomto tutoriálu budu používat Homestead, takže pro Ubuntu jsou uvedeny všechny pokyny pro konzoli.

Pokud používáte VM, připojte se ke svému počítači pomocí SSH a začneme konfigurací softwaru.

Instalace Elasticsearch

K provedení požadavku na vyhledávání musíme nejprve nainstalovat Elasticsearch. To lze snadno provést pomocí správce paketů:

// stáhněte a nainstalujte podpisový klíč Elasticsearch
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -
// nainstalujte balíček apt-transport-https
sudo apt-get install apt-transport-https
// uložení definice úložiště
echo "deb https://artifacts.elastic.co/packages/5.x/apt stabilní hlavní" | sudo tee -a /etc/apt/sources.list.d/elastic-5.x.list
// nainstalujte balíček elasticsearch
sudo apt-get update & & sudo apt-get install elasticsearch

Ale jako obvykle to není jediná akce, kterou musíme udělat, aby to fungovalo.

Otevřete soubor /etc/elasticsearch/elasticsearch.yml pomocí libovolného editoru, například vim, a nastavte network.host na localhost:

network.host: localhost

Nyní musíme službu restartovat:

restart služby sudo service elasticsearch

Restartování služby trvá nějakou dobu. Po dokončení zkontrolujte, zda funguje pomocí jednoduchého dotazu http:

curl http: // localhost: 9200 /

Musí vrátit odpověď podobnou následující:

{
  "name": "UhEc17r",
  "cluster_name": "elasticsearch",
  "cluster_uuid": "QoNneSUhTG6EJaA4LMqDlA",
  "version": {
    "number": "5.4.0",
    "build_hash": "780f8c4",
    "build_date": "2017-04-28T17: 43: 27.229Z",
    "build_snapshot": false,
    "lucene_version": "6.5.0"
  },
  "tagline": "Víte, pro vyhledávání"
}

Doufám, že jste zde neměl žádné problémy a můžeme pokračovat.

Vytvoření nového projektu Laravel

Vytvořme novou instalaci rámce Laravel. Používáte-li Homestead, máte instalačního programu Laravel po vybalení z krabice, pokud jej nemáte, můžete jej nainstalovat pomocí Composeru.

V Homestead jsou všechny projekty umístěny v domovském adresáři Code. Přesuňte se do adresáře a vytvořte nový projekt pomocí instalačního programu Laravel:

cd ~ / Code /
laravel nový vyhledávací kurz

V tomto tutoriálu budeme používat databázi sqlite, jen aby věci zůstaly jednoduché. Přejděte na konec souboru .env a nastavte možnost DB_CONNECTION na sqlite a vypište další možnosti databáze:

DB_CONNECTION = sqlite
# DB_HOST = 127.0.0.1
# DB_PORT = 3306
# DB_DATABASE = usedlost
# DB_USERNAME = usedlost
# DB_PASSWORD = tajemství

A konečně, pro ukládání dat musíme vytvořit soubor:

dotkněte se databáze / databáze.sqlite

Skautský řidič

Už jsme mluvili o Laravel Scoutovi. Přidá fulltextové vyhledávání k vašim modelům Eloquent. Také jsem zmínil, že budeme používat vlastní motor Scout, protože z něj není žádný ovladač Elasticsearch.

Nainstalujte balíček s programem Composer:

skladatel vyžaduje babenkoivan / skaut-elasticsearch-driver

Jak jste mohli uhodnout, uděláme několik konfigurací. Nejprve přejděte do souboru config / app.php a do sekce poskytovatelů přidejte dva řetězce:

'poskytovatelé' => [
   // ...
   // samotný balíček Scout
   Laravel \ Scout \ ScoutServiceProvider :: třída,
   
   // ovladač pro Elasticsearch
   ScoutElastic \ ScoutElasticServiceProvider :: třída,
   // ...
]

Za druhé, musíme nakonfigurovat nastavení balíčků, abychom mohli nakonfigurovat Scout. Spusťte v konzole následující příkazy:

Prodejce php artisan: publish --provider = "Laravel \ Scout \ ScoutServiceProvider"
Prodejce php artisan: publish --provider = "ScoutElastic \ ScoutElasticServiceProvider"

Nakonec připojte SCOUT_DRIVER = elastický na konec souboru .env.

Konfigurátor indexu

Index Elasticsearch je jako databáze v tradičním relačním modelu, zatímco typ Elasticsearch je jako tabulka. Než budeme moci poslat jakákoli data do Elasticsearch, musíme je vytvořit obě. Začneme vytvořením indexu.

Nejprve vytvoříme speciální třídu - Index Configurator:

php artisan make: index-konfigurator TutorialIndexConfigurator

Tato třída popisuje index výukového programu. Můžeme v něm zadat buď nastavení (například analyzátory), nebo výchozí mapování. Ale pro naše účely nepotřebujeme žádná speciální nastavení, jsme s výchozími nastaveními dobří.

Nakonec vytvoříme entitu indexu v ElasticSearch podle konfigurátoru indexu:

php artisan elastic: create-index "App \ TutorialIndexConfigurator"

Prohledávatelné modely

Tento tutoriál pojednává o přidání fulltextového vyhledávání do vašich modelů Eloquent, ale zatím jsme žádné z nich nevytvořili. V této sekci vyplním mezeru a vytvořím dvě entity: knihu a autora.

Řekněme, že každá kniha má název, popis, rok vydání a autora. Autor má pouze jméno. Stanovme také následující zjednodušení: autor může napsat několik knih, ale knihu může napsat pouze jeden autor.

Můžeme vytvářet modely pomocí příkazu Artisan make:

php artisan make: searchable-Model Book --index-konfigurator = "TutorialIndexConfigurator" --migration
php artisan make: searchable-model Autor --index-konfigurator = "TutorialIndexConfigurator" --migration

Prohledávatelné modely jsou rozšířené verze obvyklých, ale s určitým předdefinovaným chováním, které jim umožňuje provádět vyhledávací dotazy v Elasticsearch.

Můžete si všimnout, že jsme také provedli migrace a určili konfigurátor indexu pro každý model. To je důležité, protože musíme jasně uvést, jaký index Elasticsearch používá pro typ Elasticsearch.

Soubory vytvořené (Book.php a Author.php) najdete ve složce aplikace projektu. Budeme je upravovat, abychom určili mapování pro každý typ Ealsticsearch. Je nezbytné nastavit mapování, protože říká Elasticsearch, jak ošetřovat pole.

Zde je úplný kód třídy Book po úpravě:

namespace App;
použijte ScoutElastic \ SearchableModel;
třída Book rozšiřuje SearchableModel
{
    // V tomto tutoriálu nechceme používat časová razítka
    public $ timestamps = false;
    protected $ indexConfigurator = TutorialIndexConfigurator :: class;
// Neanalyzujeme čísla, veškerý text je v angličtině
    chráněné $ mapování = [
        'properties' => [
            'id' => [
                'type' => 'celé číslo',
                'index' => 'not_analyzed'
            ],
            'title' => [
                'type' => 'string',
                'analyzer' => 'english'
            ],
            'description' => [
                'type' => 'string',
                'analyzer' => 'english'
            ],
            'year' => [
                'type' => 'celé číslo',
                'index' => 'not_analyzed'
            ],
            'author_id' => [
                'type' => 'celé číslo',
                'index' => 'not_analyzed'
            ]
        ]
    ];
    // Každá kniha patří jednomu autorovi
    autor veřejné funkce ()
    {
        návrat $ this-> patříTo (autor :: třída);
    }
}

Kód autorského modelu je níže:

namespace App;
použijte ScoutElastic \ SearchableModel;
třída Author rozšiřuje SearchableModel
{
   public $ timestamps = false;
   protected $ indexConfigurator = TutorialIndexConfigurator :: class;
   chráněné $ mapování = [
        'properties' => [
            'id' => [
                'type' => 'celé číslo',
                'index' => 'not_analyzed'
            ],
            'name' => [
                'type' => 'string',
                'analyzer' => 'english'
            ]
        ]
    ];
    // Každý autor může napsat několik knih
    knihy veřejných funkcí ()
    {
        return $ this-> hasMany (Book :: class);
    }
}

Migrace

Během vytváření modelu jsme také vytvořili dva migrační soubory. Najdete je ve složce databáze / migrace. Vyhledejte soubor, který končí books_table.php a popište pole tabulky takto:

použijte Illuminate \ Support \ Facades \ Schema;
použijte Illuminate \ Database \ Schema \ Blueprint;
použijte Illuminate \ Database \ Migrations \ Migration;
třída CreateBooksTable rozšiřuje migraci
{
    veřejná funkce nahoru ()
    {
        Schéma :: create ('books', function (Blueprint $ table) {
            $ table-> inkrementy ('id');
            $ table-> string ('title');
            $ table-> text ('description');
            $ table-> integer ('year');
            $ table-> integer ('author_id');
        });
    }
    veřejná funkce dolů ()
    {
        Schéma :: dropIfExists ('books');
    }
}

Nyní najděte soubor autor_table.php a popište pole tabulky autorů:

použijte Illuminate \ Support \ Facades \ Schema;
použijte Illuminate \ Database \ Schema \ Blueprint;
použijte Illuminate \ Database \ Migrations \ Migration;
třída CreateAuthorsTable rozšiřuje migraci
{
    veřejná funkce nahoru ()
    {
        Schéma :: create ('autoři', funkce (Blueprint $ table) {
            $ table-> inkrementy ('id');
            $ table-> string ('name');
        });
    }
    veřejná funkce dolů ()
    {
        Schéma :: dropIfExists ('autoři');
    }
}

Chcete-li vytvořit tabulky, spusťte příkaz migrace v konzole:

php řemeslník migrovat

Dummy Data

K hraní s vyhledávačem potřebujeme některá data. Nejjednodušší způsob, jak to získat, je vytvořit nějaká falešná data. Přidáme továrny na model knihy a model autora do souboru databáze / továrny / modelFactory.php:

$ factory-> define (App \ Author :: class, function (Faker \ Generator $ faker) {
    vrátit se [
        'name' => "{$ faker-> firstName} {$ faker-> lastName}"
    ];
});
$ factory-> define (App \ Book :: class, function (Faker \ Generator $ faker) {
    vrátit se [
        'title' => ucfirst ($ faker-> realText (15)),
        'description' => $ faker-> realText (200),
        'year' => $ faker-> year,
        'author_id' => function () {
        // Z tabulky vezmeme prvního náhodného autora
            return App \ Author :: inRandomOrder () -> first () -> id;
        }
    ];
});

Laravel má skvělou PHP konzoli nazvanou Tinker. Můžete jej spustit pomocí řemeslníka:

řemeslník držitele php

Vytvořme 50 autorů:

továrna (App \ Autor :: třída, 50) -> create ()

a 200 knih:

továrna (App \ Book :: class, 200) -> create ()

Skvělý! Nyní máme data, se kterými můžeme hrát.

Vyhledávací dotazy

Všechny přípravy jsou hotové, jsme připraveni vytvořit náš první vyhledávací dotaz. Najdeme všechny knihy, které mají slovo Alice:

// V tomto dotazu říkáme Elasticsearch - dejte nám 10 záznamů, které obsahují slovo Alice v jakémkoli poli
App \ Book :: search ('Alice') -> take (20) -> get ()

Jsem si jist, že získáte nějaké výsledky, protože metoda Faker \ Generator :: realText vrací náhodný text od Alice v knize Wonderland.

Elasticsearch jsme neřekli, která pole mají vyšší prioritu, a proto můžeme ve výsledku vidět nepořádek. Bylo by mnohem hezčí mít záznamy s Alice v názvu nahoře a záznamy s Alice v popisu dole. Ale jak toho dosáhnout? Odpověď je pravidlo vyhledávání.

Pro vaše pohodlí existuje příkaz pro vytvoření pravidla vyhledávání:

php artisan make: search-Rule BookSearchRule

Vyhledávací pravidlo je třída, která popisuje, jak bude vyhledávací dotaz proveden. Musíte implementovat pouze jednu metodu - buildQueryPayload. Tato metoda musí vrátit bool dotaz.

Chceme prozatím hledat podle názvu a popisu, takže upravme třídu BookSearchRule podle našich potřeb:

namespace App;
použijte ScoutElastic \ SearchRule;
třída BookSearchRule rozšiřuje SearchRule
{
    buildQueryPayload () veřejné funkce
    {
        $ query = $ this-> builder-> query;
        vrátit se [
            'should' => [
                [
                    'match' => [
                        'title' => [
                            'query' => $ query,
                            'boost' => 2
                        ]
                    ]
                ],
                [
                    'match' => [
                        'description' => [
                            'query' => $ query,
                            'boost' => 1
                        ]
                    ]
                ]
            ]
        ];
    }
}

Nyní řekněme modelu knihy, aby ve výchozím nastavení použil nové pravidlo vyhledávání:

// ...
třída Book rozšiřuje SearchableModel
{
    chráněné $ searchRules = [
        BookSearchRule :: třída
    ];
    / ...
}

Udělejme stejný dotaz ještě jednou:

App \ Book :: search ('Alice') -> take (20) -> get ()

Nyní můžete vidět jiný výsledek: záznamy s Alice v názvu jsou nahoře. A to je přesně to, co jsme chtěli.

Dobře, pojďme k dalšímu příkladu. Co když chceme získat všechny knihy, které byly napsány po roce 2010? Žádný problém! Můžeme to udělat takto:

// Říkáme Elasticsearch - najděte vše, co vyšlo po roce 2010
App \ Book :: search ('*') -> kde ('year', '>', 2010) -> take (10) -> get ()

Udělejme něco těžšího. Nyní chci prohledávat knihy podle jména autora. Jak to mohu udělat? Existuje několik možností. Prvním je najít autora podle jména pomocí modelu Author a potom předat ID autora klauzule where vyhledávacího dotazu. Druhým je, aby model knihy našel záznamy podle jména samotného autora.

Můžeme určit metodu toSearchableArray, abychom určili, která pole musí být indexována pomocí Elasticsearch:

// ...
třída Book rozšiřuje SearchableModel
{
    // ...
    veřejná funkce toSearchableArray ()
    {
        return array_merge (
            // Ve výchozím nastavení budou všechna pole modelu indexována
            rodič :: toSearchableArray (),
            ['author_name' => $ this-> author-> name]
        );
    }
}

Nyní musíme do mapování modelu přidat jméno_ autora:

// ...
třída Book rozšiřuje SearchableModel
{
    // ...
    chráněné $ mapování = [
        'properties' => [
            'id' => [
                'type' => 'celé číslo',
                'index' => 'not_analyzed'
            ],
            'title' => [
                'type' => 'string',
                'analyzer' => 'english'
            ],
            'description' => [
                'type' => 'string',
                'analyzer' => 'english'
            ],
            'year' => [
                'type' => 'celé číslo',
                'index' => 'not_analyzed'
            ],
            'author_id' => [
                'type' => 'celé číslo',
                'index' => 'not_analyzed'
            ],
            'author_name' => [
                'type' => 'string',
                'analyzer' => 'english'
            ]
        ]
    ];
    // ...
}

Ještě jedna věc, musíme upravit vyhledávací pravidlo:

namespace App;
použijte ScoutElastic \ SearchRule;
třída BookSearchRule rozšiřuje SearchRule
{
    buildQueryPayload () veřejné funkce
    {
        $ query = $ this-> builder-> query;
        vrátit se [
            'should' => [
                [
                    'match' => [
                        'title' => [
                            'query' => $ query,
                            'boost' => 3
                        ]
                    ]
                ],
                [
                    'match' => [
                        'author_name' => [
                            'query' => $ query,
                            'boost' => 2
                        ]
                    ]
                ],
                [
                    'match' => [
                        'description' => [
                            'query' => $ query,
                            'boost' => 1
                        ]
                    ]
                ]
            ]
        ];
    }
}

Nyní jsme připraveni hledat podle nového pole, ale nebylo indexováno: Elasticsearch o tomto poli nevěděl nic, když jsme vytvářeli falešná data. Naštěstí můžeme reindexovat data příkazem import:

php artisan scout: import "App \ Book"

Vezměte si z databáze jméno prvního autora:

App \ Author :: first () -> name

V mém případě je to Roxanne Boehm. Zkusme najít knihy Roxanne:

App \ Book :: hledat ('Roxanne Boehm') -> get ()

Funguje to!

Afterwords

Pokud chcete získat více informací o možnostech Laravel Scout, podívejte se na oficiální dokumentaci a stránku ovladače Elasticsearch na GitHub.

Pokud máte nějaké dotazy nebo potřebujete pomoc, neváhejte zanechat komentář níže.

Děkujeme všem za zpětnou vazbu! Změnil jsem instalační část Elasticsearch, abych vám poskytl kroky instalace nejnovější verze. V článku jsem také opravil některé nepřesnosti a vydal novou verzi ovladače Scout s pevným dotazem match_all.