jenkins-bot has submitted this change and it was merged.
Change subject: Add an initial scoring function
......................................................................
Add an initial scoring function
This function tries to build a score that reflects the quality of a page.
Bug: T106128
Change-Id: I1f61bb66bcf8fff4b02b31ac9fe8525b85fe9ea5
---
M autoload.php
M includes/BuildDocument/SuggestBuilder.php
M includes/BuildDocument/SuggestScoring.php
M includes/Searcher.php
M maintenance/updateSuggesterIndex.php
A tests/unit/SuggestScoringTest.php
6 files changed, 484 insertions(+), 14 deletions(-)
Approvals:
Cindy-the-browser-test-bot: Looks good to me, but someone else must approve
EBernhardson: Looks good to me, approved
jenkins-bot: Verified
diff --git a/autoload.php b/autoload.php
index df5da06..71d764a 100644
--- a/autoload.php
+++ b/autoload.php
@@ -17,6 +17,7 @@
'CirrusSearch\\BuildDocument\\PageDataBuilder' => __DIR__ .
'/includes/BuildDocument/PageDataBuilder.php',
'CirrusSearch\\BuildDocument\\PageTextBuilder' => __DIR__ .
'/includes/BuildDocument/PageTextBuilder.php',
'CirrusSearch\\BuildDocument\\ParseBuilder' => __DIR__ .
'/includes/BuildDocument/Builder.php',
+ 'CirrusSearch\\BuildDocument\\QualityScore' => __DIR__ .
'/includes/BuildDocument/SuggestScoring.php',
'CirrusSearch\\BuildDocument\\RedirectsAndIncomingLinks' => __DIR__ .
'/includes/BuildDocument/RedirectsAndIncomingLinks.php',
'CirrusSearch\\BuildDocument\\SuggestBuilder' => __DIR__ .
'/includes/BuildDocument/SuggestBuilder.php',
'CirrusSearch\\BuildDocument\\SuggestScoringMethod' => __DIR__ .
'/includes/BuildDocument/SuggestScoring.php',
diff --git a/includes/BuildDocument/SuggestBuilder.php
b/includes/BuildDocument/SuggestBuilder.php
index 8ad11ea..4188095 100644
--- a/includes/BuildDocument/SuggestBuilder.php
+++ b/includes/BuildDocument/SuggestBuilder.php
@@ -1,7 +1,6 @@
<?php
namespace CirrusSearch\BuildDocument;
-use SuggestScoringMethod;
/**
* Build a doc ready for the titlesuggest index.
@@ -22,24 +21,27 @@
* http://www.gnu.org/copyleft/gpl.html
*/
+/**
+ * Builder used to create suggester docs
+ */
class SuggestBuilder {
const MAX_INPUT_LENGTH = 50;
/**
- * $scoringMethod the scoring function
+ * @var SuggestScoringMethod the scoring function
*/
private $scoringMethod;
/**
- * @param SuggestScoringMethod
+ * @param SuggestScoringMethod $scoringMethod the scoring function to
use
*/
- public function __construct( $scoringMethod ) {
+ public function __construct( SuggestScoringMethod $scoringMethod ) {
$this->scoringMethod = $scoringMethod;
}
/**
* @param int $id the page id
- * @param $inputDoc the page data
+ * @param array $inputDoc the page data
* @return array a set of suggest documents
*/
public function build( $id, $inputDoc ) {
@@ -75,11 +77,15 @@
'context' => $location
);
}
-
return array( $doc );
}
- public function buildInputs( $input ) {
+ /**
+ * @param array $input Document to build inputs for
+ * @return array list of prepared suggestions that should
+ * resolve to the document.
+ */
+ public function buildInputs( array $input ) {
$inputs = array( $this->prepareInput( $input['title'] ) );
foreach ( $input['redirect'] as $redir ) {
$inputs[] = $this->prepareInput( $redir['title'] );
@@ -87,6 +93,11 @@
return $inputs;
}
+ /**
+ * @param string $input A page title
+ * @return string A page title short enough to not cause indexing
+ * issues.
+ */
public function prepareInput( $input ) {
if ( mb_strlen( $input ) > self::MAX_INPUT_LENGTH ) {
$input = mb_substr( $input, 0, self::MAX_INPUT_LENGTH );
diff --git a/includes/BuildDocument/SuggestScoring.php
b/includes/BuildDocument/SuggestScoring.php
index af7068c..b025b72 100644
--- a/includes/BuildDocument/SuggestScoring.php
+++ b/includes/BuildDocument/SuggestScoring.php
@@ -2,6 +2,8 @@
namespace CirrusSearch\BuildDocument;
+use CirrusSearch\Searcher;
+
/**
* Scoring methods used by the completion suggester
*
@@ -23,16 +25,17 @@
* http://www.gnu.org/copyleft/gpl.html
*/
-
class SuggestScoringMethodFactory {
/**
* @param $scoringMethods string the name of the scoring method
* @return SuggestScoringMethod
*/
- public static function getScoringMethod( $scoringMethod ) {
+ public static function getScoringMethod( $scoringMethod, $maxDocs ) {
switch( $scoringMethod ) {
case 'incomingLinks':
- return new IncomingsLinksScoringMethod();
+ return new IncomingsLinksScoringMethod( $maxDocs );
+ case 'quality':
+ return new QualityScore( $maxDocs );
}
throw new \Exception( 'Unknown scoring method ' .
$scoringMethod );
}
@@ -51,7 +54,174 @@
* Very simple scoring method based on incoming links
*/
class IncomingsLinksScoringMethod implements SuggestScoringMethod {
+ /**
+ * Constructor
+ * @param integer $maxDocs the number of docs in the index
+ */
+ public function __construct( $maxDocs ) {
+ // This scoring function is very simple and we
+ // don't need to normalize
+ }
+
+ /**
+ * {@inheritDoc}
+ */
public function score( $doc ) {
- return $doc['incoming_links'];
+ return isset( $doc['incoming_links'] ) ? $doc['incoming_links']
: 0;
+ }
+}
+
+/**
+ * Score that tries to reflect the quality of a page
+ */
+class QualityScore implements SuggestScoringMethod {
+ // TODO: move these constants into a cirrus profile
+ const INCOMING_LINKS_MAX_DOCS_FACTOR = 0.1;
+
+ const EXTERNAL_LINKS_NORM = 1000;
+ const PAGE_SIZE_NORM = 300000;
+ const HEADING_NORM = 50;
+ const REDIRECT_NORM = 100;
+
+ const INCOMING_LINKS_WEIGHT = 0.6;
+ const EXTERNAL_LINKS_WEIGHT = 0.3;
+ const PAGE_SIZE_WEIGHT = 0.1;
+ const HEADING_WEIGHT = 0.2;
+ const REDIRECT_WEIGHT = 0.1;
+
+ // The final score will be in the range [0, SCORE_RANGE]
+ const SCORE_RANGE = 100000;
+
+ /**
+ * Template boosts configured by the mediawiki admin.
+ * @var array of key values, key is the template and value is a float
+ */
+ private $boostTemplates;
+
+ /**
+ * @var integer the number of docs in the index
+ */
+ private $maxDocs;
+
+ /**
+ * @var integer normalisation factor for incoming links
+ */
+ private $incomingLinksNorm;
+
+ /**
+ * @param integer $maxDocs the number of docs in the index
+ * @param array of key values, key is the template name, value the
boost factor.
+ * Defaults to Searcher::getDefaultBoostTemplates()
+ */
+ public function __construct( $maxDocs, $boostTemplates = null ) {
+ $this->maxDocs = $maxDocs;
+ $this->boostTemplates = $boostTemplates ?:
Searcher::getDefaultBoostTemplates();
+ // We normalize incoming links according to the size of the
index
+ $this->incomingLinksNorm = (int) ($maxDocs *
self::INCOMING_LINKS_MAX_DOCS_FACTOR);
+ if ( $this->incomingLinksNorm < 1 ) {
+ // it's a very small wiki let's force the norm to 1
+ $this->incomingLinksNorm = 1;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public function score( $doc ) {
+ $incLinks = $this->scoreNormL2( isset( $doc['incoming_links'] )
? $doc['incoming_links'] : 0, $this->incomingLinksNorm );
+ $extLinks = $this->scoreNormL2( isset( $doc['external_link'] )
? count( $doc['external_link'] ) : 0, self::EXTERNAL_LINKS_NORM );
+ $pageSize = $this->scoreNormL2( isset( $doc['text_bytes'] ) ?
$doc['text_bytes'] : 0, self::PAGE_SIZE_NORM );
+ $headings = $this->scoreNorm( isset( $doc['heading'] ) ? count(
$doc['heading'] ) : 0, self::HEADING_NORM );
+ $redirects = $this->scoreNorm( isset( $doc['redirect'] ) ?
count( $doc['redirect'] ) : 0, self::REDIRECT_NORM );
+
+ $score = $incLinks * self::INCOMING_LINKS_WEIGHT;
+
+ $score += $extLinks * self::EXTERNAL_LINKS_WEIGHT;
+ $score += $pageSize * self::PAGE_SIZE_WEIGHT;
+ $score += $headings * self::HEADING_WEIGHT;
+ $score += $redirects * self::REDIRECT_WEIGHT;
+
+ // We have a standardized composite score between 0 and 1
+ $score /= self::INCOMING_LINKS_WEIGHT +
self::EXTERNAL_LINKS_WEIGHT + self::PAGE_SIZE_WEIGHT + self::HEADING_WEIGHT +
self::REDIRECT_WEIGHT;
+
+ $score = $this->boostTemplates( $doc, $score );
+
+ return intval( $score * self::SCORE_RANGE );
+ }
+
+ /**
+ * log2( ( value / norm ) + 1 ) => [0-1]
+ *
+ * @param float $value
+ * @param float $norm
+ * @return float between 0 and 1
+ */
+ public function scoreNormL2( $value, $norm ) {
+ return log( $value > $norm ? 2 : ( $value / $norm ) + 1, 2 );
+ }
+
+ /**
+ * value / norm => [0-1]
+ *
+ * @param float $value
+ * @param float $norm
+ * @return float between 0 and 1
+ */
+ public function scoreNorm( $value, $norm ) {
+ return $value > $norm ? 1 : $value / $norm;
+ }
+
+ /**
+ * Modify an existing score based on templates contained
+ * by the document.
+ *
+ * @param array $doc Document score is generated for
+ * @param float $score Current score between 0 and 1
+ * @return float Score after boosting templates
+ */
+ public function boostTemplates( $doc, $score ) {
+ if ( !isset( $doc['template'] ) ) {
+ return $score;
+ }
+
+ if ( $this->boostTemplates ) {
+ $boost = 1;
+ // compute the global boost
+ foreach ( $this->boostTemplates as $k => $v ) {
+ if ( in_array( $k, $doc['template'] ) ) {
+ $boost *= $v;
+ }
+ }
+ if ( $boost != 1 ) {
+ return $this->boost( $score, $boost );
+ }
+ }
+ return $score;
+ }
+
+ /**
+ * Boost the score :
+ * boost value lower than 1 will decrease the score
+ * boost value set to 1 will keep the score unchanged
+ * boost value greater than 1 will increase the score
+ *
+ * score = 0.5, boost = 0.5 result is 0.375
+ * score = 0.1, boost = 2 result is 0.325
+ *
+ * @param float $score
+ * @param float $boost
+ * @return float adjusted score
+ */
+ public function boost( $score, $boost ) {
+ if ( $boost == 1 ) {
+ return $score;
+ }
+
+ $boost = $boost > 1 ? 1 - ( 1 / $boost ) : - ( 1 - $boost );
+ if ( $boost > 0 ) {
+ return $score + ( ( ( 1 - $score ) / 2 ) * $boost );
+ } else {
+ return $score + ( ( $score / 2 ) * $boost );
+ }
}
}
diff --git a/includes/Searcher.php b/includes/Searcher.php
index 341c804..e3f68e3 100644
--- a/includes/Searcher.php
+++ b/includes/Searcher.php
@@ -1476,7 +1476,7 @@
$query->setRewrite( 'top_terms_boost_1024' );
if ( isset( $wgCirrusSearchQueryStringMaxDeterminizedStates ) )
{
- # Requires ES 1.4+
+ // Requires ES 1.4+
$query->setParam( 'max_determinized_states',
$wgCirrusSearchQueryStringMaxDeterminizedStates );
}
@@ -1817,7 +1817,7 @@
/**
* @return float[]
*/
- private static function getDefaultBoostTemplates() {
+ public static function getDefaultBoostTemplates() {
static $defaultBoostTemplates = null;
if ( $defaultBoostTemplates === null ) {
$source = wfMessage( 'cirrussearch-boost-templates'
)->inContentLanguage();
diff --git a/maintenance/updateSuggesterIndex.php
b/maintenance/updateSuggesterIndex.php
index 79225b2..4df6f0d 100644
--- a/maintenance/updateSuggesterIndex.php
+++ b/maintenance/updateSuggesterIndex.php
@@ -51,6 +51,11 @@
private $indexIdentifier;
/**
+ * @var SuggestScoringMethod the score function to use.
+ */
+ private $scoreMethod;
+
+ /**
* @var old suggester index that will be deleted at the end of the
process
*/
private $oldIndex;
@@ -89,6 +94,8 @@
'of moving a shard this can time out. This will retry
the attempt after some backoff ' .
'rather than failing the whole reindex process.
Defaults to 5.', false, true );
$this->addOption( 'optimize', 'Optimize the index to 1 segment.
Defaults to false.', false, false );
+ $this->addOption( 'scoringMethod', 'The scoring method to use
when computing suggestion weights. ' .
+ 'Detauls to quality.', false, true );
}
public function execute() {
@@ -195,7 +202,6 @@
'size' => $this->indexChunkSize
);
- $builder = new SuggestBuilder(
SuggestScoringMethodFactory::getScoringMethod( 'incomingLinks' ) );
// TODO: only content index for now ( we'll have to check how
it works with commons )
$sourceIndex = Connection::getIndex( $this->indexBaseName,
Connection::CONTENT_INDEX_TYPE );
@@ -204,6 +210,10 @@
$totalDocsInIndex = $totalDocsInIndex['hits']['total'];
$totalDocsToDump = $totalDocsInIndex;
+ $scoreMethodName = $this->getOption( 'scoringMethod', 'quality'
);
+ $this->scoreMethod =
SuggestScoringMethodFactory::getScoringMethod( $scoreMethodName,
$totalDocsInIndex );
+ $builder = new SuggestBuilder( $this->scoreMethod );
+
$docsDumped = 0;
$this->output( "Indexing $totalDocsToDump documents
($totalDocsInIndex in the index)\n" );
$self = $this;
diff --git a/tests/unit/SuggestScoringTest.php
b/tests/unit/SuggestScoringTest.php
new file mode 100644
index 0000000..c2bf342
--- /dev/null
+++ b/tests/unit/SuggestScoringTest.php
@@ -0,0 +1,278 @@
+<?php
+
+namespace CirrusSearch;
+
+use CirrusSearch\BuildDocument\QualityScore;
+
+/**
+ * test suggest scoring functions.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+class SuggestScoringTest extends \MediaWikiTestCase {
+ public function testQualityScoreNormFunctions() {
+ $qs = new QualityScore( 100000 );
+ for( $i = 0; $i < 1000; $i++ ) {
+ $value = rand( 0, 1000000 );
+ $norm = rand( 1, 1000000 );
+ $score = $qs->scoreNorm( $value, $norm );
+ $this->assertLessThanOrEqual( 1, $score, "scoreNorm
cannot produce a score greater than 1" );
+ $this->assertGreaterThanOrEqual( 0, $score, "scoreNorm
cannot produce a score lower than 0" );
+
+ $score = $qs->scoreNormL2( $value, $norm );
+ $this->assertLessThanOrEqual( 1, $score, "scoreNormL2
cannot produce a score greater than 1" );
+ $this->assertGreaterThanOrEqual( 0, $score,
"scoreNormL2 cannot produce a score lower than 0" );
+ }
+
+ # Edges
+ $score = $qs->scoreNorm( 1, 1 );
+ $this->assertLessThanOrEqual( 1, $score, "scoreNorm cannot
produce a score greater than 1" );
+ $this->assertGreaterThanOrEqual( 0, $score, "scoreNorm cannot
produce a score lower than 0" );
+
+ $score = $qs->scoreNorm( 0, 1 );
+ $this->assertLessThanOrEqual( 1, $score, "scoreNorm cannot
produce a score greater than 1" );
+ $this->assertGreaterThanOrEqual( 0, $score, "scoreNorm cannot
produce a score lower than 0" );
+
+ $score = $qs->scoreNormL2( 1, 1 );
+ $this->assertLessThanOrEqual( 1, $score, "scoreNormL2 cannot
produce a score greater than 1" );
+ $this->assertGreaterThanOrEqual( 0, $score, "scoreNormL2 cannot
produce a score lower than 0" );
+
+ $score = $qs->scoreNormL2( 0, 1 );
+ $this->assertLessThanOrEqual( 1, $score, "scoreNormL2 cannot
produce a score greater than 1" );
+ $this->assertGreaterThanOrEqual( 0, $score, "scoreNormL2 cannot
produce a score lower than 0" );
+ }
+
+ public function testQualityScoreBoostFunction() {
+ $qs = new QualityScore( 100000 );
+ for( $i = 0; $i < 1000; $i++ ) {
+ $score = (float) rand() / (float) mt_getrandmax();
+ $boost = (float) rand( 0, 10000 ) / rand( 1, 10000 );
+ $res = $qs->boost( $score, $boost );
+ $this->assertLessThanOrEqual( 1, $score, "boost cannot
produce a score greater than 1" );
+ $this->assertGreaterThanOrEqual( 0, $score, "boost
cannot produce a score lower than 0" );
+ if ( $boost > 1 ) {
+ $this->assertGreaterThan( $score, $res, "With a
boost ($boost) greater than 1 the boosted score must be greater than the
original." );
+ } else if ( $boost < 1 ) {
+ $this->assertLessThan( $score, $res, "With a
boost ($boost) lesser than 1 the boosted score must be lesser than the
original." );
+ } else {
+ $this->assertEquals( $score, $res, "When boost
is 1 the score remains unchanged." );
+ }
+ }
+ for( $i = 1; $i < 1000; $i++ ) {
+ # The same boost value must keep original score ordering
+ $score1 = 0.1;
+ $score2 = 0.5;
+
+ $boost = $i;
+
+ $res1 = $qs->boost( $score1, $boost );
+ $res2 = $qs->boost( $score2, $boost );
+
+ $this->assertGreaterThan( $res1, $res2, "A boost cannot
'overboost' a score" );
+ $res1 = $qs->boost( $score1, (float) 1/(float) $boost );
+ $res2 = $qs->boost( $score2, (float) 1/(float) $boost );
+ $this->assertGreaterThan( $res1, $res2, "A boost cannot
'overboost' a score" );
+ }
+
+ # Edges
+ $res = $qs->boost( 1, 1 );
+ $this->assertEquals( $res, 1, "When boost is 1 the score
remains unchanged." );
+ $res = $qs->boost( 1, 0 );
+ $this->assertEquals( $res, 0.5, "When boost is 0 the score is
divided by 2." );
+ $res = $qs->boost( 1, 2^31-1);
+ $this->assertEquals( $res, 1, "When score is 1 and boost is
very high the score is still 1." );
+ $res = $qs->boost( 0, 0 );
+ $this->assertEquals( $res, 0, "When score is 0 and boost is 0
the score is still 0." );
+ }
+
+
+ public function testQualityScoreBoostTemplates() {
+ $goodDoc = array(
+ 'template' => array( 'Good' )
+ );
+
+ $badDoc = array(
+ 'template' => array( 'Bad' )
+ );
+
+ $mixedDoc = array(
+ 'template' => array( 'Good', 'Bad' )
+ );
+
+ $neutralDoc = array(
+ 'template' => array( 'Neutral' )
+ );
+
+ $qs = new QualityScore( 100000, array( 'Good' => 2, 'Bad' =>
0.5 ) );
+
+ $score = 0.5;
+ $res = $qs->boostTemplates( $goodDoc, $score );
+ $this->assertGreaterThan( $score, $res, "A good doc gets a
better score" );
+
+ $res = $qs->boostTemplates( $badDoc, $score );
+ $this->assertLessThan( $score, $res, "A good doc gets a lower
score" );
+
+ $res = $qs->boostTemplates( $mixedDoc, $score );
+ $this->assertEquals( $score, $res, "A mixed doc gets the same
score");
+
+ $res = $qs->boostTemplates( $neutralDoc, $score );
+ $this->assertEquals( $res, $score, "A neutral doc gets the same
score" );
+ }
+
+ public function testQualityScoreRanking() {
+ $maxDocs = 10000000;
+ $qs = new QualityScore( $maxDocs, array( 'Good' => 2, 'Bad' =>
0.5 ) );
+ $veryGoodArticle = array(
+ 'incoming_links' => 120340,
+ 'external_link' => array_fill( 0, 200, null ),
+ 'text_bytes' => '230000',
+ 'heading' => array_fill( 0, 30, null ),
+ 'redirect' => array_fill( 0, 100, null ),
+ 'template' => array( 'Good' )
+ );
+
+ $goodArticle = array(
+ 'incoming_links' => 120340,
+ 'external_link' => array_fill( 0, 200, null ),
+ 'text_bytes' => '230000',
+ 'heading' => array_fill( 0, 30, null ),
+ 'redirect' => array_fill( 0, 100, null ),
+ 'template' => array()
+ );
+
+ $goodButBadArticle = array(
+ 'incoming_links' => 120340,
+ 'external_link' => array_fill( 0, 200, null ),
+ 'text_bytes' => '230000',
+ 'heading' => array_fill( 0, 30, null ),
+ 'redirect' => array_fill( 0, 100, null ),
+ 'template' => array( 'Bad' )
+ );
+
+
+ $this->assertLessThan( $qs->score( $veryGoodArticle ),
$qs->score( $goodArticle ),
+ "Same values but a boosted template give a better
score" );
+ $this->assertLessThan( $qs->score( $goodArticle ), $qs->score(
$goodButBadArticle ),
+ "Same values but without a negative boosted template
give a better score" );
+
+ $page1 = array(
+ 'incoming_links' => $maxDocs *
QualityScore::INCOMING_LINKS_MAX_DOCS_FACTOR,
+ 'external_link' => array_fill( 0, 200, null ),
+ 'text_bytes' => '230000',
+ 'heading' => array_fill( 0, 30, null ),
+ 'redirect' => array_fill( 0, 100, null ),
+ 'template' => array( 'Good' )
+ );
+
+ $page2 = array(
+ 'incoming_links' => $maxDocs *
QualityScore::INCOMING_LINKS_MAX_DOCS_FACTOR + 1,
+ 'external_link' => array_fill( 0, 200, null ),
+ 'text_bytes' => '230000',
+ 'heading' => array_fill( 0, 30, null ),
+ 'redirect' => array_fill( 0, 100, null ),
+ 'template' => array( 'Good' )
+ );
+ $this->assertEquals( $qs->score( $page1 ), $qs->score( $page2 ),
+ "Having more incoming links than the norm give the same
score" );
+
+ $page1 = array(
+ 'incoming_links' => $maxDocs *
QualityScore::INCOMING_LINKS_MAX_DOCS_FACTOR,
+ 'external_link' => array_fill( 0, 200, null ),
+ 'text_bytes' => QualityScore::PAGE_SIZE_NORM,
+ 'heading' => array_fill( 0, 30, null ),
+ 'redirect' => array_fill( 0, 100, null ),
+ 'template' => array( 'Good' )
+ );
+
+ $page2 = array(
+ 'incoming_links' => $maxDocs *
QualityScore::INCOMING_LINKS_MAX_DOCS_FACTOR,
+ 'external_link' => array_fill( 0, 200, null ),
+ 'text_bytes' => QualityScore::PAGE_SIZE_NORM + 1,
+ 'heading' => array_fill( 0, 30, null ),
+ 'redirect' => array_fill( 0, 100, null ),
+ 'template' => array( 'Good' )
+ );
+
+ $this->assertEquals( $qs->score( $page1 ), $qs->score( $page2 ),
+ "Having more text_bytes than the norm give the same
score" );
+ }
+
+ public function testQualityScoreWithRandomValues() {
+ $maxDocs = 10000000;
+ $qs = new QualityScore( $maxDocs, array( 'Good' => 2, 'Bad' =>
0.5 ) );
+
+ for( $i = 0; $i < 1000; $i++ ) {
+ $page = array(
+ 'incoming_links' => rand( 0, 2^31-1 ),
+ 'external_link' => array_fill( 0, rand( 1, 2000
), null ),
+ 'text_bytes' => rand( 1, 400000 ),
+ 'heading' => array_fill( 0, rand( 1, 1000 ),
null ),
+ 'redirect' => array_fill( 0, rand( 1, 1000 ),
null ),
+ 'template' => rand( 0, 1 ) == 1 ? array( 'Good'
) : array('Bad')
+ );
+ $this->assertGreaterThan( 0, $qs->score( $page ),
"Score is always greater than 0" );
+ $this->assertLessThan( QualityScore::SCORE_RANGE,
$qs->score( $page ), "Score is always lower than " . QualityScore::SCORE_RANGE
);
+ }
+
+ # Edges
+ $page = array(
+ 'incoming_links' => $maxDocs *
QualityScore::INCOMING_LINKS_MAX_DOCS_FACTOR,
+ 'external_link' => array_fill( 0,
QualityScore::EXTERNAL_LINKS_NORM, null ),
+ 'text_bytes' => QualityScore::PAGE_SIZE_NORM,
+ 'heading' => array_fill( 0, QualityScore::HEADING_NORM,
null ),
+ 'redirect' => array_fill( 0,
QualityScore::REDIRECT_NORM, null ),
+ 'template' => array()
+ );
+ $this->assertEquals( QualityScore::SCORE_RANGE, $qs->score(
$page ), "Highest score is " . QualityScore::SCORE_RANGE );
+
+ $page = array(
+ 'incoming_links' => 0,
+ 'external_link' => array(),
+ 'text_bytes' => 0,
+ 'heading' => array(),
+ 'redirect' => array(),
+ 'template' => array()
+ );
+ $this->assertEquals( 0, $qs->score( $page ), "Lowest score is
0" );
+
+ $page = array();
+ $this->assertEquals( 0, $qs->score( $page ), "Score of a broken
article is 0" );
+
+ # A very small wiki
+ $qs = new QualityScore( 1 );
+ $page = array(
+ 'incoming_links' => 1,
+ 'external_link' => array_fill( 0,
QualityScore::EXTERNAL_LINKS_NORM, null ),
+ 'text_bytes' => QualityScore::PAGE_SIZE_NORM,
+ 'heading' => array_fill( 0, QualityScore::HEADING_NORM,
null ),
+ 'redirect' => array_fill( 0,
QualityScore::REDIRECT_NORM, null ),
+ 'template' => array()
+ );
+ $this->assertEquals( QualityScore::SCORE_RANGE, $qs->score(
$page ), "With very small wiki the highest score is also " .
QualityScore::SCORE_RANGE );
+
+ # The scoring function should not fail with 0 page
+ $qs = new QualityScore( 0 );
+ $page = array(
+ 'incoming_links' => 1,
+ 'external_link' => array_fill( 0,
QualityScore::EXTERNAL_LINKS_NORM, null ),
+ 'text_bytes' => QualityScore::PAGE_SIZE_NORM,
+ 'heading' => array_fill( 0, QualityScore::HEADING_NORM,
null ),
+ 'redirect' => array_fill( 0,
QualityScore::REDIRECT_NORM, null ),
+ 'template' => array()
+ );
+ $this->assertEquals( QualityScore::SCORE_RANGE, $qs->score(
$page ), "With a zero page wiki the highest score is also " .
QualityScore::SCORE_RANGE );
+ }
+}
--
To view, visit https://gerrit.wikimedia.org/r/231023
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings
Gerrit-MessageType: merged
Gerrit-Change-Id: I1f61bb66bcf8fff4b02b31ac9fe8525b85fe9ea5
Gerrit-PatchSet: 10
Gerrit-Project: mediawiki/extensions/CirrusSearch
Gerrit-Branch: master
Gerrit-Owner: DCausse <[email protected]>
Gerrit-Reviewer: Chad <[email protected]>
Gerrit-Reviewer: Cindy-the-browser-test-bot <[email protected]>
Gerrit-Reviewer: DCausse <[email protected]>
Gerrit-Reviewer: EBernhardson <[email protected]>
Gerrit-Reviewer: Manybubbles <[email protected]>
Gerrit-Reviewer: Tjones <[email protected]>
Gerrit-Reviewer: jenkins-bot <>
_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits