ElasticSearchEngine::mapIds()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
rs 10
1
<?php
2
/**
3
 * Created by PhpStorm.
4
 * User: arun
5
 * Date: 2019-07-07
6
 * Time: 12:59
7
 */
8
9
namespace ArunFung\ScoutElasticSearch;
10
11
use Laravel\Scout\Builder;
12
use Laravel\Scout\Engines\Engine;
13
use Elasticsearch\Client as ElasticSearch;
14
use Illuminate\Database\Eloquent\Model;
15
use Illuminate\Database\Eloquent\Collection;
16
17
/**
18
 * Class ElasticSearchEngine
19
 * @package ArunFung\ScoutElasticSearch
20
 */
21
class ElasticSearchEngine extends Engine
22
{
23
    /**
24
     * ElasticSearch instance
25
     *
26
     * @var ElasticSearch
27
     */
28
    protected $elasticSearch;
29
30
    /**
31
     * @var ElasticSearch Index
32
     */
33
    protected $index;
34
35
    /**
36
     * Create a new engine instance.
37
     *
38
     * @param ElasticSearch $elasticSearch
39
     * @param  $index
40
     * @return void
41
     */
42
    public function __construct(ElasticSearch $elasticSearch, $index)
43
    {
44
        $this->elasticSearch = $elasticSearch;
45
        $this->index = $index;
46
    }
47
48
    /**
49
     * Update the given model in the index.
50
     *
51
     * @param Collection $models
52
     * @return void
53
     */
54
    public function update($models)
55
    {
56
        $params = [];
57
        $models->each(function ($model) use (&$params) {
58
            $params['body'][] = [
59
                'index' => [
60
                    '_index' => $this->index,
61
                    '_type' => $model->searchableAs(),
62
                    '_id' => $model->getScoutKey()
63
                ]
64
            ];
65
            $params['body'][] = $model->toSearchableArray();
66
        });
67
68
        $this->elasticSearch->bulk($params);
69
    }
70
71
    /**
72
     * Remove the given model from the index.
73
     *
74
     * @param Collection $models
75
     * @return void
76
     */
77
    public function delete($models)
78
    {
79
        $params = [];
80
        $models->each(function ($model) use (&$params) {
81
            $params['body'][] = [
82
                'delete' => [
83
                    '_index' => $this->index,
84
                    '_type' => $model->searchableAs(),
85
                    '_id' => $model->getScoutKey()
86
                ]
87
            ];
88
        });
89
        $this->elasticSearch->bulk($params);
90
    }
91
92
    /**
93
     * Perform the given search on the engine.
94
     *
95
     * @param Builder $builder
96
     * @return mixed
97
     */
98
    public function search(Builder $builder)
99
    {
100
        return $this->performSearch($builder, array_filter([
101
            'numericFilters' => $this->filters($builder),
102
            'size' => $builder->limit,
103
        ]));
104
    }
105
106
    /**
107
     * Perform the given search on the engine.
108
     *
109
     * @param Builder $builder
110
     * @param int $perPage
111
     * @param int $page
112
     * @return mixed
113
     */
114
    public function paginate(Builder $builder, $perPage, $page)
115
    {
116
        $options = [
117
            'numericFilters' => $this->filters($builder),
118
            'from' => (($page - 1) * $perPage),
119
            'size' => $perPage,
120
        ];
121
122
        return $this->performSearch($builder, $options);
123
    }
124
125
    /**
126
     * Perform the given search on the engine.
127
     *
128
     * @param Builder $builder
129
     * @param array $options
130
     * @return mixed
131
     */
132
    protected function performSearch(Builder $builder, array $options = [])
133
    {
134
        $params = [
135
            'index' => $this->index,
136
            'type' => $builder->model->searchableAs(),
137
        ];
138
139
        $must = [
140
            [
141
                'query_string' => ['query' => "{$builder->query}"]
142
            ]
143
        ];
144
145
        if (isset($options['numericFilters']) && count($options['numericFilters'])) {
146
            $must = array_merge($must, $options['numericFilters']);
147
        }
148
149
        $body = [
150
            'query' => [
151
                'bool' => [
152
                    'must' => $must
153
                ]
154
            ]
155
        ];
156
157
        if (isset($options['from'])) {
158
            $body['from'] = $options['from'];
159
        }
160
161
        if (isset($options['size'])) {
162
            $body['size'] = $options['size'];
163
        }
164
165
        $params['body'] = $body;
166
167
        if ($builder->callback) {
168
            return call_user_func(
169
                $builder->callback,
170
                $this->elasticSearch,
171
                $builder->query,
172
                $params
173
            );
174
        }
175
176
        return $this->elasticSearch->search($params);
177
    }
178
179
    /**
180
     * Get the filter array for the query.
181
     *
182
     * @param Builder $builder
183
     * @return array
184
     */
185
    protected function filters(Builder $builder)
186
    {
187
        return collect($builder->wheres)->map(function ($value, $key) {
188
            if (is_array($value)) {
189
                return ['terms' => [$key => $value]];
190
            }
191
            return ['match_phrase' => [$key => $value]];
192
        })->values()->all();
193
    }
194
195
    /**
196
     * Pluck and return the primary keys of the given results.
197
     *
198
     * @param mixed $results
199
     * @return \Illuminate\Support\Collection
200
     */
201
    public function mapIds($results)
202
    {
203
        return collect($results['hits']['hits'])->pluck('_id');
204
    }
205
206
    /**
207
     * Map the given results to instances of the given model.
208
     *
209
     * @param Builder $builder
210
     * @param mixed $results
211
     * @param Model $model
212
     * @return Collection
213
     */
214
    public function map(Builder $builder, $results, $model)
215
    {
216
        if ($this->getTotalCount($results) == 0) {
217
            return Collection::make();
218
        }
219
220
        $ids = $this->mapIds($results)->all();
221
        $objectIdPositions = array_flip($ids);
222
223
        /**
224
         * @var Collection $models
225
         */
226
        $models = $model->getScoutModelsByIds($builder, $ids);
227
228
        return $models->filter(function ($model) use ($ids) {
229
            return in_array($model->getScoutKey(), $ids);
230
        })->sortBy(function ($model) use ($objectIdPositions) {
231
            return $objectIdPositions[$model->getScoutKey()];
232
        })->values();
233
    }
234
235
    /**
236
     * Get the total count from a raw result returned by the engine.
237
     *
238
     * @param mixed $results
239
     * @return int
240
     */
241
    public function getTotalCount($results)
242
    {
243
        return $results['hits']['total']['value'];
244
    }
245
246
    /**
247
     * Flush all of the model's records from the engine.
248
     *
249
     * @param Model $model
250
     * @return void
251
     */
252
    public function flush($model)
253
    {
254
        $model->newQuery()->orderBy($model->getKeyName())->unsearchable();
255
    }
256
}
257