From f28396a2f82fe61af05f19c1df367f4c660655ab Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Sat, 19 May 2018 15:47:55 +0200 Subject: [PATCH 1/2] Fix order of tags with the same number of occurrences Fixes #1142 --- application/LinkDB.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/application/LinkDB.php b/application/LinkDB.php index c1661d52..ce53f200 100644 --- a/application/LinkDB.php +++ b/application/LinkDB.php @@ -458,8 +458,10 @@ public function linksCountPerTag($filteringTags = [], $visibility = 'all') $tags[$caseMapping[strtolower($tag)]]++; } } - // Sort tags by usage (most used tag first) - arsort($tags); + $keys = array_keys($tags); + $tmpTags = array_combine($keys, $keys); + // We sort tags by DESC occurrences, then ASC alphabetically for equal values. + array_multisort($tags, SORT_DESC, $tmpTags, SORT_ASC, $tags); return $tags; } From f8c5660df82432c7f3bb0686e393db5a2b46eeb5 Mon Sep 17 00:00:00 2001 From: ArthurHoaro Date: Tue, 29 May 2018 20:52:30 +0200 Subject: [PATCH 2/2] Tag sort - UT + comment + fix filter and visibility Before this, linksCountPerTag call without would have ignored visibility parameter --- application/LinkDB.php | 25 ++++++++--- tests/LinkDBTest.php | 100 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 7 deletions(-) diff --git a/application/LinkDB.php b/application/LinkDB.php index ce53f200..cd0f2967 100644 --- a/application/LinkDB.php +++ b/application/LinkDB.php @@ -436,15 +436,17 @@ public function filterSearch($filterRequest = array(), $casesensitive = false, $ /** * Returns the list tags appearing in the links with the given tags - * @param $filteringTags: tags selecting the links to consider - * @param $visibility: process only all/private/public links - * @return: a tag=>linksCount array + * + * @param array $filteringTags tags selecting the links to consider + * @param string $visibility process only all/private/public links + * + * @return array tag => linksCount */ public function linksCountPerTag($filteringTags = [], $visibility = 'all') { - $links = empty($filteringTags) ? $this->links : $this->filterSearch(['searchtags' => $filteringTags], false, $visibility); - $tags = array(); - $caseMapping = array(); + $links = $this->filterSearch(['searchtags' => $filteringTags], false, $visibility); + $tags = []; + $caseMapping = []; foreach ($links as $link) { foreach (preg_split('/\s+/', $link['tags'], 0, PREG_SPLIT_NO_EMPTY) as $tag) { if (empty($tag)) { @@ -458,9 +460,18 @@ public function linksCountPerTag($filteringTags = [], $visibility = 'all') $tags[$caseMapping[strtolower($tag)]]++; } } + + /* + * Formerly used arsort(), which doesn't define the sort behaviour for equal values. + * Also, this function doesn't produce the same result between PHP 5.6 and 7. + * + * So we now use array_multisort() to sort tags by DESC occurrences, + * then ASC alphabetically for equal values. + * + * @see https://github.com/shaarli/Shaarli/issues/1142 + */ $keys = array_keys($tags); $tmpTags = array_combine($keys, $keys); - // We sort tags by DESC occurrences, then ASC alphabetically for equal values. array_multisort($tags, SORT_DESC, $tmpTags, SORT_ASC, $tags); return $tags; } diff --git a/tests/LinkDBTest.php b/tests/LinkDBTest.php index 5b2f3667..3b980878 100644 --- a/tests/LinkDBTest.php +++ b/tests/LinkDBTest.php @@ -542,4 +542,104 @@ public function testDeleteTag() $this->assertEquals(3, count($res)); $this->assertNotContains('cartoon', $linkDB[4]['tags']); } + + /** + * Test linksCountPerTag all tags without filter. + * Equal occurrences should be sorted alphabetically. + */ + public function testCountLinkPerTagAllNoFilter() + { + $expected = [ + 'web' => 4, + 'cartoon' => 3, + 'dev' => 2, + 'gnu' => 2, + 'hashtag' => 2, + 'sTuff' => 2, + '-exclude' => 1, + '.hidden' => 1, + 'Mercurial' => 1, + 'css' => 1, + 'free' => 1, + 'html' => 1, + 'media' => 1, + 'samba' => 1, + 'software' => 1, + 'stallman' => 1, + 'tag1' => 1, + 'tag2' => 1, + 'tag3' => 1, + 'tag4' => 1, + 'ut' => 1, + 'w3c' => 1, + ]; + $tags = self::$privateLinkDB->linksCountPerTag(); + + $this->assertEquals($expected, $tags, var_export($tags, true)); + } + + /** + * Test linksCountPerTag all tags with filter. + * Equal occurrences should be sorted alphabetically. + */ + public function testCountLinkPerTagAllWithFilter() + { + $expected = [ + 'gnu' => 2, + 'hashtag' => 2, + '-exclude' => 1, + '.hidden' => 1, + 'free' => 1, + 'media' => 1, + 'software' => 1, + 'stallman' => 1, + 'stuff' => 1, + 'web' => 1, + ]; + $tags = self::$privateLinkDB->linksCountPerTag(['gnu']); + + $this->assertEquals($expected, $tags, var_export($tags, true)); + } + + /** + * Test linksCountPerTag public tags with filter. + * Equal occurrences should be sorted alphabetically. + */ + public function testCountLinkPerTagPublicWithFilter() + { + $expected = [ + 'gnu' => 2, + 'hashtag' => 2, + '-exclude' => 1, + '.hidden' => 1, + 'free' => 1, + 'media' => 1, + 'software' => 1, + 'stallman' => 1, + 'stuff' => 1, + 'web' => 1, + ]; + $tags = self::$privateLinkDB->linksCountPerTag(['gnu'], 'public'); + + $this->assertEquals($expected, $tags, var_export($tags, true)); + } + + /** + * Test linksCountPerTag public tags with filter. + * Equal occurrences should be sorted alphabetically. + */ + public function testCountLinkPerTagPrivateWithFilter() + { + $expected = [ + 'cartoon' => 1, + 'dev' => 1, + 'tag1' => 1, + 'tag2' => 1, + 'tag3' => 1, + 'tag4' => 1, + ]; + $tags = self::$privateLinkDB->linksCountPerTag(['dev'], 'private'); + + $this->assertEquals($expected, $tags, var_export($tags, true)); + } }