Enable supplying inline interwiki results
authorStanislav Malyshev <smalyshev@gmail.com>
Thu, 1 Dec 2016 04:22:59 +0000 (20:22 -0800)
committerStanislav Malyshev <smalyshev@gmail.com>
Tue, 6 Dec 2016 03:11:13 +0000 (19:11 -0800)
"Inline" interwiki results are added as "additionalsearch" section to the results,
if enablerewrites are turned on.

Bug: T142795
Change-Id: I1c14179ee4fb72b6b84772074b6d231d1129ac14

includes/api/ApiQuerySearch.php

index 9962d5e..2351d53 100644 (file)
@@ -169,123 +169,47 @@ class ApiQuerySearch extends ApiQueryGeneratorBase {
 
                        // Silently skip broken and missing titles
                        if ( $result->isBrokenTitle() || $result->isMissingRevision() ) {
-                               $result = $matches->next();
-                               continue;
+                               return null;
                        }
 
-                       $title = $result->getTitle();
                        if ( $resultPageSet === null ) {
-                               $vals = [];
-                               ApiQueryBase::addTitleInfo( $vals, $title );
-
-                               if ( isset( $prop['snippet'] ) ) {
-                                       $vals['snippet'] = $result->getTextSnippet( $terms );
-                               }
-                               if ( isset( $prop['size'] ) ) {
-                                       $vals['size'] = $result->getByteSize();
-                               }
-                               if ( isset( $prop['wordcount'] ) ) {
-                                       $vals['wordcount'] = $result->getWordCount();
-                               }
-                               if ( isset( $prop['timestamp'] ) ) {
-                                       $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $result->getTimestamp() );
-                               }
-                               if ( isset( $prop['titlesnippet'] ) ) {
-                                       $vals['titlesnippet'] = $result->getTitleSnippet();
-                               }
-                               if ( isset( $prop['categorysnippet'] ) ) {
-                                       $vals['categorysnippet'] = $result->getCategorySnippet();
-                               }
-                               if ( !is_null( $result->getRedirectTitle() ) ) {
-                                       if ( isset( $prop['redirecttitle'] ) ) {
-                                               $vals['redirecttitle'] = $result->getRedirectTitle()->getPrefixedText();
-                                       }
-                                       if ( isset( $prop['redirectsnippet'] ) ) {
-                                               $vals['redirectsnippet'] = $result->getRedirectSnippet();
+                               $vals = $this->getSearchResultData( $result, $prop, $terms );
+                               if ( $vals ) {
+                                       // Add item to results and see whether it fits
+                                       $fit = $apiResult->addValue( [ 'query', $this->getModuleName() ], null, $vals );
+                                       if ( !$fit ) {
+                                               $this->setContinueEnumParameter( 'offset', $params['offset'] + $count - 1 );
+                                               break;
                                        }
                                }
-                               if ( !is_null( $result->getSectionTitle() ) ) {
-                                       if ( isset( $prop['sectiontitle'] ) ) {
-                                               $vals['sectiontitle'] = $result->getSectionTitle()->getFragment();
-                                       }
-                                       if ( isset( $prop['sectionsnippet'] ) ) {
-                                               $vals['sectionsnippet'] = $result->getSectionSnippet();
-                                       }
-                               }
-                               if ( isset( $prop['isfilematch'] ) ) {
-                                       $vals['isfilematch'] = $result->isFileMatch();
-                               }
-
-                               // Add item to results and see whether it fits
-                               $fit = $apiResult->addValue( [ 'query', $this->getModuleName() ],
-                                       null, $vals );
-                               if ( !$fit ) {
-                                       $this->setContinueEnumParameter( 'offset', $params['offset'] + $count - 1 );
-                                       break;
-                               }
                        } else {
-                               $titles[] = $title;
+                               $titles[] = $result->getTitle();
                        }
 
                        $result = $matches->next();
                }
 
-               $hasInterwikiResults = false;
-               $totalhits = null;
-               if ( $interwiki && $resultPageSet === null && $matches->hasInterwikiResults() ) {
-                       foreach ( $matches->getInterwikiResults() as $interwikiMatches ) {
-                               $hasInterwikiResults = true;
-
-                               // Include number of results if requested
-                               if ( $resultPageSet === null && isset( $searchInfo['totalhits'] ) ) {
-                                       $totalhits += $interwikiMatches->getTotalHits();
-                               }
-
-                               $result = $interwikiMatches->next();
-                               while ( $result ) {
-                                       $title = $result->getTitle();
-
-                                       if ( $resultPageSet === null ) {
-                                               $vals = [
-                                                       'namespace' => $result->getInterwikiNamespaceText(),
-                                                       'title' => $title->getText(),
-                                                       'url' => $title->getFullURL(),
-                                               ];
-
-                                               // Add item to results and see whether it fits
-                                               $fit = $apiResult->addValue(
-                                                       [ 'query', 'interwiki' . $this->getModuleName(), $result->getInterwikiPrefix() ],
-                                                       null,
-                                                       $vals
-                                               );
-
-                                               if ( !$fit ) {
-                                                       // We hit the limit. We can't really provide any meaningful
-                                                       // pagination info so just bail out
-                                                       break;
-                                               }
-                                       } else {
-                                               $titles[] = $title;
-                                       }
+               // Here we assume interwiki results do not count with
+               // regular search results. We may want to reconsider this
+               // if we ever return a lot of interwiki results or want pagination
+               // for them.
+               // Interwiki results inside main result set
+               $canAddInterwiki = (bool)$params['enablerewrites'] && ( $resultPageSet === null );
+               if ( $canAddInterwiki ) {
+                       $this->addInterwikiResults( $matches, $apiResult, $prop, $terms, 'additional',
+                               SearchResultSet::INLINE_RESULTS );
+               }
 
-                                       $result = $interwikiMatches->next();
-                               }
-                       }
-                       if ( $totalhits !== null ) {
-                               $apiResult->addValue( [ 'query', 'interwikisearchinfo' ],
-                                       'totalhits', $totalhits );
-                       }
+               // Interwiki results outside main result set
+               if ( $interwiki && $resultPageSet === null ) {
+                       $this->addInterwikiResults( $matches, $apiResult, $prop, $terms, 'interwiki',
+                               SearchResultSet::SECONDARY_RESULTS );
                }
 
                if ( $resultPageSet === null ) {
                        $apiResult->addIndexedTagName( [
                                'query', $this->getModuleName()
                        ], 'p' );
-                       if ( $hasInterwikiResults ) {
-                               $apiResult->addIndexedTagName( [
-                                       'query', 'interwiki' . $this->getModuleName()
-                               ], 'p' );
-                       }
                } else {
                        $resultPageSet->setRedirectMergePolicy( function ( $current, $new ) {
                                if ( !isset( $current['index'] ) || $new['index'] < $current['index'] ) {
@@ -301,6 +225,119 @@ class ApiQuerySearch extends ApiQueryGeneratorBase {
                }
        }
 
+       /**
+        * Assemble search result data.
+        * @param SearchResult $result Search result
+        * @param array        $prop Props to extract (as keys)
+        * @param array        $terms Terms list
+        * @return array|null Result data or null if result is broken in some way.
+        */
+       private function getSearchResultData( SearchResult $result, $prop, $terms ) {
+               // Silently skip broken and missing titles
+               if ( $result->isBrokenTitle() || $result->isMissingRevision() ) {
+                       return null;
+               }
+
+               $vals = [];
+
+               $title = $result->getTitle();
+               ApiQueryBase::addTitleInfo( $vals, $title );
+
+               if ( isset( $prop['size'] ) ) {
+                       $vals['size'] = $result->getByteSize();
+               }
+               if ( isset( $prop['wordcount'] ) ) {
+                       $vals['wordcount'] = $result->getWordCount();
+               }
+               if ( isset( $prop['snippet'] ) ) {
+                       $vals['snippet'] = $result->getTextSnippet( $terms );
+               }
+               if ( isset( $prop['timestamp'] ) ) {
+                       $vals['timestamp'] = wfTimestamp( TS_ISO_8601, $result->getTimestamp() );
+               }
+               if ( isset( $prop['titlesnippet'] ) ) {
+                       $vals['titlesnippet'] = $result->getTitleSnippet();
+               }
+               if ( isset( $prop['categorysnippet'] ) ) {
+                       $vals['categorysnippet'] = $result->getCategorySnippet();
+               }
+               if ( !is_null( $result->getRedirectTitle() ) ) {
+                       if ( isset( $prop['redirecttitle'] ) ) {
+                               $vals['redirecttitle'] = $result->getRedirectTitle()->getPrefixedText();
+                       }
+                       if ( isset( $prop['redirectsnippet'] ) ) {
+                               $vals['redirectsnippet'] = $result->getRedirectSnippet();
+                       }
+               }
+               if ( !is_null( $result->getSectionTitle() ) ) {
+                       if ( isset( $prop['sectiontitle'] ) ) {
+                               $vals['sectiontitle'] = $result->getSectionTitle()->getFragment();
+                       }
+                       if ( isset( $prop['sectionsnippet'] ) ) {
+                               $vals['sectionsnippet'] = $result->getSectionSnippet();
+                       }
+               }
+               if ( isset( $prop['isfilematch'] ) ) {
+                       $vals['isfilematch'] = $result->isFileMatch();
+               }
+               return $vals;
+       }
+
+       /**
+        * Add interwiki results as a section in query results.
+        * @param SearchResultSet $matches
+        * @param ApiResult       $apiResult
+        * @param array           $prop Props to extract (as keys)
+        * @param array           $terms Terms list
+        * @param string          $section Section name where results would go
+        * @param int             $type Interwiki result type
+        * @return int|null Number of total hits in the data or null if none was produced
+        */
+       private function addInterwikiResults(
+               SearchResultSet $matches, ApiResult $apiResult, $prop,
+               $terms, $section, $type
+       ) {
+               $totalhits = null;
+               if ( $matches->hasInterwikiResults( $type ) ) {
+                       foreach ( $matches->getInterwikiResults( $type ) as $interwikiMatches ) {
+                               // Include number of results if requested
+                               $totalhits += $interwikiMatches->getTotalHits();
+
+                               $result = $interwikiMatches->next();
+                               while ( $result ) {
+                                       $title = $result->getTitle();
+                                       $vals = $this->getSearchResultData( $result, $prop, $terms );
+
+                                       $vals['namespace'] = $result->getInterwikiNamespaceText();
+                                       $vals['title'] = $title->getText();
+                                       $vals['url'] = $title->getFullURL();
+
+                                       // Add item to results and see whether it fits
+                                       $fit = $apiResult->addValue( [
+                                                       'query',
+                                                       $section . $this->getModuleName(),
+                                                       $result->getInterwikiPrefix()
+                                               ], null, $vals );
+
+                                       if ( !$fit ) {
+                                               // We hit the limit. We can't really provide any meaningful
+                                               // pagination info so just bail out
+                                               break;
+                                       }
+
+                                       $result = $interwikiMatches->next();
+                               }
+                       }
+                       if ( $totalhits !== null ) {
+                               $apiResult->addValue( [ 'query', $section . 'searchinfo' ], 'totalhits', $totalhits );
+                               $apiResult->addIndexedTagName( [
+                                       'query', $section . $this->getModuleName()
+                               ], 'p' );
+                       }
+               }
+               return $totalhits;
+       }
+
        public function getCacheMode( $params ) {
                return 'public';
        }