laravel
Elasticsearch
Elasticquent

Elasticquent を使って、LaravelからElasticSearchのデータを取得する

More than 1 year has passed since last update.

Elasticquent を利用して、Laravelで検索画面を作り、
ElasticSearchのAPIを呼び出して、結果を表示する画面を作ります。

※ ElasticSearch側の設定は、こちらをご覧ください。
https://qiita.com/nonoichi123/items/1cf9c5752686c9a14c86

Elasticquentライブラリの場所

https://github.com/elasticquent/Elasticquent

上記のページを元に、インストール作業を実行する

  1. composer で ライブラリをインストール
  2. config/app.php に追記
  3. vendor:publish を行い、config/elasticquent.php を生成

次から、ファイルの編集および作成をしていきます。

config/elasticquent.php

ElasticSearch のポートはデフォルトの9200ポート
インデックス名は、「search_doc」として作成しました。

<?php

return array(

    /*
    |--------------------------------------------------------------------------
    | Custom Elasticsearch Client Configuration
    |--------------------------------------------------------------------------
    |
    | This array will be passed to the Elasticsearch client.
    | See configuration options here:
    |
    | http://www.elasticsearch.org/guide/en/elasticsearch/client/php-api/current/_configuration.html
    */

    'config' => [
        'hosts'     => ['localhost:9200'],
        'retries'   => 1,
    ],

    /*
    |--------------------------------------------------------------------------
    | Default Index Name
    |--------------------------------------------------------------------------
    |
    | This is the index name that Elasticquent will use for all
    | Elasticquent models.
    */

    'default_index' => 'search_doc',

);

app/Http/Controllers/SearchController.php

※SearchController.phpファイル 作成は、Artisanコマンドで作成
php artisan make:controller SearchController

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Document;

class SearchController extends Controller
{
    public function __construct()
    {
    }

    public function index()
    {
        return view('search');
    }

    public function result(Request $request)
    {
        $input = $request->all();

        //$ret = \App\Document::search($input['q']);

        $ret = \App\Document::complexSearch([
            'body' => [
                'size' => 100,
                'query' => [
                    'match' => [
                        'file.content' => $input['q']
                    ]
                ],
                'highlight' => [
                    'pre_tags'  => '<em style="color:#cc0000;">',
                    'post_tags' => '</em>',
                    'fields'    => [
                       'file.content' => new \stdClass()
                    ]
                ]
            ]
        ]);
        $ret = $ret->getHits();

        return view('search_result', ['ret' => $ret, 'q' => $input['q']]);
    }

    public function download($id)
    {
        $ret = \App\Document::complexSearch([
            'body' => [
                'query' => [
                  'match' => [
                        '_id' => $id
                    ]
                ]
            ]
        ]);
        $ret = $ret->getHits();

        $tmpdir = '../storage/tmp/';
        $data =  base64_decode($ret['hits'][0]['_source']['file']);
        file_put_contents($tmpdir.$ret['hits'][0]['_source']['title'], $data);

        return response()->download($tmpdir.$ret['hits'][0]['_source']['title']);
    }
}

Document.php

App 配下に、Document.phpファイルを作成し、以下を記載

※Document.phpファイル 作成は、Artisanコマンドで作成
  php artisan make:model Document

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Elasticquent\ElasticquentTrait;

class Document extends Model
{
    use ElasticquentTrait;
}

routes/web.php

routes/web.php に以下を追記

Route::get('/search', 'SearchController@index')->name('search');
Route::get('/search/result/download/{id}', 'SearchController@download')->name('search_result_download');
Route::get('/search/result', 'SearchController@result')->name('search_result');

views/search.blade.php

検索画面と検索結果画面の2テンプレートを作成

<form action="/search/result" method="get">
    {!! csrf_field() !!}

    <div class="form-group has-feedback {{ $errors->has('keyword') ? 'has-error' : '' }}">

    </div>
    <div class="form-group has-feedback {{ $errors->has('q') ? 'has-error' : '' }}">
        <input type="text" name="q" class="form-control" value="{{ old('q') }}"
               placeholder="{{ trans('please input keyword...') }}">
        <span class="glyphicon form-control-feedback fa fa-search"></span>
        @if ($errors->has('keyword'))
            <span class="help-block">
                <strong>{{ $errors->first('keyword') }}</strong>
            </span>
        @endif
    </div>
    <button type="submit"
            class="btn btn-primary btn-block btn-flat">
    {{ trans('search') }}</button>
</form>

views/search_result.blade.php

<section class="content">
  <div class="row">
    <div class="col-xs-12">
      <div class="box">
        <div class="box-header">
          <h3 class="box-title">Search Result</h3>

          <div class="box-tools">
            <form action="/search/result" method="get">
            <div class="input-group input-group-sm" style="width: 150px;">

                <input type="text" name="q" class="form-control" value="{{ $q }}"
                   placeholder="{{ trans('search...') }}">

                <div class="input-group-btn">
                  <button type="submit" class="btn btn-default"><i class="fa fa-search"></i></button>
                </div>

            </div>
            </form>
          </div>
        </div>
        <!-- /.box-header -->
        <div class="box-body table-responsive no-padding">
          <table class="table table-hover">
            <tbody><tr>
              <!--th>Thumbnail Image</th>-->
              <th>Title</th>
              <!--<th>Date</th>
              <th>ContentType</th>-->
              <th>Highlight</th>
              <th>Download</th>
            </tr>
            @foreach($ret['hits'] as $r)
            <tr>
              <!--<td>
                <a href="https://themequarry.com/theme/ample-admin-the-ultimate-dashboard-template-ASFEDA95" class="ad-click-event">
                  <img src="https://themequarry.com/storage/images/approved/ASFEDA95_v1.0_58db8909df34d.png" alt="Ample Admin" class="media-object" style="width: 150px;height: auto;border-radius: 4px;box-shadow: 0 1px 3px rgba(0,0,0,.15);">
                </a>
              </td>-->
              <td>{{ $r['_source']['title'] }}</td>
              <!--<td>11-7-2014</td>
              <td><span class="label label-success">Approved</span></td>-->
              <td>{!! nl2br($r['highlight']['file.content'][0]) !!}</td>
              <td><a href="/search/result/download/{{ ($r['_id']) }}">Download</a></td>
            </tr>
            @endforeach
          </tbody></table>
        </div>
        <!-- /.box-body -->
      </div>
      <!-- /.box -->
    </div>
  </div>
</section>

表示確認

こんな感じでファイルを作ると、
以下の感じで動作しました。

検索画面(/search)にアクセス

スクリーンショット 2017-10-11 15.42.07.png

検索画面で「talend」と入力して検索した結果

ファイル名、ファイルの中身の一致箇所をハイライト表示、ダウンロードリンクを
表形式で表示

スクリーンショット 2017-10-11 15.42.19.png

ダウンロードボタンをクリックすると、ファイルがダウンロードされる

スクリーンショット 2017-10-11 15.42.42.png

ファイルの中身検索を無償のツールで実現したく、
色々と考えて、ElasticSearchが良さそうだという結論になり、触ってきましたが、
最近、AlfrescoというオープンソースCMSを知りました。

このCMSには、ApacheSolr が搭載されており、
ワークフローやCMS機能など様々な機能が搭載されており、
品質も良いようで、
興味がAlfrescoに移ってきているこの頃です。。