Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 78
0.00% covered (danger)
0.00%
0 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
ComplexSearchService
0.00% covered (danger)
0.00%
0 / 78
0.00% covered (danger)
0.00%
0 / 10
870
0.00% covered (danger)
0.00%
0 / 1
 getZendServiceManager
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
6
 getOperator
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 setModel
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getGateway
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 query
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 searchType
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
12
 setLanguage
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 parseValue
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
30
 validValue
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 getQuery
0.00% covered (danger)
0.00%
0 / 29
0.00% covered (danger)
0.00%
0 / 1
56
1<?php
2
3/**
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; under version 2
7 * of the License (non-upgradable).
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
17 *
18 * Copyright (c) 2015 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT);
19 *
20 * @author Christophe GARCIA <christopheg@taotesting.com>
21 * @license GPLv2
22 * @package generis
23 *
24 */
25
26namespace   oat\generis\model\kernel\persistence\smoothsql\search;
27
28use core_kernel_persistence_smoothsql_SmoothModel;
29use oat\generis\model\data\Model;
30use oat\generis\model\kernel\persistence\smoothsql\search\filter\FilterFactory;
31use oat\generis\model\OntologyAwareTrait;
32use oat\oatbox\service\ConfigurableService;
33use oat\search\base\QueryBuilderInterface;
34use oat\search\base\QueryInterface;
35use oat\search\base\SearchGateWayInterface;
36use Zend\ServiceManager\Config;
37use Zend\ServiceManager\ServiceManager;
38
39/**
40 * Complexe search service
41 *
42 * @author Christophe GARCIA <christopheg@taotesting.com>
43 */
44class ComplexSearchService extends ConfigurableService
45{
46    use OntologyAwareTrait;
47
48    public const SERVICE_ID = 'generis/complexSearch';
49
50    public const SERVICE_SEARCH_ID = 'search.tao.gateway';
51
52    /**
53     * internal service locator
54     * @var \Zend\ServiceManager\ServiceLocatorInterface
55     */
56    protected $services;
57    /**
58     * search gateway
59     * @var SearchGateWayInterface
60     */
61    protected $gateway;
62
63    /**
64     * @var Model
65     */
66    protected $model;
67
68    /**
69     * Returns the internal service manager
70     * @return \Zend\ServiceManager\ServiceLocatorInterface
71     */
72    protected function getZendServiceManager()
73    {
74        if (is_null($this->services)) {
75            $options = $this->getOptions();
76            $model = $this->model ?? $this->getModel();
77            $ontologyOptions = $model->getOptions();
78            $options['services']['search.options']['model'] = $model;
79            $options['services']['search.options']['persistence'] = $ontologyOptions['persistence'] ?? null;
80            $config         = new Config($options);
81            $this->services =  new ServiceManager($config);
82        }
83        return $this->services;
84    }
85
86    /**
87     * determine which operator may be used
88     * @param boolean $like
89     * @return string
90     */
91    protected function getOperator($like)
92    {
93        $operator = 'equals';
94
95        if ($like) {
96            $operator = 'contains';
97        }
98
99        return $operator;
100    }
101
102    /**
103     * Set the model the search should apply to
104     * @param Model $model
105     */
106    public function setModel(Model $model)
107    {
108        $this->model = $model;
109    }
110
111    /**
112     * return search gateway
113     * @return SearchGateWayInterface
114     */
115    public function getGateway()
116    {
117        if (is_null($this->gateway)) {
118            $this->gateway = $this->getZendServiceManager()->get(self::SERVICE_SEARCH_ID)
119                ->setServiceLocator($this->getZendServiceManager())
120                ->init();
121        }
122        return $this->gateway;
123    }
124    /**
125     * return a new query builder
126     * @return \oat\search\QueryBuilder
127     */
128    public function query()
129    {
130        return $this->getGateway()->query();
131    }
132
133    /**
134     * return a preset query builder with types
135     * @param QueryBuilderInterface $query
136     * @param string $class_uri
137     * @param boolean $recursive
138     * @return QueryInterface
139     * @throws \common_exception_Error
140     */
141    public function searchType(QueryBuilderInterface $query, $class_uri, $recursive = false)
142    {
143
144        $Class    = new \core_kernel_classes_Class($class_uri);
145        $rdftypes = [];
146
147        if ($recursive === true) {
148            foreach ($Class->getSubClasses(true) as $subClass) {
149                $rdftypes[] = $subClass->getUri();
150            }
151        }
152
153        $rdftypes[] = $class_uri;
154
155        $criteria = $query->newQuery()
156                ->add('http://www.w3.org/1999/02/22-rdf-syntax-ns#type')
157                ->in($rdftypes);
158
159        return $criteria;
160    }
161
162    /**
163     * set gateway language options
164     * @param QueryBuilderInterface $query
165     * @param string $userLanguage
166     * @param string $defaultLanguage
167     * @return $this
168     */
169    public function setLanguage(QueryBuilderInterface $query, $userLanguage = '', $defaultLanguage = null)
170    {
171        if (is_null($defaultLanguage)) {
172            $defaultLanguage = DEFAULT_LANG;
173        }
174        $options = $this->getGateway()->getOptions();
175        if (!empty($userLanguage)) {
176            $options['language'] = $userLanguage;
177        }
178        $options['defaultLanguage'] = $defaultLanguage;
179
180        $this->getGateway()->setOptions($options);
181
182        return $query->newQuery();
183    }
184
185    protected function parseValue($rawValue)
186    {
187        $result = [];
188        if (!is_array($rawValue)) {
189            $rawValue = [$rawValue];
190        }
191        foreach ($rawValue as $value) {
192            if ($value instanceof \core_kernel_classes_Resource) {
193                $result[] = $value->getUri();
194            } else {
195                $result[] = preg_replace('/^\*$/', '', $value);
196            }
197        }
198        return count($result) === 1 ? $result[0] : $result;
199    }
200
201    /**
202     * verify if value is valid
203     * @param string $value
204     * @return boolean
205     * @throws exception\InvalidValueException
206     */
207    protected function validValue($value)
208    {
209        if (is_array($value)) {
210            if (empty($value)) {
211                throw new exception\InvalidValueException('query filter value cann\'t be empty ');
212            }
213        }
214    }
215
216    /**
217     * serialyse a query for searchInstance
218     * use for legacy search
219     * @param core_kernel_persistence_smoothsql_SmoothModel $model
220     * @param array $classUri
221     * @param array $propertyFilters
222     * @param boolean $and
223     * @param boolean $isLikeOperator
224     * @param string $lang
225     * @param integer $offset
226     * @param integer $limit
227     * @param string $order
228     * @param string $orderDir
229     * @return string
230     */
231    public function getQuery(
232        core_kernel_persistence_smoothsql_SmoothModel $model,
233        $classUri,
234        array $propertyFilters,
235        $and = true,
236        $isLikeOperator = true,
237        $lang = '',
238        $offset = 0,
239        $limit = 0,
240        $order = '',
241        $orderDir = 'ASC',
242        $onlyClass = false
243    ) {
244        $query = $this->getGateway()->query()->setLimit($limit)->setOffset($offset);
245
246        if (!empty($order)) {
247            $query->sort([$order => strtolower($orderDir)]);
248        }
249
250        $this->setLanguage($query, $lang);
251
252        if ($onlyClass === true) {
253            $criteria = $query->newQuery()
254                ->add('http://www.w3.org/2000/01/rdf-schema#subClassOf')
255                ->in($classUri);
256        } else {
257            $criteria = $query->newQuery()
258                ->add('http://www.w3.org/1999/02/22-rdf-syntax-ns#type')
259                ->in($classUri);
260        }
261
262        $query->setCriteria($criteria);
263        $count     = 0;
264        $propertyFilters = FilterFactory::buildFilters($propertyFilters, $isLikeOperator);
265        $maxLength = count($propertyFilters);
266        foreach ($propertyFilters as $filter) {
267            $this->validValue($filter->getValue());
268
269            $criteria->addCriterion($filter->getKey(), $filter->getOperator(), $this->parseValue($filter->getValue()));
270
271            $orValues = $filter->getOrConditionValues();
272
273            foreach ($orValues as $val) {
274                $criteria->addOr($this->parseValue($val));
275            }
276
277            $count++;
278            if ($and === false && $maxLength > $count) {
279                $criteria = $query->newQuery()
280                    ->add('http://www.w3.org/1999/02/22-rdf-syntax-ns#type')
281                    ->in($classUri);
282                $query->setOr($criteria);
283            }
284        }
285
286        $queryString = $this->getGateway()->serialyse($query)->getQuery();
287
288        return $queryString;
289    }
290}