Подтвердить что ты не робот

Как выбирать случайным образом с доктриной

Вот как я запрашиваю мою базу данных для некоторых слов

$query = $qb->select('w')
    ->from('DbEntities\Entity\Word', 'w')
    ->where('w.indictionary = 0 AND w.frequency > 3')
    ->orderBy('w.frequency', 'DESC')
    ->getQuery()
    ->setMaxResults(100);

Я использую mysql, и я хотел бы получить случайные строки, соответствующие критериям, я бы использовал порядок по rand() в моем запросе.

Я нашел этот похожий вопрос, который в основном предполагает, поскольку ORDER BY RAND не поддерживается в доктрине, вместо этого вы можете рандомизировать первичный ключ. Однако этого не может быть сделано в моем случае, потому что у меня есть критерии поиска и предложение where, чтобы не каждый первичный ключ удовлетворял этому условию.

Я также нашел фрагмент кода, который предлагает использовать OFFSET для рандомизации строк следующим образом:

$userCount = Doctrine::getTable('User')
     ->createQuery()
     ->select('count(*)')
     ->fetchOne(array(), Doctrine::HYDRATE_NONE); 
$user = Doctrine::getTable('User')
     ->createQuery()
     ->limit(1)
     ->offset(rand(0, $userCount[0] - 1))
     ->fetchOne();

Я немного смущен в отношении того, поможет ли это мне обойти отсутствие поддержки для заказа случайным в моем случае или нет. Мне не удалось добавить смещение после setMaxResult.

Любая идея, как это можно сделать?

4b9b3361

Ответ 1

Команда Doctrine не хочет реализовывать эту функцию.

В вашей проблеме есть несколько решений, каждый из которых имеет свои недостатки:

  • Добавьте пользовательскую числовую функцию: см. эту функцию DQL RAND()
    (может быть медленным, если у вас много подходящих строк)
  • Используйте собственный запрос
    (Я лично стараюсь избегать этого решения, которое мне было трудно поддерживать)
  • Сначала выведите исходный SQL-запрос, чтобы получить некоторые идентификаторы случайным образом, затем используйте DQL WHERE x.id IN(?) для загрузки связанных объектов, передав массив идентификаторов в качестве параметра.
    Это решение включает в себя два отдельных запроса, но может дать лучшую производительность, чем первое решение (существуют другие методы raw SQL, чем ORDER BY RAND(), я не буду подробно их описывать здесь, вы найдете некоторые полезные ресурсы на этом сайте).

Ответ 2

Выполните следующие действия:

Определите новый класс в вашем проекте как:

namespace My\Custom\Doctrine2\Function;

use Doctrine\ORM\Query\Lexer;

class Rand extends \Doctrine\ORM\Query\AST\Functions\FunctionNode
{

    public function parse(\Doctrine\ORM\Query\Parser $parser)
    {
        $parser->match(Lexer::T_IDENTIFIER);
        $parser->match(Lexer::T_OPEN_PARENTHESIS);
        $parser->match(Lexer::T_CLOSE_PARENTHESIS);
    }

    public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
    {
        return 'RAND()';
    }
}

Зарегистрируйте класс config.yml:

doctrine:
     orm:
         dql:
             numeric_functions:
                 Rand: My\Custom\Doctrine2\Function\Rand

Используйте его как:

$qb->addSelect('RAND() as HIDDEN rand')->orderBy('rand');

Ответ 3

Или вы можете это сделать →

$words = $em->getRepository('Entity\Word')->findAll();
shuffle($words);

Конечно, это было бы очень неэффективно, если бы у вас было много записей, поэтому используйте их с осторожностью.

Ответ 4

Doctrine 2 не поддерживает ORDER BY rand(), но я нашел эту статью, в которой содержатся исправления для этого.

Ответ 5

Почему бы не использовать репозиторий?

<?php

namespace Project\ProductsBundle\Entity;

use Doctrine\ORM;

class ProductRepository extends ORM\EntityRepository
{
    /**
     * @param int $amount
     * @return Product[]
     */
    public function getRandomProducts($amount = 7)
    {
        return $this->getRandomProductsNativeQuery($amount)->getResult();
    }

