jenkins-bot has submitted this change and it was merged. ( 
https://gerrit.wikimedia.org/r/340307 )

Change subject: If search term is a entity ID add known repository prefixes
......................................................................


If search term is a entity ID add known repository prefixes

If entity ID is given, prefixes of repositories configured
as providing entities of the given type are prepended to
the given search term, and a resulting entity ID is search
in respective repositories.

In particular, it is possible to search for entity ID
provided by the foreign repository without providing
the repository prefix in the search term.

If multiple repositories provided entity of the given type,
and accordingly prefixed matches were found in several
repositories, all relevant matches will be returned.
As of January 2017 single entity type is only provided by
single repository but the code in EntitySearchHelper
would be able to handle the multi-repo case if this
was changed.

Bug: T158169
Change-Id: I2405dcdc2cedaf17b1df363bf7ea2918a338b909
---
M repo/Wikibase.php
M repo/includes/Api/EntitySearchHelper.php
M repo/includes/WikibaseRepo.php
M repo/tests/phpunit/includes/Api/EntitySearchHelperTest.php
M repo/tests/phpunit/includes/WikibaseRepoTest.php
5 files changed, 148 insertions(+), 22 deletions(-)

Approvals:
  Daniel Kinzler: Looks good to me, approved
  WMDE-leszek: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/repo/Wikibase.php b/repo/Wikibase.php
index 4375ce6..5236193 100644
--- a/repo/Wikibase.php
+++ b/repo/Wikibase.php
@@ -216,7 +216,8 @@
                                        $repo->getTermLookup(),
                                        $repo->getLanguageFallbackChainFactory()
                                                ->newFromLanguage( 
$repo->getUserLanguage() )
-                               )
+                               ),
+                               $repo->getEntityTypeToRepositoryMapping()
                        );
 
                        return new Wikibase\Repo\Api\SearchEntities(
@@ -630,7 +631,8 @@
                                        $repo->getTermLookup(),
                                        $repo->getLanguageFallbackChainFactory()
                                                ->newFromLanguage( 
$apiQuery->getLanguage() )
-                               )
+                               ),
+                               $repo->getEntityTypeToRepositoryMapping()
                        );
 
                        return new Wikibase\Repo\Api\QuerySearchEntities(
diff --git a/repo/includes/Api/EntitySearchHelper.php 
b/repo/includes/Api/EntitySearchHelper.php
index 848cf84..3231684 100644
--- a/repo/includes/Api/EntitySearchHelper.php
+++ b/repo/includes/Api/EntitySearchHelper.php
@@ -41,16 +41,32 @@
         */
        private $entityLookup;
 
+       /**
+        * @var string[] Associative array mapping entity type names (strings) 
to names of repositories providing
+        *               entities of this type.
+        */
+       private $entityTypeToRepositoryMapping;
+
+       /**
+        * @param EntityLookup $entityLookup
+        * @param EntityIdParser $idParser
+        * @param ConfigurableTermSearchInteractor $termSearchInteractor
+        * @param LabelDescriptionLookup $labelDescriptionLookup
+        * @param string[] $entityTypeToRepositoryMapping Associative array 
mapping entity type names (strings) to
+        *        names of repositories providing entities of this type.
+        */
        public function __construct(
                EntityLookup $entityLookup,
                EntityIdParser $idParser,
                ConfigurableTermSearchInteractor $termSearchInteractor,
-               LabelDescriptionLookup $labelDescriptionLookup
+               LabelDescriptionLookup $labelDescriptionLookup,
+               array $entityTypeToRepositoryMapping
        ) {
                $this->entityLookup = $entityLookup;
                $this->idParser = $idParser;
                $this->termSearchInteractor = $termSearchInteractor;
                $this->labelDescriptionLookup = $labelDescriptionLookup;
+               $this->entityTypeToRepositoryMapping = 
$entityTypeToRepositoryMapping;
        }
 
        /**
@@ -67,9 +83,9 @@
        public function getRankedSearchResults( $text, $languageCode, 
$entityType, $limit, $strictLanguage ) {
                $allSearchResults = [];
 
-               // If $text is the ID of an existing item, include it in the 
result.
-               $entityId = $this->getExactMatchForEntityId( $text, $entityType 
);
-               if ( $entityId !== null ) {
+               // If $text is the ID of an existing item (with repository 
prefix or without), include it in the result.
+               $entityIds = $this->getEntityIdsMatchingSearchTerm( $text, 
$entityType );
+               foreach ( $entityIds as $entityId ) {
                        // This is nothing to do with terms, but make it look a 
normal result so everything is easier
                        $displayTerms = $this->getDisplayTerms( $entityId );
                        $allSearchResults[$entityId->getSerialization()] = new 
TermSearchResult(
@@ -113,38 +129,81 @@
        }
 
        /**
-        * Gets exact match for the search term as an EntityId if it can be 
found.
+        * Returns EntityIds matching the search term (possibly with some 
repository prefix).
+        * If search term is a serialized entity id of the requested type, and 
multiple repositories provide
+        * entities of the type, prefixes of each of repositories are added to 
the search term and those repositories
+        * are searched for the result entity ID. If such concatenated entity 
IDs are found in several respective
+        * repositories, this returns all relevant matches.
         *
         * @param string $term
         * @param string $entityType
         *
-        * @return EntityId|null
+        * @return EntityId[]
         */
-       private function getExactMatchForEntityId( $term, $entityType ) {
+       private function getEntityIdsMatchingSearchTerm( $term, $entityType ) {
                try {
                        $entityId = $this->idParser->parse( trim( $term ) );
                } catch ( EntityIdParsingException $ex ) {
                        // Extract the last (ASCII-only) word. This covers URIs 
and input strings like "(Q42)".
                        if ( !preg_match( '/.*(\b\w{2,})/s', $term, $matches ) 
) {
-                               return null;
+                               return [];
                        }
 
                        try {
                                $entityId = $this->idParser->parse( $matches[1] 
);
                        } catch ( EntityIdParsingException $ex ) {
-                               return null;
+                               return [];
                        }
                }
 
                if ( $entityId->getEntityType() !== $entityType ) {
-                       return null;
+                       return [];
                }
 
-               if ( !$this->entityLookup->hasEntity( $entityId ) ) {
-                       return null;
+               return $this->getMatchingIdsIncludingPrefixes( $entityId );
+       }
+
+       /**
+        * Returns a list of entity IDs matching the pattern defined by 
$entityId: existing entities
+        * of type of $entityId, and serialized id equal to $entityId, possibly 
including prefixes
+        * of configured repositories.
+        *
+        * @param EntityId $entityId
+        *
+        * @return EntityId[]
+        */
+       private function getMatchingIdsIncludingPrefixes( EntityId $entityId ) {
+               $entityIds = [];
+
+               if ( $this->entityLookup->hasEntity( $entityId ) ) {
+                       $entityIds[] = $entityId;
                }
 
-               return $entityId;
+               // Entity ID without repository prefix, let's try prepending 
known prefixes
+               $entityType = $entityId->getEntityType();
+               $unprefixedIdPart = $entityId->getLocalPart();
+
+               if ( !array_key_exists( $entityType, 
$this->entityTypeToRepositoryMapping ) ) {
+                       return $entityIds;
+               }
+
+               $repositoryPrefix = 
$this->entityTypeToRepositoryMapping[$entityType];
+
+               try {
+                       $id = $this->idParser->parse( 
EntityId::joinSerialization( [
+                               $repositoryPrefix,
+                               '',
+                               $unprefixedIdPart
+                       ] ) );
+               } catch ( EntityIdParsingException $ex ) {
+                       return [];
+               }
+
+               if ( $this->entityLookup->hasEntity( $id ) ) {
+                       $entityIds[] = $id;
+               }
+
+               return $entityIds;
        }
 
        /**
diff --git a/repo/includes/WikibaseRepo.php b/repo/includes/WikibaseRepo.php
index c611c3b..0a4d922 100644
--- a/repo/includes/WikibaseRepo.php
+++ b/repo/includes/WikibaseRepo.php
@@ -270,7 +270,7 @@
        /**
         * @var RepositoryDefinitions
         */
-       private $repositoryDefinitons;
+       private $repositoryDefinitions;
 
        /**
         * @var ValueSnakRdfBuilderFactory
@@ -405,7 +405,7 @@
                        $this->getVocabularyBaseUri(),
                        $this->getMonolingualTextLanguages(),
                        $this->getCachingCommonsMediaFileNameLookup(),
-                       
$this->repositoryDefinitons->getEntityTypesPerRepository(),
+                       
$this->repositoryDefinitions->getEntityTypesPerRepository(),
                        new MediaWikiPageNameNormalizer(),
                        $this->settings->getSetting( 
'geoShapeStorageApiEndpointUrl' )
                );
@@ -518,7 +518,7 @@
                $this->settings = $settings;
                $this->dataTypeDefinitions = $dataTypeDefinitions;
                $this->entityTypeDefinitions = $entityTypeDefinitions;
-               $this->repositoryDefinitons = $repositoryDefinitions;
+               $this->repositoryDefinitions = $repositoryDefinitions;
                $this->entityDataRetrievalServiceFactory = 
$entityDataRetrievalServiceFactory;
        }
 
@@ -1306,7 +1306,7 @@
         *  entity types from the configured foreign repositories.
         */
        public function getEnabledEntityTypes() {
-               return $this->repositoryDefinitons->getAllEntityTypes();
+               return $this->repositoryDefinitions->getAllEntityTypes();
        }
 
        /**
@@ -1905,4 +1905,14 @@
                return $this->entityRdfBuilderFactory;
        }
 
+       /**
+        * @return string[] Associative array mapping names of known entity 
types (strings) to names of
+        *         repositories providing entities of those types.
+        *         Note: Currently entities of a given type are only provided 
by single repository. This
+        *         assumption can be changed in the future.
+        */
+       public function getEntityTypeToRepositoryMapping() {
+               return 
$this->repositoryDefinitions->getEntityTypeToRepositoryMapping();
+       }
+
 }
diff --git a/repo/tests/phpunit/includes/Api/EntitySearchHelperTest.php 
b/repo/tests/phpunit/includes/Api/EntitySearchHelperTest.php
index 54dbc18..87b2d4c 100644
--- a/repo/tests/phpunit/includes/Api/EntitySearchHelperTest.php
+++ b/repo/tests/phpunit/includes/Api/EntitySearchHelperTest.php
@@ -13,6 +13,7 @@
 use Wikibase\Lib\Interactors\ConfigurableTermSearchInteractor;
 use Wikibase\Lib\Interactors\TermSearchOptions;
 use Wikibase\Lib\Interactors\TermSearchResult;
+use Wikibase\Lib\RepositoryDefinitions;
 use Wikibase\Repo\Api\EntitySearchHelper;
 use Wikibase\TermIndexEntry;
 
@@ -30,6 +31,7 @@
        const EXISTING_LOCAL_ITEM = 'Q111';
        const FOREIGN_REPO_PREFIX = 'foreign';
        const EXISTING_FOREIGN_ITEM = 'foreign:Q2';
+       const EXISTING_FOREIGN_ITEM_WITHOUT_REPOSITORY_PREFIX = 'Q2';
        const DEFAULT_LANGUAGE = 'pt';
        const DEFAULT_LABEL = 'ptLabel';
        const DEFAULT_DESCRIPTION = 'ptDescription';
@@ -94,8 +96,10 @@
                return $mock;
        }
 
-       private function newEntitySearchHelper( 
ConfigurableTermSearchInteractor $searchInteractor ) {
-
+       private function newEntitySearchHelper(
+               ConfigurableTermSearchInteractor $searchInteractor,
+               array $entityTypeToRepositoryMapping = []
+       ) {
                $localEntityLookup = new InMemoryEntityLookup();
                $localEntityLookup->addEntity( new Item( new ItemId( 
self::EXISTING_LOCAL_ITEM ) ) );
 
@@ -113,7 +117,8 @@
                        $entityLookup,
                        new ItemIdParser(),
                        $searchInteractor,
-                       $this->getMockLabelDescriptionLookup()
+                       $this->getMockLabelDescriptionLookup(),
+                       $entityTypeToRepositoryMapping
                );
        }
 
@@ -257,4 +262,36 @@
                $this->assertEquals( $expected, $results );
        }
 
