Completed
Push — master ( 1af24b...a0fb30 )
by Nenad
06:43
created

TNTSearchEngine::performSearch()   B

Complexity

Conditions 5
Paths 16

Size

Total Lines 20
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 13
nc 16
nop 2
1
<?php
2
3
namespace TeamTNT\Scout\Engines;
4
5
use Illuminate\Database\Eloquent\Collection;
6
use Laravel\Scout\Builder;
7
use Laravel\Scout\Engines\Engine;
8
use TeamTNT\TNTSearch\TNTSearch;
9
use TeamTNT\TNTSearch\Exceptions\IndexNotFoundException;
10
11
class TNTSearchEngine extends Engine
12
{
13
    /**
14
     * @var TNTSearch
15
     */
16
    protected $tnt;
17
18
    /**
19
     * @var Builder
20
     */
21
    protected $builder;
22
23
    /**
24
     * Create a new engine instance.
25
     *
26
     * @param TNTSearch $tnt
27
     */
28
    public function __construct(TNTSearch $tnt)
29
    {
30
        $this->tnt = $tnt;
31
    }
32
33
    /**
34
     * Update the given model in the index.
35
     *
36
     * @param Collection $models
37
     *
38
     * @return void
39
     */
40
    public function update($models)
41
    {
42
        $this->initIndex($models->first());
43
        $this->tnt->selectIndex("{$models->first()->searchableAs()}.index");
44
        $index = $this->tnt->getIndex();
45
        $index->setPrimaryKey($models->first()->getKeyName());
46
47
        $index->indexBeginTransaction();
48
        $models->each(function ($model) use ($index) {
49
            if ($model->getKey()) {
50
                $index->update($model->getKey(), $model->toSearchableArray());
51
            } else {
52
                $index->insert($model->toSearchableArray());
53
            }
54
        });
55
        $index->indexEndTransaction();
56
    }
57
58
    /**
59
     * Remove the given model from the index.
60
     *
61
     * @param Collection $models
62
     *
63
     * @return void
64
     */
65
    public function delete($models)
66
    {
67
        $this->initIndex($models->first());
68
        $models->each(function ($model) {
69
            $this->tnt->selectIndex("{$model->searchableAs()}.index");
70
            $index = $this->tnt->getIndex();
71
            $index->setPrimaryKey($model->getKeyName());
72
            $index->delete($model->getKey());
73
        });
74
    }
75
76
    /**
77
     * Perform the given search on the engine.
78
     *
79
     * @param Builder $builder
80
     *
81
     * @return mixed
82
     */
83
    public function search(Builder $builder)
84
    {
85
        try {
86
            return $this->performSearch($builder);
87
        } catch (IndexNotFoundException $e) {
88
            $this->initIndex($builder->model);
89
        }
90
    }
91
92
    /**
93
     * Perform the given search on the engine.
94
     *
95
     * @param Builder $builder
96
     * @param int     $perPage
97
     * @param int     $page
98
     *
99
     * @return mixed
100
     */
101
    public function paginate(Builder $builder, $perPage, $page)
102
    {
103
        $results = $this->performSearch($builder);
104
105
        if ($builder->limit) {
106
            $results['hits'] = $builder->limit;
107
        }
108
109
        $chunks = array_chunk($results['ids'], $perPage);
110
111
        if (!empty($chunks)) {
112
            if (array_key_exists($page - 1, $chunks)) {
113
                $results['ids'] = $chunks[$page - 1];
114
            } else {
115
                $results['ids'] = end($chunks);
116
            }
117
        }
118
119
        return $results;
120
    }
121
122
    /**
123
     * Perform the given search on the engine.
124
     *
125
     * @param Builder $builder
126
     *
127
     * @return mixed
128
     */
129
    protected function performSearch(Builder $builder, array $options = [])
130
    {
131
        $index = $builder->index ?: $builder->model->searchableAs();
132
        $limit = $builder->limit ?: 10000;
133
        $this->tnt->selectIndex("{$index}.index");
134
135
        $this->builder = $builder;
136
        $this->tnt->asYouType = $builder->model->asYouType ?: false;
137
138
        if ($builder->callback) {
139
            return call_user_func(
140
                $builder->callback,
141
                $this->tnt,
142
                $builder->query,
143
                $options
144
            );
145
        }
146
147
        return $this->tnt->search($builder->query, $limit);
148
    }
149
150
    /**
151
     * Get the filter array for the query.
152
     *
153
     * @param Builder $builder
154
     *
155
     * @return array
156
     */
157
    protected function filters(Builder $builder)
158
    {
159
        return collect($builder->wheres)->map(function ($value, $key) {
160
            return $key.'='.$value;
161
        })->values()->all();
162
    }
163
164
    /**
165
     * Map the given results to instances of the given model.
166
     *
167
     * @param mixed                               $results
168
     * @param \Illuminate\Database\Eloquent\Model $model
169
     *
170
     * @return Collection
171
     */
172
    public function map($results, $model)
173
    {
174
        if (count($results['ids']) === 0) {
175
            return Collection::make();
176
        }
177
178
        $keys   = collect($results['ids'])->values()->all();
179
        $fieldsWheres = array_keys($this->builder->wheres);
180
        $models = $model->whereIn(
181
            $model->getQualifiedKeyName(), $keys
182
        )->get()->keyBy($model->getKeyName());
183
184
        return collect($results['ids'])->map(function ($hit) use ($models) {
185
            return $models->has($hit) ? $models[$hit] : null;
186
        })->filter(function ($model) use ($fieldsWheres) {
187
            return !is_null($model) && array_reduce($fieldsWheres, function ($carry, $item) use($model) {
188
                    return $carry && $model[$item] == $this->builder->wheres[$item];
189
                }, true);;
190
        });
191
    }
192
193
    /**
194
     * Pluck and return the primary keys of the given results.
195
     *
196
     * @param mixed $results
197
     * @return \Illuminate\Support\Collection
198
     */
199
    public function mapIds($results)
200
    {
201
        return collect($results['ids'])->values();
202
    }
203
204
    /**
205
     * Get the total count from a raw result returned by the engine.
206
     *
207
     * @param mixed $results
208
     *
209
     * @return int
210
     */
211
    public function getTotalCount($results)
212
    {
213
        return $results['hits'];
214
    }
215
216
    public function initIndex($model)
217
    {
218
        $indexName = $model->searchableAs();
219
220
        if (!file_exists($this->tnt->config['storage']."/{$indexName}.index")) {
221
            $indexer = $this->tnt->createIndex("$indexName.index");
222
            $indexer->setDatabaseHandle($model->getConnection()->getPdo());
223
            $indexer->setPrimaryKey($model->getKeyName());
224
        }
225
    }
226
}