Search frontend:
authorRobert Stojnić <rainman@users.mediawiki.org>
Sun, 23 Mar 2008 13:43:11 +0000 (13:43 +0000)
committerRobert Stojnić <rainman@users.mediawiki.org>
Sun, 23 Mar 2008 13:43:11 +0000 (13:43 +0000)
* let the backend provide snippets and other info, fill only what is not
  provided
* wrap textual results in a div, should make the snippets look more
  compact and consistent over hits
* added a did you mean.. container
* show total number of hits if available
* added messages for "redirects to article", and "relevant section" hits

includes/SearchEngine.php
includes/SpecialSearch.php
languages/messages/MessagesEn.php
skins/monobook/main.css

index ff93cdf..739e8a1 100644 (file)
@@ -309,13 +309,16 @@ class SearchResultSet {
        }
 
        /**
-        * Some search modes return a suggested alternate term if there are
-        * no exact hits. Check hasSuggestion() first.
-        *
-        * @return string
-        * @access public
+        * @return string suggested query, null if none
+        */
+       function getSuggestionQuery(){
+               return null;
+       }
+       
+       /**
+        * @return string highlighted suggested query, '' if none
         */
-       function getSuggestion() {
+       function getSuggestionSnippet(){
                return '';
        }
 
@@ -370,6 +373,69 @@ class SearchResult {
        function getScore() {
                return null;
        }
+       
+       /**
+        * @return string highlighted text snippet, null if not supported 
+        */
+       function getTextSnippet(){
+               return null;            
+       }
+       
+       /**
+        * @return string highlighted title, '' if not supported
+        */
+       function getTitleSnippet(){
+               return '';
+       }
+       
+       /**
+        * @return string highlighted redirect name (redirect to this page), '' if none or not supported
+        */
+       function getRedirectSnippet(){
+               return '';
+       }
+       
+       /**
+        * @return Title object for the redirect to this page, null if none or not supported
+        */
+       function getRedirectTitle(){
+               return null;
+       }
+       
+       /**
+        * @return string highlighted relevant section name, null if none or not supported
+        */
+       function getSectionSnippet(){
+               return '';
+       }
+       
+       /**
+        * @return Title object (pagename+fragment) for the section, null if none or not supported 
+        */
+       function getSectionTitle(){
+               return null;
+       }
+       
+       /**
+        * @return string timestamp, null if not supported
+        */
+       function getTimestamp(){
+               return null;
+       }
+       
+       /**
+        * @return int number of words, null if not supported
+        */
+       function getWordCount(){
+               return null;
+       }
+       
+       /**
+        * @return int size in bytes, null if not supported
+        */
+       function getByteSize(){
+               return null;
+       }
 }
 
 /**
index d914454..434ea33 100644 (file)
@@ -171,11 +171,29 @@ class SpecialSearch {
                        return;
                }
                $textMatches = $search->searchText( $term );
+               
+               // did you mean...
+               if($textMatches && $textMatches->hasSuggestion()){
+                       global $wgScript;
+                       $fulltext = htmlspecialchars(wfMsg('search'));
+                       $suggestLink = '<a href="'.$wgScript.'?title=Special:Search&amp;search='.
+                               urlencode($textMatches->getSuggestionQuery()).'&amp;fulltext='.$fulltext.'">'
+                               .$textMatches->getSuggestionSnippet().'</a>';
+                       $wgOut->addHTML('<div class="searchdidyoumean">'.wfMsg('search-suggest',$suggestLink).'</div>');
+               } 
+               
 
                $num = ( $titleMatches ? $titleMatches->numRows() : 0 )
                        + ( $textMatches ? $textMatches->numRows() : 0);
+               $totalNum = 0;
+               if($titleMatches && !is_null($titleMatches->getTotalHits()))
+                       $totalNum += $titleMatches->getTotalHits();
+               if($textMatches && !is_null($textMatches->getTotalHits()))
+                       $totalNum += $textMatches->getTotalHits();
                if ( $num > 0 ) {
-                       if ( $num >= $this->limit ) {
+                       if ( $totalNum > 0 ){
+                               $top = wfMsgExt('showingresultstotal',array( 'parseinline' ), $this->offset+1, $this->offset+$num, $totalNum);
+                       } elseif ( $num >= $this->limit ) {
                                $top = wfShowingResults( $this->offset, $this->limit );
                        } else {
                                $top = wfShowingResultsNum( $this->offset, $this->limit, $num );
@@ -208,7 +226,10 @@ class SpecialSearch {
 
                if( $textMatches ) {
                        if( $textMatches->numRows() ) {
-                               $wgOut->wrapWikiMsg( "==$1==\n", 'textmatches' );
+                               if($titleMatches) 
+                                       $wgOut->wrapWikiMsg( "==$1==\n", 'textmatches' );
+                               else // if no title matches the heading is redundant
+                                       $wgOut->addHTML("<hr/>");
                                $wgOut->addHTML( $this->showMatches( $textMatches ) );
                        } elseif( $num == 0 ) {
                                # Don't show the 'no text matches' if we received title matches
@@ -341,8 +362,8 @@ class SpecialSearch {
                //$contextlines = $wgUser->getOption( 'contextlines',  5 );
                $contextlines = 2; // Hardcode this. Old defaults sucked. :)
                $contextchars = $wgUser->getOption( 'contextchars', 50 );
-
-               $link = $sk->makeKnownLinkObj( $t );
+               
+               $link = $sk->makeKnownLinkObj( $t, $result->getTitleSnippet());
 
                //If page content is not readable, just return the title.
                //This is not quite safe, but better than showing excerpts from non-readable pages
@@ -360,12 +381,6 @@ class SpecialSearch {
                                htmlspecialchars( $t->getPrefixedText() ) . "-->\n";
                }
                
-               $text = $revision->getText();
-               $size = wfMsgExt( 'search-result-size', array( 'parsemag', 'escape' ),
-                       $sk->formatSize( strlen( $text ) ),
-                       str_word_count( $text ) );
-               $date = $wgLang->timeanddate( $revision->getTimestamp() );
-               
                if( is_null( $result->getScore() ) ) {
                        // Search engine doesn't report scoring info
                        $score = '';
@@ -374,8 +389,51 @@ class SpecialSearch {
                        $score = wfMsg( 'search-result-score', $wgLang->formatNum( $percent ) )
                                . ' - ';
                }
-
-               $extract = $this->extractText( $text, $terms, $contextlines, $contextchars );
+               
+               // try to fetch everything from the search engine backend
+               // then fill-in what couldn't be fetched
+               $extract = $result->getTextSnippet();
+               $byteSize = $result->getByteSize();
+               $wordCount = $result->getWordCount();
+               $timestamp = $result->getTimestamp();
+               $redirectTitle = $result->getRedirectTitle();
+               $redirectText = $result->getRedirectSnippet();
+               $sectionTitle = $result->getSectionTitle();
+               $sectionText = $result->getSectionSnippet();
+
+               // fallback
+               if( is_null($extract) || is_null($wordCount) || is_null($byteSize) ){
+                       $text = $revision->getText();
+                       if( is_null($extract) )
+                               $extract = $this->extractText( $text, $terms, $contextlines, $contextchars );
+                       if( is_null($byteSize) )
+                               $byteSize = strlen( $text );
+                       if( is_null($wordCount) )
+                               $wordCount = str_word_count( $text );
+               }
+               if( is_null($timestamp) ){
+                       $timestamp = $revision->getTimestamp();
+               }
+               
+               // format description           
+               $size = wfMsgExt( 'search-result-size', array( 'parsemag', 'escape' ),
+                       $sk->formatSize( $byteSize ),
+                       $wordCount );
+               $date = $wgLang->timeanddate( $timestamp );
+               
+               // format redirects / sections
+               $redirect = '';
+               if( !is_null($redirectTitle) )
+                       $redirect = "<span class='searchalttitle'>"
+                               .wfMsg('search-redirect',$sk->makeKnownLinkObj( $redirectTitle, $redirectText))
+                               ."</span>";
+               $section = '';
+               if( !is_null($sectionTitle) )
+                       $section = "<span class='searchalttitle'>" 
+                               .wfMsg('search-section', $sk->makeKnownLinkObj( $sectionTitle, $sectionText))
+                               ."</span>";
+               // wrap extract
+               $extract = "<div class='searchresult'>".$extract."</div>";
                
                // Include a thumbnail for media files...
                if( $t->getNamespace() == NS_IMAGE ) {
@@ -408,7 +466,7 @@ class SpecialSearch {
                }
 
                wfProfileOut( $fname );
-               return "<li>{$link} {$extract}\n" .
+               return "<li>{$link} {$redirect} {$section} {$extract}\n" .
                        "<div class='mw-search-result-data'>{$score}{$size} - {$date}</div>" .
                        "</li>\n";
 
@@ -437,12 +495,12 @@ class SpecialSearch {
                                continue;
                        }
                        --$contextlines;
-                       $pre = $wgContLang->truncate( $m[1], -$contextchars, '...' );
+                       $pre = $wgContLang->truncate( $m[1], -$contextchars, ' ... ' );
 
                        if ( count( $m ) < 3 ) {
                                $post = '';
                        } else {
-                               $post = $wgContLang->truncate( $m[3], $contextchars, '...' );
+                               $post = $wgContLang->truncate( $m[3], $contextchars, ' ... ' );
                        }
 
                        $found = $m[2];
@@ -452,7 +510,7 @@ class SpecialSearch {
                        $line = preg_replace( $pat2,
                          "<span class='searchmatch'>\\1</span>", $line );
 
-                       $extract .= "<br /><small>{$line}</small>\n";
+                       $extract .= "${line}\n";
                }
                wfProfileOut( "$fname-extract" );
                
index 86e34fd..31221c1 100644 (file)
@@ -1231,8 +1231,12 @@ Make sure that this change will maintain historical page continuity.
 'viewprevnext'          => 'View ($1) ($2) ($3)',
 'search-result-size'    => '$1 ({{PLURAL:$2|1 word|$2 words}})',
 'search-result-score'   => 'Relevance: $1%',
+'search-redirect'       => '(redirect $1)',
+'search-section'        => '(section $1)',
+'search-suggest'        => 'Did you mean: $1',
 'showingresults'        => "Showing below up to {{PLURAL:$1|'''1''' result|'''$1''' results}} starting with #'''$2'''.",
 'showingresultsnum'     => "Showing below {{PLURAL:$3|'''1''' result|'''$3''' results}} starting with #'''$2'''.",
+'showingresultstotal'   => "Showing below results '''$1 - $2''' of '''$3'''",
 'nonefound'             => "'''Note''': Unsuccessful searches are often caused by searching for common words like \"have\" and \"from\", which are not indexed, or by specifying more than one search term (only pages containing all of the search terms will appear in the result).",
 'powersearch'           => 'Advanced search',
 'powersearch-legend'    => 'Advanced search',
index ba53bf2..b3908f8 100644 (file)
@@ -1184,8 +1184,23 @@ span.unpatrolled {
 }
 
 span.searchmatch {
-       color: red;
+       font-size: 95%; 
+}
+div.searchresult {
+       font-size: 95%;
+       width:38em;
+}
+
+span.searchalttitle {
+       font-size: 95%;
 }
+
+div.searchdidyoumean {
+       font-size: 127%;
+       padding-bottom:1ex;
+       padding-top:1ex; 
+}
+
 .sharedUploadNotice {
        font-style: italic;
 }