From 3fd37b5debac47acbf67c531182a9a4ecd7d8ae7 Mon Sep 17 00:00:00 2001 From: Roman Parpalak <roman@parpalak.com> Date: Sat, 24 Aug 2024 16:14:13 +0300 Subject: [PATCH] Refinements in Russian stemmer. --- src/S2/Rose/Stemmer/PorterStemmerRussian.php | 108 +++++++++---------- tests/unit/Rose/Stemmer/StemmerTest.php | 19 ++-- 2 files changed, 65 insertions(+), 62 deletions(-) diff --git a/src/S2/Rose/Stemmer/PorterStemmerRussian.php b/src/S2/Rose/Stemmer/PorterStemmerRussian.php index 61b5ed3..d986cb6 100644 --- a/src/S2/Rose/Stemmer/PorterStemmerRussian.php +++ b/src/S2/Rose/Stemmer/PorterStemmerRussian.php @@ -25,6 +25,7 @@ class PorterStemmerRussian extends AbstractStemmer implements StemmerInterface 'когда' => '', 'где' => '', 'куда' => '', + 'откуда' => '', 'если' => '', 'тире' => '', 'после' => '', @@ -90,7 +91,6 @@ class PorterStemmerRussian extends AbstractStemmer implements StemmerInterface 'просто' => '', 'почему' => '', 'потому' => '', - 'всего' => '', 'чтоб' => '', 'чтобы' => 'чтоб', 'лишь' => '', @@ -107,6 +107,10 @@ class PorterStemmerRussian extends AbstractStemmer implements StemmerInterface 'из' => '', 'из-за' => '', + 'всего' => 'все', + 'всему' => 'все', + 'всем' => 'все', + 'что' => '', 'чего' => 'что', 'чему' => 'что', @@ -131,56 +135,30 @@ class PorterStemmerRussian extends AbstractStemmer implements StemmerInterface 'никем' => 'никто', 'ником' => 'никто', - 'все-таки' => '', - 'во-первых' => '', - 'во-вторых' => '', - 'в-третьих' => '', - - // Some popular prefixes and postfixes. TODO come up with a more systematic approach - 'как-то' => '', - 'как-нибудь' => '', - 'где-то' => '', - 'когда-то' => '', - 'когда-нибудь' => '', - 'куда-то' => '', - 'почему-то' => '', - 'вообще-то' => '', - - 'что-то' => 'что-то', - 'чего-то' => 'что-то', - 'чему-то' => 'что-то', - 'чем-то' => 'что-то', - 'чём-то' => 'что-то', - - 'что-нибудь' => 'что-нибудь', - 'чего-нибудь' => 'что-нибудь', - 'чему-нибудь' => 'что-нибудь', - 'чем-нибудь' => 'что-нибудь', - 'чём-нибудь' => 'что-нибудь', - - 'кое-что' => 'кое-что', - 'кое-чего' => 'кое-что', - 'кое-чему' => 'кое-что', - 'кое-чем' => 'кое-что', - 'кое-чём' => 'кое-что', - - 'кто-то' => 'кто-то', - 'кого-то' => 'кто-то', - 'кому-то' => 'кто-то', - 'кем-то' => 'кто-то', - 'ком-то' => 'кто-то', - - 'кто-нибудь' => 'кто-нибудь', - 'кого-нибудь' => 'кто-нибудь', - 'кому-нибудь' => 'кто-нибудь', - 'кем-нибудь' => 'кто-нибудь', - 'ком-нибудь' => 'кто-нибудь', - - 'кое-кто' => 'кое-кто', - 'кое-кого' => 'кое-кто', - 'кое-кому' => 'кое-кто', - 'кое-кем' => 'кое-кто', - 'кое-ком' => 'кое-кто', + // Не могу добавить тем и том из-за существительных + 'того' => 'тот', + 'тому' => 'тот', + + 'чей' => '', + 'чьего' => 'чей', + 'чьё' => 'чей', + 'чье' => 'чей', + 'чья' => 'чей', + 'чьи' => 'чей', + 'чьей' => 'чей', + 'чьих' => 'чей', + 'чьему' => 'чей', + 'чьим' => 'чей', + 'чью' => 'чей', + 'чьею' => 'чей', + 'чьими' => 'чей', + 'чьём' => 'чей', + 'чьем' => 'чей', + + 'все-таки' => '', + 'во-первых' => '', + 'во-вторых' => '', + 'в-третьих' => '', 'печать' => 'печат', // стеммер считает, что это глагол @@ -320,6 +298,18 @@ class PorterStemmerRussian extends AbstractStemmer implements StemmerInterface 'могло' => 'мочь', 'могли' => 'мочь', + 'быть' => '', + 'был' => 'быть', + 'была' => 'быть', + 'было' => 'быть', + 'были' => 'быть', + 'буду' => 'быть', + 'будешь' => 'быть', + 'будет' => 'быть', + 'будем' => 'быть', + 'будете' => 'быть', + 'будут' => 'быть', + 'другой' => 'друго', 'другого' => 'друго', 'другому' => 'друго', @@ -343,11 +333,17 @@ public function stemWord(string $word, bool $normalize = true): string return $this->cache[$word]; } - /** - * TODO How to deal with postfixes like "кто-либо" -> "кого-либо"? - * Ignoring postfix is not an option - there are a lot of trash results found. - */ - // $word = preg_replace('/^(.*)-(то|либо|нибудь)$/Su', '-\\2-\\1', $word); + if (\preg_match('/^([^\-]+)-(то|либо|нибудь|ка)$/Su', $word, $matches)) { + return $this->cache[$word] = $this->stemWord($matches[1]) . '-' . $matches[2]; + } + + if (substr($word, 0, strlen('кое-')) === 'кое-') { + return $this->cache[$word] = 'кое-' . $this->stemWord(substr($word, strlen('кое-'))); + } + + if (substr($word, 0, strlen('по-')) === 'по-') { + return $this->cache[$word] = $word; + } if (isset(self::$irregularWords[$word])) { return $this->cache[$word] = (self::$irregularWords[$word] !== '' ? self::$irregularWords[$word] : $word); diff --git a/tests/unit/Rose/Stemmer/StemmerTest.php b/tests/unit/Rose/Stemmer/StemmerTest.php index 4333fc1..9b25a1a 100644 --- a/tests/unit/Rose/Stemmer/StemmerTest.php +++ b/tests/unit/Rose/Stemmer/StemmerTest.php @@ -1,6 +1,6 @@ <?php /** - * @copyright 2016-2023 Roman Parpalak + * @copyright 2016-2024 Roman Parpalak * @license MIT */ @@ -54,6 +54,18 @@ public function testRegexes(): void $this->assertEquals('доб', $this->russianStemmer->stemWord('добившись')); } + public function testParticles(): void + { + $this->assertEquals('кто-нибудь', $this->russianStemmer->stemWord('кого-нибудь')); + $this->assertEquals('когда-нибудь', $this->russianStemmer->stemWord('когда-нибудь')); + $this->assertEquals('что-то', $this->russianStemmer->stemWord('чему-то')); + $this->assertEquals('нехитр-то', $this->russianStemmer->stemWord('нехитрое-то')); + $this->assertEquals('когда-либо', $this->russianStemmer->stemWord('когда-либо')); + $this->assertEquals('что-либо', $this->russianStemmer->stemWord('чем-либо')); + $this->assertEquals('кое-что', $this->russianStemmer->stemWord('кое-чем')); + $this->assertEquals('кое-кто', $this->russianStemmer->stemWord('кое-кого')); + } + public function testStem(): void { $this->assertEquals('ухмыляться', $this->englishStemmer->stemWord('ухмыляться')); @@ -65,11 +77,6 @@ public function testStem(): void $this->assertEquals('метро', $this->russianStemmer->stemWord('метро')); - $this->assertEquals('кое-кто', $this->russianStemmer->stemWord('кое-кого')); - $this->assertEquals('чем-либ', $this->russianStemmer->stemWord('чем-либо')); - $this->assertEquals('когда-нибудь', $this->russianStemmer->stemWord('когда-нибудь')); - $this->assertEquals('нехитрое-т', $this->russianStemmer->stemWord('нехитрое-то')); - $this->assertEquals('экзамен', $this->russianStemmer->stemWord('экзамен')); $this->assertEquals('экзамен', $this->russianStemmer->stemWord('экзамена')); $this->assertEquals('экзамен', $this->russianStemmer->stemWord('экзамену'));