1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace SMW\SPARQLStore\QueryEngine; |
4
|
|
|
|
5
|
|
|
use RuntimeException; |
6
|
|
|
use SMW\Utils\CircularReferenceGuard; |
7
|
|
|
use SMW\DataTypeRegistry; |
8
|
|
|
use SMW\DIProperty; |
9
|
|
|
use SMW\DIWikiPage; |
10
|
|
|
use SMW\Message; |
11
|
|
|
use SMW\PropertyHierarchyLookup; |
12
|
|
|
use SMW\Query\Language\Description; |
13
|
|
|
use SMW\Query\Language\SomeProperty; |
14
|
|
|
use SMW\Query\Language\ThingDescription; |
15
|
|
|
use SMW\SPARQLStore\HierarchyFinder; |
16
|
|
|
use SMW\SPARQLStore\QueryEngine\Condition\Condition; |
17
|
|
|
use SMW\SPARQLStore\QueryEngine\Condition\SingletonCondition; |
18
|
|
|
use SMW\SPARQLStore\QueryEngine\Condition\TrueCondition; |
19
|
|
|
use SMWDataItem as DataItem; |
20
|
|
|
use SMWExpElement as ExpElement; |
21
|
|
|
use SMWExpNsResource as ExpNsResource; |
22
|
|
|
use SMWExporter as Exporter; |
23
|
|
|
use SMWTurtleSerializer as TurtleSerializer; |
24
|
|
|
use SMW\Query\DescriptionFactory; |
25
|
|
|
use SMW\DataValues\PropertyChainValue; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Build an internal representation for a SPARQL condition from individual query |
29
|
|
|
* descriptions |
30
|
|
|
* |
31
|
|
|
* @license GNU GPL v2+ |
32
|
|
|
* @since 2.0 |
33
|
|
|
* |
34
|
|
|
* @author Markus Krötzsch |
35
|
|
|
* @author mwjames |
36
|
|
|
*/ |
37
|
|
|
class CompoundConditionBuilder { |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* @var EngineOptions |
41
|
|
|
*/ |
42
|
|
|
private $engineOptions; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* @var DispatchingDescriptionInterpreter |
46
|
|
|
*/ |
47
|
|
|
private $dispatchingDescriptionInterpreter; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* @var CircularReferenceGuard |
51
|
|
|
*/ |
52
|
|
|
private $circularReferenceGuard; |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* @var PropertyHierarchyLookup |
56
|
|
|
*/ |
57
|
|
|
private $propertyHierarchyLookup; |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* @var DescriptionFactory |
61
|
|
|
*/ |
62
|
|
|
private $descriptionFactory; |
63
|
|
|
|
64
|
|
|
/** |
65
|
|
|
* @var array |
66
|
|
|
*/ |
67
|
|
|
private $errors = array(); |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* Counter used to generate globally fresh variables. |
71
|
|
|
* @var integer |
72
|
|
|
*/ |
73
|
|
|
private $variableCounter = 0; |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* sortKeys that are being used while building the query conditions |
77
|
|
|
* @var array |
78
|
|
|
*/ |
79
|
|
|
private $sortKeys = array(); |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* The name of the SPARQL variable that represents the query result |
83
|
|
|
* @var string |
84
|
|
|
*/ |
85
|
|
|
private $resultVariable = 'result'; |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* @var string |
89
|
|
|
*/ |
90
|
|
|
private $joinVariable; |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* @var DIProperty|null |
94
|
|
|
*/ |
95
|
|
|
private $orderByProperty; |
96
|
|
|
|
97
|
|
|
/** |
98
|
|
|
* @var array |
99
|
|
|
*/ |
100
|
|
|
private $redirectByVariableReplacementMap = array(); |
101
|
|
|
|
102
|
|
|
/** |
103
|
|
|
* @since 2.2 |
104
|
|
|
* |
105
|
|
|
* @param DescriptionInterpreterFactory $descriptionInterpreterFactory |
106
|
|
|
* @param EngineOptions|null $engineOptions |
107
|
|
|
*/ |
108
|
33 |
|
public function __construct( DescriptionInterpreterFactory $descriptionInterpreterFactory, EngineOptions $engineOptions = null ) { |
109
|
33 |
|
$this->dispatchingDescriptionInterpreter = $descriptionInterpreterFactory->newDispatchingDescriptionInterpreter( $this ); |
|
|
|
|
110
|
33 |
|
$this->engineOptions = $engineOptions; |
111
|
|
|
|
112
|
33 |
|
if ( $this->engineOptions === null ) { |
113
|
28 |
|
$this->engineOptions = new EngineOptions(); |
114
|
|
|
} |
115
|
|
|
|
116
|
33 |
|
$this->descriptionFactory = new DescriptionFactory(); |
117
|
33 |
|
} |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* @since 2.0 |
121
|
|
|
* |
122
|
|
|
* @param string $resultVariable |
123
|
|
|
*/ |
124
|
5 |
|
public function setResultVariable( $resultVariable ) { |
125
|
5 |
|
$this->resultVariable = $resultVariable; |
126
|
5 |
|
return $this; |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* Get a fresh unused variable name for building SPARQL conditions. |
131
|
|
|
* |
132
|
|
|
* @return string |
133
|
|
|
*/ |
134
|
24 |
|
public function getNextVariable( $prefix = 'v' ) { |
135
|
24 |
|
return $prefix . ( ++$this->variableCounter ); |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* @since 2.0 |
140
|
|
|
* |
141
|
|
|
* @param array $sortKeys |
142
|
|
|
*/ |
143
|
5 |
|
public function setSortKeys( $sortKeys ) { |
144
|
5 |
|
$this->sortKeys = $sortKeys; |
145
|
5 |
|
return $this; |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
/** |
149
|
|
|
* @since 2.1 |
150
|
|
|
* |
151
|
|
|
* @return array |
152
|
|
|
*/ |
153
|
19 |
|
public function getSortKeys() { |
154
|
19 |
|
return $this->sortKeys; |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
/** |
158
|
|
|
* @since 2.2 |
159
|
|
|
* |
160
|
|
|
* @return array |
161
|
|
|
*/ |
162
|
1 |
|
public function getErrors() { |
163
|
1 |
|
return $this->errors; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
/** |
167
|
|
|
* @since 2.2 |
168
|
|
|
* |
169
|
|
|
* @param string $error |
170
|
|
|
*/ |
171
|
|
|
public function addError( $error, $type = Message::TEXT ) { |
172
|
|
|
$this->errors[Message::getHash( $error, $type )] = Message::encode( $error, $type ); |
|
|
|
|
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
/** |
176
|
|
|
* @since 2.2 |
177
|
|
|
* |
178
|
|
|
* @param CircularReferenceGuard $circularReferenceGuard |
179
|
|
|
*/ |
180
|
5 |
|
public function setCircularReferenceGuard( CircularReferenceGuard $circularReferenceGuard ) { |
181
|
5 |
|
$this->circularReferenceGuard = $circularReferenceGuard; |
182
|
5 |
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* @since 2.2 |
186
|
|
|
* |
187
|
|
|
* @return CircularReferenceGuard |
188
|
|
|
*/ |
189
|
|
|
public function getCircularReferenceGuard() { |
190
|
|
|
return $this->circularReferenceGuard; |
191
|
|
|
} |
192
|
|
|
|
193
|
|
|
/** |
194
|
|
|
* @since 2.3 |
195
|
|
|
* |
196
|
|
|
* @param PropertyHierarchyLookup $propertyHierarchyLookup |
197
|
|
|
*/ |
198
|
5 |
|
public function setPropertyHierarchyLookup( PropertyHierarchyLookup $propertyHierarchyLookup ) { |
199
|
5 |
|
$this->propertyHierarchyLookup = $propertyHierarchyLookup; |
200
|
5 |
|
} |
201
|
|
|
|
202
|
|
|
/** |
203
|
|
|
* @since 2.3 |
204
|
|
|
* |
205
|
|
|
* @return PropertyHierarchyLookup |
206
|
|
|
*/ |
207
|
18 |
|
public function getPropertyHierarchyLookup() { |
208
|
18 |
|
return $this->propertyHierarchyLookup; |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
/** |
212
|
|
|
* @since 2.2 |
213
|
|
|
* |
214
|
|
|
* @param string $joinVariable name of the variable that conditions |
215
|
|
|
* will refer to |
216
|
|
|
*/ |
217
|
24 |
|
public function setJoinVariable( $joinVariable ) { |
218
|
24 |
|
$this->joinVariable = $joinVariable; |
219
|
24 |
|
} |
220
|
|
|
|
221
|
|
|
/** |
222
|
|
|
* @since 2.2 |
223
|
|
|
* |
224
|
|
|
* @return string |
225
|
|
|
*/ |
226
|
24 |
|
public function getJoinVariable() { |
227
|
24 |
|
return $this->joinVariable; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
/** |
231
|
|
|
* @since 2.2 |
232
|
|
|
* |
233
|
|
|
* @param DIProperty|null $orderByProperty if given then |
234
|
|
|
* this is the property the values of which this condition will refer |
235
|
|
|
* to, and the condition should also enable ordering by this value |
236
|
|
|
*/ |
237
|
24 |
|
public function setOrderByProperty( $orderByProperty ) { |
238
|
24 |
|
$this->orderByProperty = $orderByProperty; |
239
|
24 |
|
} |
240
|
|
|
|
241
|
|
|
/** |
242
|
|
|
* @since 2.2 |
243
|
|
|
* |
244
|
|
|
* @return DIProperty|null |
245
|
|
|
*/ |
246
|
24 |
|
public function getOrderByProperty() { |
247
|
24 |
|
return $this->orderByProperty; |
248
|
|
|
} |
249
|
|
|
|
250
|
|
|
/** |
251
|
|
|
* Get a Condition object for a Description. |
252
|
|
|
* |
253
|
|
|
* This conversion is implemented by a number of recursive functions, |
254
|
|
|
* and this is the main entry point for this recursion. In particular, |
255
|
|
|
* it resets global variables that are used for the construction. |
256
|
|
|
* |
257
|
|
|
* If property value variables should be recorded for ordering results |
258
|
|
|
* later on, the keys of the respective properties need to be given in |
259
|
|
|
* sortKeys earlier. |
260
|
|
|
* |
261
|
|
|
* @param Description $description |
262
|
|
|
* |
263
|
|
|
* @return Condition |
264
|
|
|
*/ |
265
|
24 |
|
public function getConditionFrom( Description $description ) { |
266
|
24 |
|
$this->variableCounter = 0; |
267
|
|
|
|
268
|
24 |
|
$this->setJoinVariable( $this->resultVariable ); |
269
|
24 |
|
$this->setOrderByProperty( null ); |
270
|
|
|
|
271
|
24 |
|
$condition = $this->mapDescriptionToCondition( $description ); |
272
|
|
|
|
273
|
24 |
|
$this->addMissingOrderByConditions( |
274
|
|
|
$condition |
275
|
|
|
); |
276
|
|
|
|
277
|
23 |
|
$this->addPropertyPathToMatchRedirectTargets( |
278
|
|
|
$condition |
279
|
|
|
); |
280
|
|
|
|
281
|
23 |
|
$this->addFilterToRemoveEntitiesThatContainRedirectPredicate( |
282
|
|
|
$condition |
283
|
|
|
); |
284
|
|
|
|
285
|
23 |
|
return $condition; |
286
|
|
|
} |
287
|
|
|
|
288
|
|
|
/** |
289
|
|
|
* Recursively create a Condition from a Description |
290
|
|
|
* |
291
|
|
|
* @param Description $description |
292
|
|
|
* |
293
|
|
|
* @return Condition |
294
|
|
|
*/ |
295
|
24 |
|
public function mapDescriptionToCondition( Description $description ) { |
296
|
24 |
|
return $this->dispatchingDescriptionInterpreter->interpretDescription( $description ); |
297
|
|
|
} |
298
|
|
|
|
299
|
|
|
/** |
300
|
|
|
* Build the condition (WHERE) string for a given Condition. |
301
|
|
|
* The function also expresses the single value of |
302
|
|
|
* SingletonCondition objects in the condition, which may |
303
|
|
|
* lead to additional namespaces for serializing its URI. |
304
|
|
|
* |
305
|
|
|
* @param Condition $condition |
306
|
|
|
* |
307
|
|
|
* @return string |
308
|
|
|
*/ |
309
|
23 |
|
public function convertConditionToString( Condition &$condition ) { |
310
|
|
|
|
311
|
23 |
|
$conditionAsString = $condition->getWeakConditionString(); |
312
|
|
|
|
313
|
23 |
|
if ( ( $conditionAsString === '' ) && !$condition->isSafe() ) { |
314
|
1 |
|
$swivtPageResource = Exporter::getInstance()->getSpecialNsResource( 'swivt', 'page' ); |
315
|
1 |
|
$conditionAsString = '?' . $this->resultVariable . ' ' . $swivtPageResource->getQName() . " ?url .\n"; |
316
|
|
|
} |
317
|
|
|
|
318
|
23 |
|
$conditionAsString .= $condition->getCondition(); |
319
|
23 |
|
$conditionAsString .= $condition->getCogentConditionString(); |
320
|
|
|
|
321
|
23 |
|
if ( $condition instanceof SingletonCondition ) { // prepare for ASK, maybe rather use BIND? |
322
|
|
|
|
323
|
2 |
|
$matchElement = $condition->matchElement; |
324
|
|
|
|
325
|
2 |
|
if ( $matchElement instanceof ExpElement ) { |
|
|
|
|
326
|
1 |
|
$matchElementName = TurtleSerializer::getTurtleNameForExpElement( $matchElement ); |
327
|
|
|
} else { |
328
|
1 |
|
$matchElementName = $matchElement; |
329
|
|
|
} |
330
|
|
|
|
331
|
2 |
|
if ( $matchElement instanceof ExpNsResource ) { |
|
|
|
|
332
|
|
|
$condition->namespaces[$matchElement->getNamespaceId()] = $matchElement->getNamespace(); |
333
|
|
|
} |
334
|
|
|
|
335
|
2 |
|
$conditionAsString = str_replace( '?' . $this->resultVariable . ' ', "$matchElementName ", $conditionAsString ); |
336
|
|
|
} |
337
|
|
|
|
338
|
23 |
|
return $conditionAsString; |
339
|
|
|
} |
340
|
|
|
|
341
|
|
|
/** |
342
|
|
|
* Create an Condition from an empty (true) description. |
343
|
|
|
* May still require helper conditions for ordering. |
344
|
|
|
* |
345
|
|
|
* @param $joinVariable string name, see mapDescriptionToCondition() |
346
|
|
|
* @param $orderByProperty mixed DIProperty or null, see mapDescriptionToCondition() |
347
|
|
|
* |
348
|
|
|
* @return Condition |
349
|
|
|
*/ |
350
|
9 |
|
public function newTrueCondition( $joinVariable, $orderByProperty ) { |
351
|
9 |
|
$result = new TrueCondition(); |
352
|
9 |
|
$this->addOrderByDataForProperty( $result, $joinVariable, $orderByProperty ); |
353
|
9 |
|
return $result; |
354
|
|
|
} |
355
|
|
|
|
356
|
|
|
/** |
357
|
|
|
* @since 2.3 |
358
|
|
|
* |
359
|
|
|
* @param DataItem|null $dataItem |
360
|
|
|
* |
361
|
|
|
* @return string|null |
362
|
|
|
*/ |
363
|
23 |
|
public function tryToFindRedirectVariableForDataItem( DataItem $dataItem = null ) { |
364
|
|
|
|
365
|
23 |
|
if ( !$dataItem instanceof DIWikiPage || !$this->canUseQFeature( SMW_SPARQL_QF_REDI ) ) { |
366
|
7 |
|
return null; |
367
|
|
|
} |
368
|
|
|
|
369
|
|
|
// Maybe there is a better way to verify the "isRedirect" state other |
370
|
|
|
// than by using the Title object |
371
|
21 |
|
if ( $dataItem->getTitle() === null || !$dataItem->getTitle()->isRedirect() ) { |
372
|
19 |
|
return null; |
373
|
|
|
} |
374
|
|
|
|
375
|
2 |
|
$redirectExpElement = Exporter::getInstance()->getResourceElementForWikiPage( $dataItem ); |
|
|
|
|
376
|
|
|
|
377
|
|
|
// If the resource was matched to an imported vocab then no redirect is required |
378
|
2 |
|
if ( $redirectExpElement->isImported() ) { |
379
|
|
|
return null; |
380
|
|
|
} |
381
|
|
|
|
382
|
2 |
|
$valueName = TurtleSerializer::getTurtleNameForExpElement( $redirectExpElement ); |
|
|
|
|
383
|
|
|
|
384
|
|
|
// Add unknow redirect target/variable for value |
385
|
2 |
|
if ( !isset( $this->redirectByVariableReplacementMap[$valueName] ) ) { |
386
|
|
|
|
387
|
2 |
|
$namespaces[$redirectExpElement->getNamespaceId()] = $redirectExpElement->getNamespace(); |
|
|
|
|
388
|
2 |
|
$redirectByVariable = '?' . $this->getNextVariable( 'r' ); |
389
|
|
|
|
390
|
2 |
|
$this->redirectByVariableReplacementMap[$valueName] = array( |
391
|
2 |
|
$redirectByVariable, |
392
|
2 |
|
$namespaces |
393
|
|
|
); |
394
|
|
|
} |
395
|
|
|
|
396
|
|
|
// Reuse an existing variable for the value to allow to be used more than |
397
|
|
|
// once when referring to the same property/value redirect |
398
|
2 |
|
list( $redirectByVariable, $namespaces ) = $this->redirectByVariableReplacementMap[$valueName]; |
|
|
|
|
399
|
|
|
|
400
|
2 |
|
return $redirectByVariable; |
401
|
|
|
} |
402
|
|
|
|
403
|
|
|
/** |
404
|
|
|
* @since 2.3 |
405
|
|
|
* |
406
|
|
|
* @param integer $queryFeatureFlag |
407
|
|
|
* |
408
|
|
|
* @return boolean |
409
|
|
|
*/ |
410
|
21 |
|
public function canUseQFeature( $queryFeatureFlag ) { |
411
|
|
|
|
412
|
21 |
|
$canUse = true; |
413
|
|
|
|
414
|
|
|
// Adhere additional condition |
415
|
21 |
|
if ( $queryFeatureFlag === SMW_SPARQL_QF_SUBP ) { |
416
|
18 |
|
$canUse = $this->engineOptions->get( 'smwgQSubpropertyDepth' ) > 0; |
417
|
|
|
} |
418
|
|
|
|
419
|
21 |
|
if ( $queryFeatureFlag === SMW_SPARQL_QF_SUBC ) { |
420
|
2 |
|
$canUse = $this->engineOptions->get( 'smwgQSubcategoryDepth' ) > 0; |
421
|
|
|
} |
422
|
|
|
|
423
|
21 |
|
return $this->engineOptions->get( 'smwgSparqlQFeatures' ) === ( (int)$this->engineOptions->get( 'smwgSparqlQFeatures' ) | (int)$queryFeatureFlag ) && $canUse; |
|
|
|
|
424
|
|
|
} |
425
|
|
|
|
426
|
|
|
/** |
427
|
|
|
* Extend the given SPARQL condition by a suitable order by variable, |
428
|
|
|
* if an order by property is set. |
429
|
|
|
* |
430
|
|
|
* @param Condition $sparqlCondition condition to modify |
431
|
|
|
* @param string $mainVariable the variable that represents the value to be ordered |
432
|
|
|
* @param mixed $orderByProperty DIProperty or null |
433
|
|
|
* @param integer $diType DataItem type id if known, or DataItem::TYPE_NOTYPE to determine it from the property |
434
|
|
|
*/ |
435
|
24 |
|
public function addOrderByDataForProperty( Condition &$sparqlCondition, $mainVariable, $orderByProperty, $diType = DataItem::TYPE_NOTYPE ) { |
436
|
24 |
|
if ( is_null( $orderByProperty ) ) { |
437
|
24 |
|
return; |
438
|
|
|
} |
439
|
|
|
|
440
|
2 |
|
if ( $diType == DataItem::TYPE_NOTYPE ) { |
441
|
2 |
|
$diType = DataTypeRegistry::getInstance()->getDataItemId( $orderByProperty->findPropertyTypeID() ); |
|
|
|
|
442
|
|
|
} |
443
|
|
|
|
444
|
2 |
|
$this->addOrderByData( $sparqlCondition, $mainVariable, $diType ); |
445
|
2 |
|
} |
446
|
|
|
|
447
|
|
|
/** |
448
|
|
|
* Extend the given SPARQL condition by a suitable order by variable, |
449
|
|
|
* possibly adding conditions if required for the type of data. |
450
|
|
|
* |
451
|
|
|
* @param Condition $sparqlCondition condition to modify |
|
|
|
|
452
|
|
|
* @param string $mainVariable the variable that represents the value to be ordered |
453
|
|
|
* @param integer $diType DataItem type id |
454
|
|
|
*/ |
455
|
10 |
|
public function addOrderByData( Condition &$condition, $mainVariable, $diType ) { |
456
|
|
|
|
457
|
10 |
|
if ( $diType !== DataItem::TYPE_WIKIPAGE ) { |
458
|
3 |
|
return $condition->orderByVariable = $mainVariable; |
459
|
|
|
} |
460
|
|
|
|
461
|
7 |
|
$condition->orderByVariable = $mainVariable . 'sk'; |
462
|
7 |
|
$skeyExpElement = Exporter::getInstance()->getSpecialPropertyResource( '_SKEY' ); |
463
|
|
|
|
464
|
|
|
$weakConditions = array( |
465
|
7 |
|
$condition->orderByVariable =>"?$mainVariable " . $skeyExpElement->getQName() . " ?{$condition->orderByVariable} .\n" |
466
|
|
|
); |
467
|
|
|
|
468
|
7 |
|
$condition->weakConditions += $weakConditions; |
469
|
7 |
|
} |
470
|
|
|
|
471
|
|
|
/** |
472
|
|
|
* Extend the given Condition with additional conditions to |
473
|
|
|
* ensure that it can be ordered by all requested properties. After |
474
|
|
|
* this operation, every key in sortKeys is assigned to a query |
475
|
|
|
* variable by $sparqlCondition->orderVariables. |
476
|
|
|
* |
477
|
|
|
* @param Condition $condition condition to modify |
478
|
|
|
*/ |
479
|
24 |
|
protected function addMissingOrderByConditions( Condition &$condition ) { |
480
|
24 |
|
foreach ( $this->sortKeys as $propertyKey => $order ) { |
481
|
|
|
|
482
|
5 |
|
if ( !is_string( $propertyKey ) ) { |
483
|
1 |
|
throw new RuntimeException( "Expected a string value as sortkey" ); |
484
|
|
|
} |
485
|
|
|
|
486
|
4 |
|
if ( strpos( $propertyKey, " " ) !== false ) { |
487
|
|
|
throw new RuntimeException( "Expected the canonical form of {$propertyKey} (without any whitespace)" ); |
488
|
|
|
} |
489
|
|
|
|
490
|
4 |
|
if ( !array_key_exists( $propertyKey, $condition->orderVariables ) ) { // Find missing property to sort by. |
491
|
4 |
|
$this->addOrderForUnknownPropertyKey( $condition, $propertyKey, $order ); |
492
|
|
|
} |
493
|
|
|
} |
494
|
23 |
|
} |
495
|
|
|
|
496
|
3 |
|
private function addOrderForUnknownPropertyKey( Condition &$condition, $propertyKey, $order ) { |
497
|
|
|
|
498
|
3 |
|
if ( $propertyKey === '' || $propertyKey === '#' ) { // order by result page sortkey |
499
|
|
|
|
500
|
2 |
|
$this->addOrderByData( |
501
|
|
|
$condition, |
502
|
2 |
|
$this->resultVariable, |
503
|
2 |
|
DataItem::TYPE_WIKIPAGE |
504
|
|
|
); |
505
|
|
|
|
506
|
2 |
|
$condition->orderVariables[$propertyKey] = $condition->orderByVariable; |
507
|
2 |
|
return; |
508
|
1 |
|
} elseif ( PropertyChainValue::isChained( $propertyKey ) ) { // Try to extend query. |
509
|
|
|
$propertyChainValue = new PropertyChainValue(); |
510
|
|
|
$propertyChainValue->setUserValue( $propertyKey ); |
511
|
|
|
|
512
|
|
|
if ( !$propertyChainValue->isValid() ) { |
513
|
|
|
return $description; |
|
|
|
|
514
|
|
|
} |
515
|
|
|
|
516
|
|
|
$lastDataItem = $propertyChainValue->getLastPropertyChainValue()->getDataItem(); |
517
|
|
|
|
518
|
|
|
$description = $this->descriptionFactory->newSomeProperty( |
519
|
|
|
$lastDataItem, |
|
|
|
|
520
|
|
|
$this->descriptionFactory->newThingDescription() |
521
|
|
|
); |
522
|
|
|
|
523
|
|
|
foreach ( $propertyChainValue->getPropertyChainValues() as $val ) { |
524
|
|
|
$description = $this->descriptionFactory->newSomeProperty( |
525
|
|
|
$val->getDataItem(), |
|
|
|
|
526
|
|
|
$description |
527
|
|
|
); |
528
|
|
|
} |
529
|
|
|
|
530
|
|
|
// Add and replace Foo.Bar=asc with Bar=asc as we ultimately only |
531
|
|
|
// order to the result of the last element |
532
|
|
|
$this->sortKeys[$lastDataItem->getKey()] = $order; |
533
|
|
|
unset( $this->sortKeys[$propertyKey] ); |
534
|
|
|
$propertyKey = $lastDataItem->getKey(); |
535
|
|
|
|
536
|
|
|
$auxDescription = $description; |
537
|
|
|
} else { |
538
|
1 |
|
$auxDescription = $this->descriptionFactory->newSomeProperty( |
539
|
1 |
|
new DIProperty( $propertyKey ), |
540
|
1 |
|
$this->descriptionFactory->newThingDescription() |
541
|
|
|
); |
542
|
|
|
} |
543
|
|
|
|
544
|
1 |
|
$this->setJoinVariable( $this->resultVariable ); |
545
|
1 |
|
$this->setOrderByProperty( null ); |
546
|
|
|
|
547
|
1 |
|
$auxCondition = $this->mapDescriptionToCondition( |
548
|
|
|
$auxDescription |
549
|
|
|
); |
550
|
|
|
|
551
|
|
|
// orderVariables MUST be set for $propertyKey -- or there is a bug; let it show! |
552
|
1 |
|
$condition->orderVariables[$propertyKey] = $auxCondition->orderVariables[$propertyKey]; |
553
|
1 |
|
$condition->weakConditions[$condition->orderVariables[$propertyKey]] = $auxCondition->getWeakConditionString() . $auxCondition->getCondition(); |
554
|
1 |
|
$condition->namespaces = array_merge( $condition->namespaces, $auxCondition->namespaces ); |
555
|
1 |
|
} |
556
|
|
|
|
557
|
|
|
/** |
558
|
|
|
* @see http://www.w3.org/TR/sparql11-query/#propertypaths |
559
|
|
|
* |
560
|
|
|
* Query of: |
561
|
|
|
* |
562
|
|
|
* SELECT DISTINCT ?result WHERE { |
563
|
|
|
* ?result swivt:wikiPageSortKey ?resultsk . |
564
|
|
|
* { |
565
|
|
|
* ?result property:FOO ?v1 . |
566
|
|
|
* FILTER( ?v1sk >= "=BAR" ) |
567
|
|
|
* ?v1 swivt:wikiPageSortKey ?v1sk . |
568
|
|
|
* } UNION { |
569
|
|
|
* ?result property:FOO ?v2 . |
570
|
|
|
* } |
571
|
|
|
* } |
572
|
|
|
* |
573
|
|
|
* results in: |
574
|
|
|
* |
575
|
|
|
* SELECT DISTINCT ?result WHERE { |
576
|
|
|
* ?result swivt:wikiPageSortKey ?resultsk . |
577
|
|
|
* ?r2 ^swivt:redirectsTo property:FOO . |
578
|
|
|
* { |
579
|
|
|
* ?result ?r2 ?v1 . |
580
|
|
|
* FILTER( ?v1sk >= "=BAR" ) |
581
|
|
|
* ?v1 swivt:wikiPageSortKey ?v1sk . |
582
|
|
|
* } UNION { |
583
|
|
|
* ?result ?r2 ?v3 . |
584
|
|
|
* } |
585
|
|
|
* } |
586
|
|
|
*/ |
587
|
23 |
|
private function addPropertyPathToMatchRedirectTargets( Condition &$condition ) { |
588
|
|
|
|
589
|
23 |
|
if ( $this->redirectByVariableReplacementMap === array() ) { |
590
|
21 |
|
return; |
591
|
|
|
} |
592
|
|
|
|
593
|
2 |
|
$weakConditions = array(); |
594
|
2 |
|
$namespaces = array(); |
595
|
|
|
|
596
|
2 |
|
$rediExpElement = Exporter::getInstance()->getSpecialPropertyResource( '_REDI' ); |
597
|
2 |
|
$namespaces[$rediExpElement->getNamespaceId()] = $rediExpElement->getNamespace(); |
598
|
|
|
|
599
|
2 |
|
foreach ( $this->redirectByVariableReplacementMap as $valueName => $content ) { |
600
|
2 |
|
list( $redirectByVariable, $ns ) = $content; |
601
|
2 |
|
$weakConditions[] = "$redirectByVariable " . "^" . $rediExpElement->getQName() . " $valueName .\n"; |
602
|
2 |
|
$namespaces = array_merge( $namespaces, $ns ); |
603
|
|
|
} |
604
|
|
|
|
605
|
2 |
|
$condition->namespaces = array_merge( $condition->namespaces, $namespaces ); |
606
|
2 |
|
$condition->weakConditions += $weakConditions; |
607
|
2 |
|
} |
608
|
|
|
|
609
|
|
|
/** |
610
|
|
|
* @see https://www.w3.org/TR/rdf-sparql-query/#func-bound |
611
|
|
|
* |
612
|
|
|
* Remove entities that contain a "swivt:redirectsTo" predicate |
613
|
|
|
*/ |
614
|
23 |
|
private function addFilterToRemoveEntitiesThatContainRedirectPredicate( Condition &$condition ) { |
615
|
|
|
|
616
|
23 |
|
$rediExpElement = Exporter::getInstance()->getSpecialPropertyResource( '_REDI' ); |
617
|
23 |
|
$namespaces[$rediExpElement->getNamespaceId()] = $rediExpElement->getNamespace(); |
|
|
|
|
618
|
|
|
|
619
|
23 |
|
$boundVariable = '?' . $this->getNextVariable( 'o' ); |
620
|
23 |
|
$cogentCondition = " OPTIONAL { ?$this->resultVariable " . $rediExpElement->getQName() . " $boundVariable } .\n FILTER ( !bound( $boundVariable ) ) .\n"; |
621
|
|
|
|
622
|
23 |
|
$condition->addNamespaces( $namespaces ); |
623
|
23 |
|
$condition->cogentConditions[$boundVariable] = $cogentCondition; |
624
|
23 |
|
} |
625
|
|
|
|
626
|
|
|
} |
627
|
|
|
|
Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.
Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..