add search engine

This commit is contained in:
Simon Vieille 2021-03-29 22:45:51 +02:00
parent c3c49d89cd
commit 120dcdaa44
8 changed files with 117 additions and 25 deletions

View File

@ -7,6 +7,7 @@
"php": ">=7.2.5",
"ext-ctype": "*",
"ext-iconv": "*",
"beberlei/doctrineextensions": "^1.3",
"bjeavons/zxcvbn-php": "^1.2",
"cocur/slugify": "^4.0",
"composer/package-versions-deprecated": "1.11.99.1",

View File

@ -9,6 +9,9 @@ doctrine:
auto_generate_proxy_classes: true
naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware
auto_mapping: true
dql:
string_functions:
FIELD: DoctrineExtensions\Query\Mysql\Field
mappings:
App\Core\Entity:
is_bundle: false

View File

@ -28,28 +28,8 @@ abstract class RepositoryQuery
public function __call(string $name, $params): self
{
$fn = function (&$data) {
if (is_string($data)) {
$words = explode(' ', $data);
foreach ($words as $k => $v) {
if (isset($v[0]) && '.' === $v[0]) {
$words[$k] = $this->id.$v;
}
}
$data = implode(' ', $words);
} elseif (is_array($data)) {
foreach ($data as $k => $v) {
$fn($data[$k]);
}
}
return $data;
};
foreach ($params as $key => $value) {
$fn($params[$key]);
$this->populateDqlId($params[$key]);
}
call_user_func_array([$this->query, $name], $params);
@ -93,4 +73,25 @@ abstract class RepositoryQuery
{
return $this->repository;
}
protected function populateDqlId(&$data)
{
if (is_string($data)) {
$words = explode(' ', $data);
foreach ($words as $k => $v) {
if (isset($v[0]) && '.' === $v[0]) {
$words[$k] = $this->id.$v;
}
}
$data = implode(' ', $words);
} elseif (is_array($data)) {
foreach ($data as $k => $v) {
$this->populateDqlId($data[$k]);
}
}
return $data;
}
}

View File

@ -11,6 +11,7 @@ use App\Repository\Blog\PostRepositoryQuery;
use App\UrlGenerator\PostGenerator;
use Symfony\Component\HttpFoundation\Response;
use App\Core\Site\SiteStore;
use Symfony\Component\HttpFoundation\Request;
class PostController extends PageController
{
@ -58,6 +59,23 @@ class PostController extends PageController
]);
}
public function search(Request $request, int $page = 1): Response
{
$query = $request->query->get('query');
if ($query) {
$entities = $this->createQuery()
->search($query)
->paginate($page, 5)
;
}
return $this->defaultRender('blog/post/search.html.twig', [
'pager' => $entities ?? null,
'query' => $query,
]);
}
/**
* @UrlGenerator(service=PostGenerator::class, method="category")
*/

View File

@ -13,6 +13,10 @@ use Symfony\Component\HttpFoundation\File\File;
/**
* @ORM\Entity(repositoryClass=PostRepository::class)
* @ORM\Table(indexes={
* @ORM\Index(name="post_title", columns={"title"}, flags={"fulltext"}),
* @ORM\Index(name="post_content", columns={"content"}, flags={"fulltext"})
* })
* @ORM\HasLifecycleCallbacks
*/
class Post implements EntityInterface

View File

@ -5,6 +5,7 @@ namespace App\Repository\Blog;
use App\Entity\Blog\Post;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use App\Core\Manager\EntityManager;
class PostRepository extends ServiceEntityRepository
{
@ -12,4 +13,9 @@ class PostRepository extends ServiceEntityRepository
{
parent::__construct($registry, Post::class);
}
public function getEm()
{
return $this->getEntityManager();
}
}

View File

@ -4,7 +4,6 @@ namespace App\Repository\Blog;
use App\Core\Repository\RepositoryQuery;
use App\Entity\Blog\Category;
use App\Entity\User;
use Knp\Component\Pager\PaginatorInterface;
/**
@ -37,13 +36,14 @@ class PostRepositoryQuery extends RepositoryQuery
return $this
->andWhere('.status = 1')
->andWhere('.publishedAt <= :now')
->setParameter(':now', (new \DateTime('now'))->format('Y-m-d H:i:s'));
->setParameter(':now', (new \DateTime('now'))->format('Y-m-d H:i:s'))
;
}
public function useFilters(array $filters)
{
foreach ($filters as $name => $value) {
if ($value === null) {
if (null === $value) {
continue;
}
@ -54,7 +54,7 @@ class PostRepositoryQuery extends RepositoryQuery
$this->andWhere('.'.$name.' LIKE :'.$name);
$this->setParameter(':'.$name, '%'.$value.'%');
} else {
if ($name === 'category') {
if ('category' === $name) {
$this->inCategory($value);
}
}
@ -62,4 +62,60 @@ class PostRepositoryQuery extends RepositoryQuery
return $this;
}
public function search(string $keywords)
{
$conn = $this->repository->getEm()->getConnection();
$statement = $conn->prepare(
'SELECT
post.id,
post.title,
MATCH(post.title) AGAINST(:search) AS MATCH_TITLE,
MATCH(post.content) AGAINST(:search) AS MATCH_CONTENT
FROM post
WHERE
post.status = 1 AND
post.published_at < :date
ORDER BY
MATCH_TITLE DESC,
MATCH_CONTENT DESC
');
$statement->execute([
':search' => $keywords,
':date' => (new \DateTime())->format('Y-m-d H:i:s'),
]);
$results = $statement->fetchAll();
$ids = [];
foreach ($results as $k => $v) {
$rate = ($v['MATCH_TITLE'] * 2) + $v['MATCH_CONTENT'];
if ($rate >= 7) {
$ids[] = $v['id'];
}
}
if (0 == count($ids)) {
foreach ($results as $k => $v) {
$rate = ($v['MATCH_TITLE'] * 2) + $v['MATCH_CONTENT'];
if ($rate >= 6) {
$ids[] = $v['id'];
}
}
}
if (!$ids) {
$ids = [-1];
}
return $this
->orderBy('FIELD(p.id, :ids)')
->andWhere('.id IN(:ids)')
->setParameter(':ids', $ids)
;
}
}

View File

@ -5,6 +5,9 @@
"beberlei/assert": {
"version": "v3.3.0"
},
"beberlei/doctrineextensions": {
"version": "v1.3.0"
},
"behat/transliterator": {
"version": "v1.3.0"
},