/** * @since 2.2 * * @param Description $description * * @return QuerySegment */ public function interpretDescription(Description $description) { $query = new QuerySegment(); $cqid = QuerySegment::$qnum; $cquery = new QuerySegment(); $cquery->type = QuerySegment::Q_CLASS_HIERARCHY; $cquery->joinfield = array(); foreach ($description->getCategories() as $category) { $categoryId = $this->querySegmentListBuilder->getStore()->getObjectIds()->getSMWPageID($category->getDBkey(), NS_CATEGORY, $category->getInterwiki(), ''); if ($categoryId != 0) { $cquery->joinfield[] = $categoryId; } } if (count($cquery->joinfield) == 0) { // Empty result. $query->type = QuerySegment::Q_VALUE; $query->joinTable = ''; $query->joinfield = ''; } else { // Instance query with disjunction of classes (categories) $query->joinTable = $this->querySegmentListBuilder->getStore()->findPropertyTableID(new DIProperty('_INST')); $query->joinfield = "{$query->alias}.s_id"; $query->components[$cqid] = "{$query->alias}.o_id"; $this->querySegmentListBuilder->addQuerySegment($cquery); } return $query; }
/** * TODO: One instance of the SMW IDs table on s_id always suffices (swm_id is KEY)! Doable in execution ... (PERFORMANCE) * * @since 2.2 * * @param Description $description * * @return QuerySegment */ public function interpretDescription(Description $description) { $query = new QuerySegment(); $query->joinTable = SMWSql3SmwIds::TABLE_NAME; $query->joinfield = "{$query->alias}.smw_id"; $query->where = "{$query->alias}.smw_namespace=" . $this->querySegmentListBuilder->getStore()->getConnection('mw.db')->addQuotes($description->getNamespace()); return $query; }
/** * @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; }
/** * Given an Description that is just a conjunction or disjunction of * ValueDescription objects, create and return a plain WHERE condition * string for it. * * @param $query * @param ValueDescription $description * @param DataItemHandler $diHandler for that table * @param string $operator SQL operator "AND" or "OR" */ private function mapValueDescription($query, ValueDescription $description, DataItemHandler $diHandler, $operator) { $where = ''; $dataItem = $description->getDataItem(); $db = $this->querySegmentListBuilder->getStore()->getConnection('mw.db'); // TODO Better get the handle from the property type // Some comparators (e.g. LIKE) could use DI values of // a different type; we care about the property table, not // about the value // Do not support smw_id joined data for now. $indexField = $diHandler->getIndexField(); //Hack to get to the field used as index $keys = $diHandler->getWhereConds($dataItem); $value = $keys[$indexField]; // See if the getSQLCondition method exists and call it if this is the case. // Invoked by SMAreaValueDescription, SMGeoCoordsValueDescription if (method_exists($description, 'getSQLCondition')) { $fields = $diHandler->getTableFields(); $where = $description->getSQLCondition($query->alias, array_keys($fields), $this->querySegmentListBuilder->getStore()->getConnection(DB_SLAVE)); } if ($where == '') { $comparator = $this->comparatorMapper->mapComparator($description, $value); $where = "{$query->alias}.{$indexField}{$comparator}" . $db->addQuotes($value); } if ($where !== '') { if ($query->where && substr($query->where, -1) != '(') { $query->where .= " {$operator} "; } $query->where .= "({$where})"; } }
/** * @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; }
/** * Only type '_wpg' objects can appear on query level (essentially as nominal classes) * * @since 2.2 * * @param Description $description * * @return QuerySegment */ public function interpretDescription(Description $description) { $query = new QuerySegment(); if (!$description->getDataItem() instanceof DIWikiPage) { return $query; } if ($description->getComparator() === SMW_CMP_EQ) { $query->type = QuerySegment::Q_VALUE; $oid = $this->querySegmentListBuilder->getStore()->getObjectIds()->getSMWPageID($description->getDataItem()->getDBkey(), $description->getDataItem()->getNamespace(), $description->getDataItem()->getInterwiki(), $description->getDataItem()->getSubobjectName()); $query->joinfield = array($oid); } else { // Join with SMW IDs table needed for other comparators (apply to title string). $query->joinTable = SMWSql3SmwIds::TABLE_NAME; $query->joinfield = "{$query->alias}.smw_id"; $value = $description->getDataItem()->getSortKey(); $comparator = $this->comparatorMapper->mapComparator($description, $value); $query->where = "{$query->alias}.smw_sortkey{$comparator}" . $this->querySegmentListBuilder->getStore()->getConnection('mw.db')->addQuotes($value); } return $query; }
private function addFulltextSearchCondition($query, $comparator, &$value) { // Remove remaining ~ from the search string $value = str_replace('~', '', $value); $valueMatchConditionBuilder = $this->fulltextSearchTableFactory->newValueMatchConditionBuilderByType($this->querySegmentListBuilder->getStore()); if (!$valueMatchConditionBuilder->isEnabled() || !$valueMatchConditionBuilder->hasMinTokenLength($value)) { return false; } $query->joinTable = $valueMatchConditionBuilder->getTableName(); $query->joinfield = "{$query->alias}.s_id"; $query->components = array(); $query->where = $valueMatchConditionBuilder->getWhereCondition(new ValueDescription(new DIBlob($value), null, $comparator), $query->alias); return $query; }
private function compileAccordingConditionsAndHackThemIntoQobj(array $extraProperties, $qobj, $qid) { $this->querySegmentListBuilder->setSortKeys($this->sortKeys); $this->querySegmentListBuilder->buildQuerySegmentFor(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; }
/** * @dataProvider descriptionProvider */ public function testinterpretDescription($description, $isFixedPropertyTable, $indexField, $sortKeys, $expected) { $dataItemHandler = $this->getMockBuilder('\\SMWDataItemHandler')->disableOriginalConstructor()->getMockForAbstractClass(); $dataItemHandler->expects($this->any())->method('getIndexField')->will($this->returnValue($indexField)); $dataItemHandler->expects($this->any())->method('getTableFields')->will($this->returnValue(array('one', 'two'))); $dataItemHandler->expects($this->any())->method('getWhereConds')->will($this->returnValue(array($indexField => 'fixedFooWhereCond'))); $objectIds = $this->getMockBuilder('\\stdClass')->setMethods(array('getSMWPropertyID', 'getSMWPageID'))->getMock(); $objectIds->expects($this->any())->method('getSMWPropertyID')->will($this->returnValue(42)); $objectIds->expects($this->any())->method('getSMWPageID')->will($this->returnValue(91)); $connection = $this->getMockBuilder('\\SMW\\MediaWiki\\Database')->disableOriginalConstructor()->getMock(); $connection->expects($this->any())->method('addQuotes')->will($this->returnArgument(0)); $store = $this->getMockBuilder('\\SMW\\SQLStore\\SQLStore')->disableOriginalConstructor()->getMock(); $proptable = $this->getMockBuilder('\\SMWSQLStore3Table')->disableOriginalConstructor()->getMock(); $proptable->expects($this->any())->method('usesIdSubject')->will($this->returnValue(true)); $proptable->expects($this->any())->method('getName')->will($this->returnValue('FooPropTable')); $proptable->expects($this->any())->method('isFixedPropertyTable')->will($this->returnValue($isFixedPropertyTable)); $store = $this->getMockBuilder('\\SMW\\SQLStore\\SQLStore')->disableOriginalConstructor()->getMock(); $store->expects($this->once())->method('findPropertyTableID')->will($this->returnValue('Foo')); $store->expects($this->once())->method('getPropertyTables')->will($this->returnValue(array('Foo' => $proptable))); $store->expects($this->any())->method('getConnection')->will($this->returnValue($connection)); $store->expects($this->any())->method('getObjectIds')->will($this->returnValue($objectIds)); $store->expects($this->any())->method('getDataItemHandlerForDIType')->will($this->returnValue($dataItemHandler)); $querySegmentListBuilder = new QuerySegmentListBuilder($store); $querySegmentListBuilder->setSortKeys($sortKeys); $instance = new SomePropertyInterpreter($querySegmentListBuilder); $this->assertTrue($instance->canInterpretDescription($description)); $this->querySegmentValidator->assertThatContainerHasProperties($expected, $instance->interpretDescription($description)); }
public function testWhenSomeQuerySegments_getQuerySegmentListReturnsThemAll() { $instance = new QuerySegmentListBuilder($this->store); $firstQuerySegment = new QuerySegment(); $firstQuerySegment->segmentNumber = 42; $instance->addQuerySegment($firstQuerySegment); $secondQuerySegment = new QuerySegment(); $secondQuerySegment->segmentNumber = 23; $instance->addQuerySegment($secondQuerySegment); $expected = array(42 => $firstQuerySegment, 23 => $secondQuerySegment); $this->assertSame($expected, $instance->getQuerySegmentList()); }
public function testWhenSomeQuerySegments_getQuerySegmentListReturnsThemAll() { $instance = new QuerySegmentListBuilder($this->store, $this->descriptionInterpreterFactory); $firstQuerySegment = new QuerySegment(); $instance->addQuerySegment($firstQuerySegment); $secondQuerySegment = new QuerySegment(); $instance->addQuerySegment($secondQuerySegment); $expected = array(0 => $firstQuerySegment, 1 => $secondQuerySegment); $this->assertSame($expected, $instance->getQuerySegmentList()); }
/** * We bypass the storage interface here (which is legal as we control it, * and safe if we are careful with changes ...) * * This should be faster, but we must implement the unescaping that concepts * do on getWikiValue */ private function getConceptForId($id) { return $this->querySegmentListBuilder->getStore()->getConnection('mw.db')->selectRow('smw_fpt_conc', array('concept_txt', 'concept_features', 'concept_size', 'concept_depth', 'cache_date'), array('s_id' => $id), __METHOD__); }