+       public function 
testGivenEntityIdWithoutRepositoryPrefix_entityIsFound() {
+               $expectedResults = [
+                       self::EXISTING_FOREIGN_ITEM => new TermSearchResult(
+                               new Term( 'qid', self::EXISTING_FOREIGN_ITEM ),
+                               'entityId',
+                               new ItemId( self::EXISTING_FOREIGN_ITEM ),
+                               new Term( self::DEFAULT_LANGUAGE, 
self::DEFAULT_LABEL ),
+                               new Term( self::DEFAULT_LANGUAGE, 
self::DEFAULT_DESCRIPTION )
+                       )
+               ];
+
+               $mockSearchInteractor = $this->getMock( 
ConfigurableTermSearchInteractor::class );
+               $mockSearchInteractor->method( 'searchForEntities' )
+                       ->will( $this->returnValue( [] ) );
+
+               $entitySearchHelper = $this->newEntitySearchHelper(
+                       $mockSearchInteractor,
+                       [ 'item' => 'foreign' ]
+               );
+
+               $this->assertEquals(
+                       $expectedResults,
+                       $entitySearchHelper->getRankedSearchResults(
+                               
self::EXISTING_FOREIGN_ITEM_WITHOUT_REPOSITORY_PREFIX,
+                               'en',
+                               'item',
+                               10,
+                               false
+                       )
+               );
+       }
+
 }
