summaryrefslogtreecommitdiff
path: root/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/ConditionBuilder.php
diff options
context:
space:
mode:
Diffstat (limited to 'www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/ConditionBuilder.php')
-rw-r--r--www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/ConditionBuilder.php464
1 files changed, 464 insertions, 0 deletions
diff --git a/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/ConditionBuilder.php b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/ConditionBuilder.php
new file mode 100644
index 00000000..09370a24
--- /dev/null
+++ b/www/wiki/extensions/SemanticMediaWiki/src/Elastic/QueryEngine/ConditionBuilder.php
@@ -0,0 +1,464 @@
+<?php
+
+namespace SMW\Elastic\QueryEngine;
+
+use Psr\Log\LoggerAwareTrait;
+use SMW\ApplicationFactory;
+use SMW\DIProperty;
+use SMW\DIWikiPage;
+use SMW\Options;
+use SMW\HierarchyLookup;
+use SMW\Services\ServicesContainer;
+use SMW\Query\Language\ClassDescription;
+use SMW\Query\Language\ConceptDescription;
+use SMW\Query\Language\Conjunction;
+use SMW\Query\Language\Description;
+use SMW\Query\Language\Disjunction;
+use SMW\Query\Language\NamespaceDescription;
+use SMW\Query\Language\SomeProperty;
+use SMW\Query\Language\ValueDescription;
+use SMW\Store;
+use SMWDataItem as DataItem;
+
+/**
+ * Build an internal representation for a SPARQL condition from individual query
+ * descriptions.
+ *
+ * @license GNU GPL v2+
+ * @since 3.0
+ *
+ * @author mwjames
+ */
+class ConditionBuilder {
+
+ use LoggerAwareTrait;
+
+ /**
+ * @var Store
+ */
+ private $store;
+
+ /**
+ * @var Options
+ */
+ private $options;
+
+ /**
+ * @var TermsLookup
+ */
+ private $termsLookup;
+
+ /**
+ * @var HierarchyLookup
+ */
+ private $hierarchyLookup;
+
+ /**
+ * @var ServicesContainer
+ */
+ private $servicesContainer;
+
+ /**
+ * @var FieldMapper
+ */
+ private $fieldMapper;
+
+ /**
+ * @var ConceptDescriptionInterpreter
+ */
+ private $conceptDescriptionInterpreter;
+
+ /**
+ * @var ClassDescriptionInterpreter
+ */
+ private $classDescriptionInterpreter;
+
+ /**
+ * @var ValueDescriptionInterpreter
+ */
+ private $valueDescriptionInterpreter;
+
+ /**
+ * @var SomePropertyInterpreter
+ */
+ private $somePropertyInterpreter;
+
+ /**
+ * @var ConjunctionInterpreter
+ */
+ private $conjunctionInterpreter;
+
+ /**
+ * @var DisjunctionInterpreter
+ */
+ private $disjunctionInterpreter;
+
+ /**
+ * @var NamespaceDescriptionInterpreter
+ */
+ private $namespaceDescriptionInterpreter;
+
+ /**
+ * @var SomeValueInterpreter
+ */
+ private $someValueInterpreter;
+
+ /**
+ * @var array
+ */
+ private $sortFields = [];
+
+ /**
+ * @var array
+ */
+ private $errors = [];
+
+ /**
+ * @var array
+ */
+ private $queryInfo = [];
+
+ /**
+ * @var array
+ */
+ private $descriptionLog = [];
+
+ /**
+ * @var boolean
+ */
+ protected $isConstantScore = true;
+
+ /**
+ * @var boolean
+ */
+ private $initServices = false;
+
+ /**
+ * @since 3.0
+ *
+ * @param Store $store
+ * @param TermsLookup $termsLookup
+ * @param HierarchyLookup $hierarchyLookup
+ * @param ServicesContainer $servicesContainer
+ */
+ public function __construct( Store $store, TermsLookup $termsLookup, HierarchyLookup $hierarchyLookup, ServicesContainer $servicesContainer ) {
+ $this->store = $store;
+ $this->termsLookup = $termsLookup;
+ $this->hierarchyLookup = $hierarchyLookup;
+ $this->servicesContainer = $servicesContainer;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Options $options
+ */
+ public function setOptions( Options $options ) {
+ $this->options = $options;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param string $key
+ *
+ * @return mixed
+ */
+ public function getOption( $key, $default = false ) {
+
+ if ( $this->options === null ) {
+ $this->options = new Options();
+ }
+
+ return $this->options->safeGet( $key, $default );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $sortFields
+ */
+ public function setSortFields( array $sortFields ) {
+ $this->sortFields = $sortFields;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return Store
+ */
+ public function getStore() {
+ return $this->store;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return TermsLookup
+ */
+ public function getTermsLookup() {
+ return $this->termsLookup;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return FieldMapper
+ */
+ public function getFieldMapper() {
+
+ if ( $this->fieldMapper === null ) {
+ $this->fieldMapper = new FieldMapper();
+ }
+
+ return $this->fieldMapper;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param []
+ */
+ public function getQueryInfo() {
+ return $this->queryInfo;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $queryInfo
+ */
+ public function addQueryInfo( array $queryInfo ) {
+ $this->queryInfo[] = $queryInfo;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param []
+ */
+ public function getDescriptionLog() {
+ return $this->descriptionLog;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return array
+ */
+ public function getErrors() {
+ return $this->errors;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param array $error
+ */
+ public function addError( array $error ) {
+ $this->errors[] = $error;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @return integer
+ */
+ public function getID( $dataItem ) {
+
+ if ( $dataItem instanceof DIProperty ) {
+ return (int)$this->store->getObjectIds()->getSMWPropertyID(
+ $dataItem
+ );
+ }
+
+ if ( $dataItem instanceof DIWikiPage ) {
+ return (int)$this->store->getObjectIds()->getSMWPageID(
+ $dataItem->getDBKey(),
+ $dataItem->getNamespace(),
+ $dataItem->getInterWiki(),
+ $dataItem->getSubobjectName()
+ );
+ }
+
+ return 0;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Condition|array $params
+ *
+ * @return Condition
+ */
+ public function newCondition( $params ) {
+ return new Condition( $params );
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Description $description
+ * @param boolean $isConstantScore
+ *
+ * @return array
+ */
+ public function makeFromDescription( Description $description, $isConstantScore = true ) {
+
+ $this->errors = [];
+ $this->queryInfo = [];
+
+ $this->descriptionLog = [];
+ $this->termsLookup->clear();
+
+ if ( $this->fieldMapper === null ) {
+ $this->fieldMapper = new FieldMapper();
+ }
+
+ $this->fieldMapper->isCompatMode(
+ $this->options->safeGet( 'compat.mode', true )
+ );
+
+ // Some notes on the difference between term, match, and query
+ // string:
+ //
+ // - match, term or range queries: look for a particular value in a
+ // particular field
+ // - bool: wrap other leaf or compound queries and are used to combine
+ // multiple queries
+ // - constant_score: simply returns a constant score equal to the query
+ // boost for every document in the filter
+
+ $condition = $this->interpretDescription( $description );
+
+ if ( $condition instanceof Condition ) {
+ $query = $condition->toArray();
+ $this->descriptionLog = $condition->getLogs();
+ } else {
+ $query = $condition;
+ }
+
+ if ( $this->options->safeGet( 'sort.property.must.exists' ) && $this->sortFields !== [] ) {
+ $params = [];
+
+ foreach ( $this->sortFields as $field ) {
+ $params[] = $this->fieldMapper->exists( "$field" );
+ }
+
+ $query = $this->fieldMapper->bool( 'must', [ $query, $params ] );
+ }
+
+ // If we know we don't need any score we turn this into a `constant_score`
+ // query
+ // @see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-constant-score-query.html
+ if ( $isConstantScore ) {
+ $query = $this->fieldMapper->constant_score( $query );
+ }
+
+ return $query;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param DataItem|null $dataItem
+ * @param integer $hierarchyDepth
+ *
+ * @return array
+ */
+ public function findHierarchyMembers( DataItem $dataItem = null, $hierarchyDepth ) {
+
+ $ids = [];
+
+ if ( $dataItem !== null && ( $members = $this->hierarchyLookup->getConsecutiveHierarchyList( $dataItem ) ) !== [] ) {
+
+ if ( $hierarchyDepth !== null ) {
+ $members = $hierarchyDepth == 0 ? [] : array_slice( $members, 0, $hierarchyDepth );
+ }
+
+ foreach ( $members as $member ) {
+ $ids[] = $this->getID( $member );
+ }
+ }
+
+ return $ids;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param Description $description
+ *
+ * @return array
+ */
+ public function interpretDescription( Description $description, $isConjunction = false ) {
+
+ $params = [];
+
+ if ( $this->initServices === false ) {
+ $this->initServices();
+ }
+
+ if ( $description instanceof SomeProperty ) {
+ $params = $this->somePropertyInterpreter->interpretDescription( $description, $isConjunction );
+ }
+
+ if ( $description instanceof ConceptDescription ) {
+ $params = $this->conceptDescriptionInterpreter->interpretDescription( $description, $isConjunction );
+ }
+
+ if ( $description instanceof ClassDescription ) {
+ $params = $this->classDescriptionInterpreter->interpretDescription( $description, $isConjunction );
+ }
+
+ if ( $description instanceof NamespaceDescription ) {
+ $params = $this->namespaceDescriptionInterpreter->interpretDescription( $description, $isConjunction );
+ }
+
+ if ( $description instanceof ValueDescription ) {
+ $params = $this->valueDescriptionInterpreter->interpretDescription( $description, $isConjunction );
+ }
+
+ if ( $description instanceof Conjunction ) {
+ $params = $this->conjunctionInterpreter->interpretDescription( $description, $isConjunction );
+ }
+
+ if ( $description instanceof Disjunction ) {
+ $params = $this->disjunctionInterpreter->interpretDescription( $description, $isConjunction );
+ }
+
+ return $params;
+ }
+
+ /**
+ * @since 3.0
+ *
+ * @param ValueDescription $description
+ * @param array &$options
+ *
+ * @return Condition
+ */
+ public function interpretSomeValue( ValueDescription $description, array &$options ) {
+
+ if ( $this->initServices === false ) {
+ $this->initServices();
+ }
+
+ return $this->someValueInterpreter->interpretDescription( $description, $options );
+ }
+
+ private function initServices() {
+
+ $this->somePropertyInterpreter = $this->servicesContainer->get( 'SomePropertyInterpreter', $this );
+ $this->conceptDescriptionInterpreter = $this->servicesContainer->get( 'ConceptDescriptionInterpreter', $this );
+ $this->classDescriptionInterpreter = $this->servicesContainer->get( 'ClassDescriptionInterpreter', $this );
+ $this->namespaceDescriptionInterpreter = $this->servicesContainer->get( 'NamespaceDescriptionInterpreter', $this );
+ $this->valueDescriptionInterpreter = $this->servicesContainer->get( 'ValueDescriptionInterpreter', $this );
+ $this->conjunctionInterpreter = $this->servicesContainer->get( 'ConjunctionInterpreter', $this );
+ $this->disjunctionInterpreter = $this->servicesContainer->get( 'DisjunctionInterpreter', $this );
+ $this->someValueInterpreter = $this->servicesContainer->get( 'SomeValueInterpreter', $this );
+
+ $this->initServices = true;
+ }
+
+}