    /**
     * @param int $amount
     * @return ORM\NativeQuery
     */
    public function getRandomProductsNativeQuery($amount = 7)
    {
        # set entity name
        $table = $this->getClassMetadata()
            ->getTableName();

        # create rsm object
        $rsm = new ORM\Query\ResultSetMapping();
        $rsm->addEntityResult($this->getEntityName(), 'p');
        $rsm->addFieldResult('p', 'id', 'id');

        # make query
        return $this->getEntityManager()->createNativeQuery("
            SELECT p.id FROM {$table} p ORDER BY RAND() LIMIT 0, {$amount}
        ", $rsm);
    }
}

Ответ 6

В соответствии с тем, что предложил Hassan Magdy Saad , вы можете использовать популярный DoctrineExtensions библиотека:

# config.yml

doctrine:
     orm:
         dql:
             numeric_functions:
                 rand: DoctrineExtensions\Query\Mysql\Rand

Протестировано в Doctrine ORM 2.6.x-dev, вы можете тогда сделать:

->orderBy('RAND()')

Ответ 7

Перетасовка может выполняться в результате запроса (массива), но перетасовка не выбирается случайным образом.

Чтобы случайно выбирать из сущности, я предпочитаю делать это на PHP, что может замедлить случайный выбор, но это позволяет мне контролировать контроль над тем, что я делаю, и облегчить возможную отладку.

Приведенный ниже пример помещает все идентификаторы из объекта в массив, который затем я могу использовать для "случайного лечения" в php.

public function getRandomArt($nbSlotsOnPage)
{
    $qbList=$this->createQueryBuilder('a');

    // get all the relevant id from the entity
    $qbList ->select('a.id')
            ->where('a.publicate=true')
            ;       
    // $list is not a simple list of values, but an nested associative array
    $list=$qbList->getQuery()->getScalarResult();       

    // get rid of the nested array from ScalarResult
    $rawlist=array();
    foreach ($list as $keyword=>$value)
        {
            // entity id have to figure as keyword as array_rand() will pick only keywords - not values
            $id=$value['id'];
            $rawlist[$id]=null;
        }

    $total=min($nbSlotsOnPage,count($rawlist));
    // pick only a few (i.e.$total)
    $keylist=array_rand($rawlist,$total);

    $qb=$this->createQueryBuilder('aw');
    foreach ($keylist as $keyword=>$value)
        {
            $qb ->setParameter('keyword'.$keyword,$value)
                ->orWhere('aw.id = :keyword'.$keyword)
            ;
        }

    $result=$qb->getQuery()->getResult();

    // if mixing the results is also required (could also be done by orderby rand();
    shuffle($result);

    return $result;
}

Ответ 8

Я надеюсь, что это поможет другим:

        $limit = $editForm->get('numberOfQuestions')->getData();
        $sql = "Select * from question order by RAND() limit $limit";

        $statement = $em->getConnection()->prepare($sql);
        $statement->execute();
        $questions = $statement->fetchAll();

Обратите внимание, что этот вопрос таблицы представляет собой AppBundle: Question Entity. Соответственно измените данные. Количество вопросов берется из формы редактирования, обязательно проверяйте переменную для построителя форм и используйте соответственно.

Ответ 9

Я знаю, что это старый вопрос. Но я использовал следующее решение для получения случайной строки.

Использование метода EntityRepository:

public function findOneRandom()
{
    $id_limits = $this->createQueryBuilder('entity')
        ->select('MIN(entity.id)', 'MAX(entity.id)')
        ->getQuery()
        ->getOneOrNullResult();
    $random_possible_id = rand($id_limits[1], $id_limits[2]);

    return $this->createQueryBuilder('entity')
        ->where('entity.id >= :random_id')
        ->setParameter('random_id', $random_possible_id)
        ->setMaxResults(1)
        ->getQuery()
        ->getOneOrNullResult();
}

Ответ 10

Просто добавьте следующее:

->orderBy('RAND()')