Passed
Branch master (249862)
by Adam
07:51
created

SearchBuilder   A

Complexity

Total Complexity 27

Size/Duplication

Total Lines 227
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 227
rs 10
c 0
b 0
f 0
wmc 27

15 Methods

Rating   Name   Duplication   Size   Complexity  
A boostLocation() 0 3 1
A __construct() 0 7 1
A getSort() 0 3 1
A addSalaryFilter() 0 4 1
A setupAggregations() 0 6 1
A addFirmFilter() 0 3 1
B setPreferences() 0 17 5
A setLanguages() 0 3 1
A setSessionId() 0 3 1
B build() 0 43 6
A setupScoreFunctions() 0 7 1
A addRemoteFilter() 0 10 3
A addUserFilter() 0 3 1
A setSort() 0 3 2
A setupFilters() 0 5 1
1
<?php
2
3
namespace Coyote\Services\Elasticsearch\Builders\Job;
4
5
use Coyote\Services\Elasticsearch\Aggs;
6
use Coyote\Services\Elasticsearch\Functions\Decay;
7
use Coyote\Services\Elasticsearch\Functions\FieldValueFactor;
8
use Coyote\Services\Elasticsearch\Functions\Random;
9
use Coyote\Services\Elasticsearch\Functions\ScriptScore;
10
use Coyote\Services\Elasticsearch\MatchAll;
11
use Coyote\Services\Elasticsearch\MultiMatch;
12
use Coyote\Services\Elasticsearch\QueryBuilder;
13
use Coyote\Services\Elasticsearch\Filters;
14
use Coyote\Services\Elasticsearch\Sort;
15
use Coyote\Services\Geocoder\Location;
16
use Illuminate\Http\Request;
17
18
class SearchBuilder extends QueryBuilder
19
{
20
    const PER_PAGE = 15;
21
    const DEFAULT_SORT = 'boost_at';
22
23
    /**
24
     * @var Filters\Job\City
25
     */
26
    public $city;
27
28
    /**
29
     * @var Filters\Job\Location
30
     */
31
    public $location;
32
33
    /**
34
     * @var Filters\Job\Tag
35
     */
36
    public $tag;
37
38
    /**
39
     * @var string|null
40
     */
41
    protected $sessionId = null;
42
43
    /**
44
     * @var array
45
     */
46
    protected $languages = [];
47
48
    /**
49
     * @var Request
50
     */
51
    protected $request;
52
53
    /**
54
     * @var string
55
     */
56
    protected $sort;
57
58
    /**
59
     * @param Request $request
60
     */
61
    public function __construct(Request $request)
62
    {
63
        $this->request = $request;
64
65
        $this->city = new Filters\Job\City();
66
        $this->tag = new Filters\Job\Tag();
67
        $this->location = new Filters\Job\Location();
68
    }
69
70
    /**
71
     * @param string $sessionId
72
     */
73
    public function setSessionId(string $sessionId)
74
    {
75
        $this->sessionId = $sessionId;
76
    }
77
78
    /**
79
     * @param array $languages
80
     */
81
    public function setLanguages(array $languages)
82
    {
83
        $this->languages = $languages;
84
    }
85
86
    /**
87
     * @param \Coyote\Job\Preferences $preferences
88
     */
89
    public function setPreferences($preferences)
90
    {
91
        if (!empty($preferences->locations)) {
92
            $this->should(new Filters\Job\Location($preferences->locations));
93
        }
94
95
        if (!empty($preferences->tags)) {
96
            $this->should(new Filters\Job\Tag($preferences->tags));
97
        }
98
99
        if (!empty($preferences->is_remote)) {
100
            $this->should(new Filters\Job\Remote());
101
        }
102
103
        if (!empty($preferences->salary)) {
0 ignored issues
show
Bug Best Practice introduced by
The property salary does not exist on Coyote\Job\Preferences. Since you implemented __get, consider adding a @property annotation.
Loading history...
104
            $this->should(new Filters\Range('salary', ['gte' => $preferences->salary]));
105
            $this->should(new Filters\Job\Currency($preferences->currency_id));
106
        }
107
    }
108
109
    /**
110
     * @param string $sort
111
     */
112
    public function setSort($sort)
113
    {
114
        $this->sort = in_array($sort, ['boost_at', '_score', 'salary']) ? $sort : self::DEFAULT_SORT;
115
    }
116
117
    /**
118
     * @return string
119
     */
120
    public function getSort()
121
    {
122
        return $this->sort;
123
    }
124
125
    /**
126
     * @param Location|null $location
127
     */
128
    public function boostLocation(Location $location = null)
129
    {
130
        $this->should(new Filters\Job\LocationScore($location));
131
    }
132
133
    /**
134
     * Apply remote job filter
135
     */
136
    public function addRemoteFilter()
137
    {
138
        // @see https://github.com/adam-boduch/coyote/issues/374
139
        // jezeli szukamy ofert pracy zdalnej ORAZ z danego miasta, stosujemy operator OR zamiast AND
140
        $method = count($this->city->getCities()) ? 'should' : 'must';
141
142
        $this->$method(new Filters\Job\Remote());
143
144
        if ($this->request->has('remote_range')) {
145
            $this->$method(new Filters\Job\RemoteRange());
146
        }
147
    }
148
149
    /**
150
     * @param int $salary
151
     * @param int $currencyId
152
     */
153
    public function addSalaryFilter($salary, $currencyId)
154
    {
155
        $this->must(new Filters\Range('salary', ['gte' => $salary]));
156
        $this->must(new Filters\Job\Currency($currencyId));
157
    }
158
159
    /**
160
     * @param string $name
161
     */
162
    public function addFirmFilter($name)
163
    {
164
        $this->must(new Filters\Job\Firm($name));
165
    }
166
167
    /**
168
     * @param int $userId
169
     */
170
    public function addUserFilter($userId)
171
    {
172
        $this->must(new Filters\Term('user_id', $userId));
173
    }
174
175
    /**
176
     * @return array
177
     */
178
    public function build()
179
    {
180
        if ($this->request->has('q')) {
181
            $this->must(
182
                new MultiMatch(
183
                    $this->request->get('q'),
184
                    ['title^3', 'description', 'requirements', 'recruitment', 'tags^2', 'firm.name']
185
                )
186
            );
187
        } else {
188
            // no keywords were provided -- let's calculate score based on score functions
189
            $this->setupScoreFunctions();
190
            $this->must(new MatchAll());
191
        }
192
193
        if ($this->request->has('city')) {
194
            $this->city->addCity($this->request->get('city'));
195
        }
196
197
        if ($this->request->has('tag')) {
198
            $this->tag->addTag($this->request->get('tag'));
199
        }
200
201
        if ($this->request->has('salary')) {
202
            $this->addSalaryFilter($this->request->get('salary'), $this->request->get('currency'));
203
        }
204
205
        if ($this->request->has('remote')) {
206
            $this->addRemoteFilter();
207
        }
208
209
        $this->score(new Random($this->sessionId, 2));
210
        $this->score(new ScriptScore('_score'));
211
        $this->sort(new Sort($this->sort, 'desc'));
212
213
        $this->setupFilters();
214
215
        // facet search
216
        $this->setupAggregations();
217
218
        $this->size(self::PER_PAGE * (max(0, (int) $this->request->get('page', 1) - 1)), self::PER_PAGE);
219
220
        return parent::build();
221
    }
222
223
    protected function setupFilters()
224
    {
225
        $this->must($this->city);
226
        $this->must($this->tag);
227
        $this->must($this->location);
228
    }
229
230
    protected function setupScoreFunctions()
231
    {
232
        // wazniejsze sa te ofery, ktorych pole score jest wyzsze. obliczamy to za pomoca wzoru: log(score * 1)
233
        $this->score(new FieldValueFactor('score', 'log', 1));
234
        // strsze ogloszenia traca na waznosci, glownie po 14d. z kazdym dniem score bedzie malalo o 1/10
235
        // za wyjatkiem pierwszych 2h publikacji
236
        $this->score(new Decay('boost_at', '14d', 0.1, '2h'));
0 ignored issues
show
Bug introduced by
'2h' of type string is incompatible with the type null|integer|double expected by parameter $offset of Coyote\Services\Elastics...ns\Decay::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

236
        $this->score(new Decay('boost_at', '14d', 0.1, /** @scrutinizer ignore-type */ '2h'));
Loading history...
237
    }
238
239
    protected function setupAggregations()
240
    {
241
        $this->aggs(new Aggs\Job\Location());
242
        $this->aggs(new Aggs\Job\Remote());
243
        $this->aggs(new Aggs\Job\Tag($this->languages));
244
        $this->aggs(new Aggs\Job\TopSpot());
245
    }
246
}
247