diff --git a/repo/tests/phpunit/includes/WikibaseRepoTest.php 
b/repo/tests/phpunit/includes/WikibaseRepoTest.php
index 543da0a..ed88102 100644
--- a/repo/tests/phpunit/includes/WikibaseRepoTest.php
+++ b/repo/tests/phpunit/includes/WikibaseRepoTest.php
@@ -703,4 +703,22 @@
                $this->assertEquals( $expected, $deserialized );
        }
 
+       public function testGetEntityTypeToRepositoryMapping() {
+               $wikibaseRepo = 
$this->getWikibaseRepoWithCustomRepositoryDefinitions( array_merge(
+                       $this->getRepositoryDefinition( '', [ 'entity-types' => 
[ 'foo', 'bar' ] ] ),
+                       $this->getRepositoryDefinition( 'repo1', [ 
'entity-types' => [ 'baz' ] ] ),
+                       $this->getRepositoryDefinition( 'repo2', [ 
'entity-types' => [ 'foobar' ] ] )
+               ) );
+
+               $this->assertEquals(
+                       [
+                               'foo' => '',
+                               'bar' => '',
+                               'baz' => 'repo1',
+                               'foobar' => 'repo2',
+                       ],
+                       $wikibaseRepo->getEntityTypeToRepositoryMapping()
+               );
+       }
+
 }

-- 
To view, visit https://gerrit.wikimedia.org/r/340307
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I2405dcdc2cedaf17b1df363bf7ea2918a338b909
Gerrit-PatchSet: 11
Gerrit-Project: mediawiki/extensions/Wikibase
Gerrit-Branch: master
Gerrit-Owner: WMDE-leszek <[email protected]>
Gerrit-Reviewer: Addshore <[email protected]>
Gerrit-Reviewer: Aleksey Bekh-Ivanov (WMDE) <[email protected]>
Gerrit-Reviewer: Daniel Kinzler <[email protected]>
Gerrit-Reviewer: Thiemo Mättig (WMDE) <[email protected]>
Gerrit-Reviewer: WMDE-leszek <[email protected]>
Gerrit-Reviewer: jenkins-bot <>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to