Completed
Pull Request — master (#128)
by
unknown
01:37
created

TNTSearchEngine::mapIds()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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