/** * @since 2.2 * * @param Description $description * * @return QuerySegment */ public function interpretDescription(Description $description) { $query = new QuerySegment(); $query->type = $description instanceof Conjunction ? QuerySegment::Q_CONJUNCTION : QuerySegment::Q_DISJUNCTION; foreach ($description->getDescriptions() as $subDescription) { $subQueryId = $this->querySegmentListBuilder->getQuerySegmentFrom($subDescription); if ($subQueryId >= 0) { $query->components[$subQueryId] = true; } } // All subconditions failed, drop this as well. if (count($query->components) == 0) { $query->type = QuerySegment::Q_NOQUERY; } return $query; }
/** * @since 2.2 * * @param Description $description * * @return QuerySegment */ public function interpretDescription(Description $description) { $query = new QuerySegment(); $conceptId = $this->querySegmentListBuilder->getStore()->getObjectIds()->getSMWPageID($description->getConcept()->getDBkey(), SMW_NS_CONCEPT, '', ''); $hash = 'concept-' . $conceptId; $this->querySegmentListBuilder->getCircularReferenceGuard()->mark($hash); if ($this->querySegmentListBuilder->getCircularReferenceGuard()->isCircularByRecursionFor($hash)) { $this->querySegmentListBuilder->addError(wfMessage('smw-query-condition-circular', $description->getQueryString())->text()); return $query; } $db = $this->querySegmentListBuilder->getStore()->getConnection('mw.db.queryengine'); $row = $this->getConceptForId($db, $conceptId); // No description found, concept does not exist. if ($row === false) { $this->querySegmentListBuilder->getCircularReferenceGuard()->unmark('concept-' . $conceptId); // keep the above query object, it yields an empty result // TODO: announce an error here? (maybe not, since the query processor can check for // non-existing concept pages which is probably the main reason for finding nothing here) return $query; } global $smwgQConceptCaching, $smwgQMaxSize, $smwgQMaxDepth, $smwgQFeatures, $smwgQConceptCacheLifetime; $may_be_computed = $smwgQConceptCaching == CONCEPT_CACHE_NONE || $smwgQConceptCaching == CONCEPT_CACHE_HARD && ~(~($row->concept_features + 0) | $smwgQFeatures) == 0 && $smwgQMaxSize >= $row->concept_size && $smwgQMaxDepth >= $row->concept_depth; if ($row->cache_date && ($row->cache_date > strtotime("now") - $smwgQConceptCacheLifetime * 60 || !$may_be_computed)) { // Cached concept, use cache unless it is dead and can be revived. $query->joinTable = SMWSQLStore3::CONCEPT_CACHE_TABLE; $query->joinfield = "{$query->alias}.s_id"; $query->where = "{$query->alias}.o_id=" . $db->addQuotes($conceptId); } elseif ($row->concept_txt) { // Parse description and process it recursively. if ($may_be_computed) { $qid = $this->querySegmentListBuilder->getQuerySegmentFrom($this->getConceptQueryDescriptionFrom($row->concept_txt)); if ($qid != -1) { $query = $this->querySegmentListBuilder->findQuerySegment($qid); } else { // somehow the concept query is no longer valid; maybe some syntax changed (upgrade) or global settings were modified since storing it $this->querySegmentListBuilder->addError(wfMessage('smw_emptysubquery')->text()); // not the right message, but this case is very rare; let us not make detailed messages for this } } else { $this->querySegmentListBuilder->addError(wfMessage('smw_concept_cache_miss', $description->getConcept()->getTitle()->getText())->text()); } } // else: no cache, no description (this may happen); treat like empty concept $this->querySegmentListBuilder->getCircularReferenceGuard()->unmark($hash); return $query; }
private function compileAccordingConditionsAndHackThemIntoQobj(array $extraProperties, $qobj, $qid) { $this->querySegmentListBuilder->setSortKeys($this->sortKeys); $this->querySegmentListBuilder->getQuerySegmentFrom(new Conjunction($extraProperties)); $newQuerySegmentId = $this->querySegmentListBuilder->getLastQuerySegmentId(); $this->querySegmentList = $this->querySegmentListBuilder->getQuerySegmentList(); $this->errors = $this->querySegmentListBuilder->getErrors(); $newQuerySegment = $this->querySegmentList[$newQuerySegmentId]; // This is always an QuerySegment::Q_CONJUNCTION ... foreach ($newQuerySegment->components as $cid => $field) { // ... so just re-wire its dependencies $qobj->components[$cid] = $qobj->joinfield; $qobj->sortfields = array_merge($qobj->sortfields, $this->querySegmentList[$cid]->sortfields); } $this->querySegmentList[$qid] = $qobj; }
public function testClassDescription() { $objectIds = $this->getMockBuilder('\\stdClass')->setMethods(array('getSMWPageID'))->getMock(); $objectIds->expects($this->any())->method('getSMWPageID')->will($this->returnValue(42)); $connection = $this->getMockBuilder('\\SMW\\MediaWiki\\Database')->disableOriginalConstructor()->getMock(); $store = $this->getMockBuilder('\\SMW\\SQLStore\\SQLStore')->disableOriginalConstructor()->getMock(); $store->expects($this->any())->method('getConnection')->will($this->returnValue($connection)); $store->expects($this->once())->method('getObjectIds')->will($this->returnValue($objectIds)); $description = new ClassDescription(new DIWikiPage('Foo', NS_CATEGORY)); $instance = new QuerySegmentListBuilder($store, $this->descriptionInterpreterFactory); $instance->getQuerySegmentFrom($description); $expectedClass = new \stdClass(); $expectedClass->type = 1; $expectedClass->alias = "t0"; $expectedClass->queryNumber = 0; $expectedHierarchy = new \stdClass(); $expectedHierarchy->type = 5; $expectedHierarchy->joinfield = array(0 => 42); $expectedHierarchy->alias = "t1"; $expectedHierarchy->queryNumber = 1; $this->assertEquals(0, $instance->getLastQuerySegmentId()); $this->assertEmpty($instance->getErrors()); $expected = array($expectedClass, $expectedHierarchy); $this->querySegmentValidator->assertThatContainerContains($expected, $instance->getQuerySegmentList()); }
/** * Modify the given query object to account for some property condition for * the given property. If it is not possible to generate a query for the * given data, the query type is changed to QueryContainer::Q_NOQUERY. Callers need * to check for this and discard the query in this case. * * @note This method does not support sortkey (_SKEY) property queries, * since they do not have a normal property table. This should not be a * problem since comparators on sortkeys are supported indirectly when * using comparators on wikipages. There is no reason to create any * query with _SKEY ad users cannot do so either (no user label). * * @since 1.8 */ private function interpretPropertyConditionForDescription(QuerySegment $query, SomeProperty $description) { $db = $this->querySegmentListBuilder->getStore()->getConnection('mw.db.queryengine'); $property = $description->getProperty(); $tableid = $this->querySegmentListBuilder->getStore()->findPropertyTableID($property); if ($tableid === '') { // Give up $query->type = QuerySegment::Q_NOQUERY; return; } $proptables = $this->querySegmentListBuilder->getStore()->getPropertyTables(); $proptable = $proptables[$tableid]; if (!$proptable->usesIdSubject()) { // no queries with such tables // (only redirects are affected in practice) $query->type = QuerySegment::Q_NOQUERY; return; } $typeid = $property->findPropertyTypeID(); $diType = DataTypeRegistry::getInstance()->getDataItemId($typeid); if ($property->isInverse() && $diType !== DataItem::TYPE_WIKIPAGE) { // can only invert properties that point to pages $query->type = QuerySegment::Q_NOQUERY; return; } $diHandler = $this->querySegmentListBuilder->getStore()->getDataItemHandlerForDIType($diType); $indexField = $diHandler->getIndexField(); // TODO: strictly speaking, the DB key is not what we want here, // since sortkey is based on a "wiki value" $sortkey = $property->getKey(); // *** Now construct the query ... ***// $query->joinTable = $proptable->getName(); // *** Add conditions for selecting rows for this property ***// if (!$proptable->isFixedPropertyTable()) { $pid = $this->querySegmentListBuilder->getStore()->getObjectIds()->getSMWPropertyID($property); // Construct property hierarchy: $pqid = QuerySegment::$qnum; $pquery = new QuerySegment(); $pquery->type = QuerySegment::Q_PROP_HIERARCHY; $pquery->joinfield = array($pid); $query->components[$pqid] = "{$query->alias}.p_id"; $this->querySegmentListBuilder->addQuerySegment($pquery); // Alternative code without property hierarchies: // $query->where = "{$query->alias}.p_id=" . $this->m_dbs->addQuotes( $pid ); } // else: no property column, no hierarchy queries // *** Add conditions on the value of the property ***// if ($diType === DataItem::TYPE_WIKIPAGE) { $o_id = $indexField; if ($property->isInverse()) { $s_id = $o_id; $o_id = 's_id'; } else { $s_id = 's_id'; } $query->joinfield = "{$query->alias}.{$s_id}"; // process page description like main query $sub = $this->querySegmentListBuilder->getQuerySegmentFrom($description->getDescription()); if ($sub >= 0) { $query->components[$sub] = "{$query->alias}.{$o_id}"; } if (array_key_exists($sortkey, $this->querySegmentListBuilder->getSortKeys())) { // TODO: This SMW IDs table is possibly duplicated in the query. // Example: [[has capital::!Berlin]] with sort=has capital // Can we prevent that? (PERFORMANCE) $query->from = ' INNER JOIN ' . $db->tableName(SMWSql3SmwIds::TABLE_NAME) . " AS ids{$query->alias} ON ids{$query->alias}.smw_id={$query->alias}.{$o_id}"; $query->sortfields[$sortkey] = "ids{$query->alias}.smw_sortkey"; } } else { // non-page value description $query->joinfield = "{$query->alias}.s_id"; $this->interpretInnerValueDescription($query, $description->getDescription(), $proptable, $diHandler, 'AND'); if (array_key_exists($sortkey, $this->querySegmentListBuilder->getSortKeys())) { $query->sortfields[$sortkey] = isset($query->sortIndexField) ? $query->sortIndexField : "{$query->alias}.{$indexField}"; } } }