Как я могу выполнить это динамическое усреднение путем приведения из PostgreSQL к DQL? - PullRequest
0 голосов
/ 26 октября 2018

Я должен добавить условие фильтра для получения списка отзывов с andWhere().Вот версия PostgreSQL:

SELECT *
FROM listing_review
WHERE(
    (
      edited_rating_meals_nutrition +
      edited_rating_treatment_effectiveness +
      rating_accommodations_amenities
    )
    /
    (
      COALESCE(
        NULLIF(
              (
                (edited_rating_meals_nutrition::BOOLEAN)::INTEGER +
                (edited_rating_effectiveness::BOOLEAN)::INTEGER +
                (rating_accommodations_amenities::BOOLEAN)::INTEGER
              )
            , 0
        ), 1
      )
    )
 )
 >= 3;

По сути, мне нужно усреднить три рейтинга на строку (обзор) и отфильтровать их по тому, равно ли среднее из них целому числу или больше (5 в этом примере)).Если они не были рассмотрены ни в одной из категорий, значение этой категории равно нулю, и мне нужно отбросить их из среднего расчета (отсюда двойное приведение от логического к целому числу, чтобы получить число оценок, которые не равны нулю).Я также не могу делить на ноль, объясняя COALESCE.

Я могу достичь этого наивного способа, просто разделив на три, как это:

if (isset($searchParams[self::PARAM_MINIMUM_RATING])) {
$qb
   ->andWhere('((r.editedRatingTreatmentEffectiveness + r.editedRatingAccommodationsAmenities + r.editedRatingMealsNutrition) / 3) >= :minimum_rating')
   ->setParameter('minimum_rating', $searchParams[self::PARAM_MINIMUM_RATING]);

Как я могусделать это в DQL, сделав делитель динамическим (как мой запрос PostgreSQL) вместо жестко заданного в 3?

1 Ответ

0 голосов
/ 01 ноября 2018

Я решил свою собственную проблему с помощью этой записи .

По сути, DQL содержит NULLIF(), а также COALESCE(), но НЕ содержит CAST().Я добавил следующие классы:

<?php
namespace DoctrineFunctions;


use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;

class CastToBoolean extends FunctionNode
{
    public $stringPrimary;

    public function getSql(SqlWalker $sqlWalker)
    {
        return 'CAST(' . $this->stringPrimary->dispatch($sqlWalker) . ' AS boolean)';
    }

    public function parse(Parser $parser)
    {
        $parser->match(Lexer::T_IDENTIFIER);
        $parser->match(Lexer::T_OPEN_PARENTHESIS);

        $this->stringPrimary = $parser->StringPrimary();

        $parser->match(Lexer::T_CLOSE_PARENTHESIS);
    }
}

А также эту очень похожую функцию приведения для приведения к целому числу:

<?php

namespace DoctrineFunctions;


use Doctrine\ORM\Query\AST\Functions\FunctionNode;
use Doctrine\ORM\Query\Lexer;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\SqlWalker;

class CastToInteger extends FunctionNode
{
    public $stringPrimary;

    public function getSql(SqlWalker $sqlWalker)
    {
        return 'CAST(' . $this->stringPrimary->dispatch($sqlWalker) . ' AS integer)';
    }

    public function parse(Parser $parser)
    {
        $parser->match(Lexer::T_IDENTIFIER);
        $parser->match(Lexer::T_OPEN_PARENTHESIS);

        $this->stringPrimary = $parser->StringPrimary();

        $parser->match(Lexer::T_CLOSE_PARENTHESIS);
    }
}

Затем я регистрирую эти методы в doctrine.yml:

orm:
    auto_generate_proxy_classes: ""
    default_entity_manager: default
    entity_managers:
        default:
            connection: default
            mappings:
                RBundle: ~
                ABundle: ~
            result_cache_driver:
                type: memcached
                host: ''
                port: 
            query_cache_driver:
                type: memcached
                host: ''
                port: 
            metadata_cache_driver:
                type: memcached
                host: ''
                port: 
            naming_strategy: doctrine.orm.naming_strategy.underscore
            filters:
                softdeleteable:
                    class: 
                    enabled: 
            dql:
                numeric_functions:
                    cast_to_int: DoctrineFunctions\CastToInteger
                string_functions:
                    cast_to_boolean: DoctrineFunctions\CastToBoolean

И, наконец, я реконструирую SQL-запрос как DQL-запрос со всеми необходимыми методами:

 if (isset($searchParams[self::PARAM_MINIMUM_RATING])) {
        $qb
            ->andWhere('((r.editedRatingTreatmentEffectiveness + r.editedRatingAccommodationsAmenities + r.editedRatingMealsNutrition) / (COALESCE(NULLIF(cast_to_int(cast_to_boolean(r.editedRatingTreatmentEffectiveness)) + cast_to_int(cast_to_boolean(r.editedRatingAccommodationsAmenities)) + cast_to_int(cast_to_boolean(r.editedRatingMealsNutrition)), 0), 1))) >= :minimum_rating')
            ->setParameter('minimum_rating', $searchParams[self::PARAM_MINIMUM_RATING]);
    }
...