diff --git a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php index 09897fec2..77da7b435 100644 --- a/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php +++ b/src/Bundle/ChillMainBundle/DependencyInjection/ChillMainExtension.php @@ -19,6 +19,7 @@ namespace Chill\MainBundle\DependencyInjection; +use Chill\MainBundle\Doctrine\DQL\StrictWordSimilarityOPS; use Chill\MainBundle\Entity\UserJob; use Chill\MainBundle\Form\UserJobType; use Symfony\Component\DependencyInjection\ContainerBuilder; @@ -185,6 +186,7 @@ class ChillMainExtension extends Extension implements PrependExtensionInterface, 'JSONB_EXISTS_IN_ARRAY' => JsonbExistsInArray::class, 'SIMILARITY' => Similarity::class, 'OVERLAPSI' => OverlapsI::class, + 'STRICT_WORD_SIMILARITY_OPS' => StrictWordSimilarityOPS::class ], ], 'hydrators' => [ diff --git a/src/Bundle/ChillMainBundle/Doctrine/DQL/Similarity.php b/src/Bundle/ChillMainBundle/Doctrine/DQL/Similarity.php index 988e55f5f..1875f978c 100644 --- a/src/Bundle/ChillMainBundle/Doctrine/DQL/Similarity.php +++ b/src/Bundle/ChillMainBundle/Doctrine/DQL/Similarity.php @@ -21,20 +21,15 @@ namespace Chill\MainBundle\Doctrine\DQL; use Doctrine\ORM\Query\AST\Functions\FunctionNode; use Doctrine\ORM\Query\Lexer; -/** - * - * - * - */ class Similarity extends FunctionNode { private $firstPart; - + private $secondPart; - + public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) { - return 'SIMILARITY('.$this->firstPart->dispatch($sqlWalker). + return 'SIMILARITY('.$this->firstPart->dispatch($sqlWalker). ', ' . $this->secondPart->dispatch($sqlWalker) .")"; } @@ -42,13 +37,13 @@ class Similarity extends FunctionNode { $parser->match(Lexer::T_IDENTIFIER); $parser->match(Lexer::T_OPEN_PARENTHESIS); - + $this->firstPart = $parser->StringPrimary(); - + $parser->match(Lexer::T_COMMA); - + $this->secondPart = $parser->StringPrimary(); - + $parser->match(Lexer::T_CLOSE_PARENTHESIS); } } diff --git a/src/Bundle/ChillMainBundle/Doctrine/DQL/StrictWordSimilarityOPS.php b/src/Bundle/ChillMainBundle/Doctrine/DQL/StrictWordSimilarityOPS.php new file mode 100644 index 000000000..22dcda879 --- /dev/null +++ b/src/Bundle/ChillMainBundle/Doctrine/DQL/StrictWordSimilarityOPS.php @@ -0,0 +1,34 @@ +firstPart->dispatch($sqlWalker). + ' <<% ' . $this->secondPart->dispatch($sqlWalker); + } + + public function parse(\Doctrine\ORM\Query\Parser $parser) + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->firstPart = $parser->StringPrimary(); + + $parser->match(Lexer::T_COMMA); + + $this->secondPart = $parser->StringPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } +} diff --git a/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepository.php b/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepository.php index f466260ca..40d06615c 100644 --- a/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepository.php +++ b/src/Bundle/ChillPersonBundle/Repository/PersonACLAwareRepository.php @@ -12,7 +12,6 @@ use Doctrine\ORM\NonUniqueResultException; use Doctrine\ORM\NoResultException; use Doctrine\ORM\Query; use Doctrine\ORM\QueryBuilder; -use Symfony\Component\Security\Core\Role\Role; use Symfony\Component\Security\Core\Security; @@ -126,7 +125,7 @@ final class PersonACLAwareRepository implements PersonACLAwareRepositoryInterfac ).'AS text' ); } else { - $qb->select('p'); + $qb->select('sp'); } $qb @@ -254,18 +253,14 @@ final class PersonACLAwareRepository implements PersonACLAwareRepositoryInterfac $grams = explode(' ', $pattern); foreach($grams as $key => $gram) { - $qb->andWhere('SIMILARITY(sp.fullnameCanonical, UNACCENT(LOWER(:default_'.$key.')) ) >= 0.15') + $qb->andWhere('STRICT_WORD_SIMILARITY_OPS(:default_'.$key.', sp.fullnameCanonical) = TRUE') ->setParameter('default_'.$key, '%'.$gram.'%'); - } - $qb->andWhere($qb->expr() - ->notIn( - 'sp.id', - $this->createSearchQuery($pattern) - ->addSelect('p.id') - ->getDQL() - ) - ); + // remove the perfect matches + $qb->andWhere($qb->expr() + ->notLike('sp.fullnameCanonical', 'UNACCENT(LOWER(:not_default_'.$key.'))')) + ->setParameter('not_default_'.$key, '%'.$gram.'%'); + } return $qb; } diff --git a/src/Bundle/ChillPersonBundle/Search/SimilarityPersonSearch.php b/src/Bundle/ChillPersonBundle/Search/SimilarityPersonSearch.php index f1ea52c8e..e62c2ee55 100644 --- a/src/Bundle/ChillPersonBundle/Search/SimilarityPersonSearch.php +++ b/src/Bundle/ChillPersonBundle/Search/SimilarityPersonSearch.php @@ -104,7 +104,7 @@ class SimilarityPersonSearch extends AbstractSearch protected function search(array $terms, $start, $limit, array $options = array()) { return $this->personACLAwareRepository - ->findBySimilaritySearch($terms['_default']); + ->findBySimilaritySearch($terms['_default'], $start, $limit, $options['simplify'] ?? false); } protected function count(array $terms) diff --git a/src/Bundle/ChillPersonBundle/migrations/Version20210910161858.php b/src/Bundle/ChillPersonBundle/migrations/Version20210910161858.php new file mode 100644 index 000000000..82cade6cf --- /dev/null +++ b/src/Bundle/ChillPersonBundle/migrations/Version20210910161858.php @@ -0,0 +1,31 @@ +addSql('DROP INDEX fullnamecanonical_trgm_idx'); + $this->addSql('CREATE INDEX fullnameCanonical_trgm_idx ON chill_person_person USING GIST (center_id, fullnameCanonical gist_trgm_ops)'); + } + + public function down(Schema $schema): void + { + $this->addSql('DROP INDEX fullnamecanonical_trgm_idx'); + $this->addSql('CREATE INDEX fullnameCanonical_trgm_idx ON chill_person_person USING GIST (fullnameCanonical gist_trgm_ops)'); + } +}