计算实体中的字段,按理论顺序

时间:2019-05-12 09:59:25

标签: php symfony doctrine

我有一个用户实体,该实体具有一个已计算的字段totalPoints。现在,我要按总积分排序我的用户。我已经有一个可行的解决方案,但目前只有150条记录的速度确实很慢。我担心我迟早会有更多记录。

在我的用户实体中,我具有此功能来计算点数

public function getTotalPoints()
{
        $totalPoints = 0;
        $totalPoints += $this->totalLevel;
        $totalPoints += $this->getWeeksJoined() * $this->staticsRepository->findOnByName("Points Weeks Joined")->getValue();
        $totalPoints += $this->forumBumps * $this->staticsRepository->findOnByName("Points Forum Bumps")->getValue();
        $totalPoints += $this->lootSplit * $this->staticsRepository->findOnByName("Points Loot Split")->getValue();
        $totalPoints += $this->isVeteran * $this->staticsRepository->findOnByName("Points Veteran Rank")->getValue();
        $totalPoints += $this->getSotwPlaces(1)->count() * $this->staticsRepository->findOnByName("Points SOTW Wins")->getValue();
        $totalPoints += $this->getSotwPlaces(2)->count() * $this->staticsRepository->findOnByName("Points SOTW Second Place")->getValue();
        $totalPoints += $this->getSotwPlaces(3)->count() * $this->staticsRepository->findOnByName("Points SOTW 3rd Place")->getValue();
        $totalPoints += $this->osrsUserRefs->count() * $this->staticsRepository->findOnByName("Points Referals")->getValue();
        $totalPoints += $this->eventsAttended->count() * $this->staticsRepository->findOnByName("Points Events Attended")->getValue();
        $totalPoints += $this->eventsHosted->count() * $this->staticsRepository->findOnByName("Points Events Hosted")->getValue();

        if($this->discordUser != null)
            $totalPoints += $this->discordUser->getLevel() * $this->staticsRepository->findOnByName("Points Discord Level")->getValue();

        return $totalPoints;
    }

现在要按总点数对它们进行排序,就必须对用户数组进行扭曲,这很慢。

/**
* @Route("/", name="highscores_index")
*/
    public function index(OsrsUserRepository $osrsUserRepository)
    {
        $osrsUsers = $osrsUserRepository->findAllHighscores();

        usort($osrsUsers, function($a, $b)
        {
            return $b->getTotalPoints() > $a->getTotalPoints();
        });

        return $this->render('highscores/index.html.twig', [
            'users' => $osrsUsers,
        ]);
    }

在学问查询构建器中是否可以执行此操作?我没有找到有关这种排序的任何文档。

1 个答案:

答案 0 :(得分:1)

您所遇到的问题本质上是:

每次调用getTotalPoints时,您都要进行完整的计算,而每个调用都包括对staticsRepository的10次调用(联结1:为什么在f ***中有一个存储库?实体?-这是一种代码味道!)并计算子实体(每个集合+1查询)。

我真的很希望您的staticsRepository不依赖于用户 ,因为在这种情况下,查询甚至不会被缓存。就您而言,他们可能不是。

因此,您实际上拥有的是the n+1 problem (see no. 5)

可能可以编写一个专门的查询,该查询将为数据库中的所有用户计算totalScore,但是老实说,我并不是在这里教您DQL或SQL 。但是,请进行研究,否则您浪费了太多的改进潜力……

现在,有一些选项(列表不完整):

  1. 每次都在数据库中完成所有操作。在这种情况下,您可以向UserRepository添加一个函数,该函数针对一个User执行查询,或者返回所有用户(包括他们的总得分)。创建一个包含该查询为字段的数据库视图可以使此代码对您的代码甚至透明。但也可以在querybuilder中完成,也就是DQL,...或SQL。也许您需要扩展学说。

    这种解决方案对于性能极高的人来说是合理的,并且总是会产生足够近的结果

  2. 在php中实现逻辑,将结果存储在数据库中(为totalScore添加新的db字段),在以下位置的便捷点重新计算时间。如果正确完成,此解决方案将非常有效,但可能会产生一些过时的结果。 (方便的时间点就像是……每天午夜或每小时一次,或每5分钟-如果您的服务器可以阻止它,请添加某种方式来确定分数是否已更改:标记所有更改分数的时间,存储比分的时间戳...)

    此解决方案在某种程度上是高性能的,它是更新(可能将通过cron作业完成),并且始终会产生正确的最新结果。 -最近==您的间隔。本质上,它简化了您当前的方法...结合1获得非常好的效果。

  3. 使用聚合字段,从本质上说,它会更新数据库中的分数-几乎像2中那样,但是-每当它改变时。看一下aggregate fields并弄清楚如何使其适应您的用例。请注意比赛条件以及如何避免比赛。根据您的具体方法,这可能会降低许多操作的性能,但是在要求高分时会大大提高性能...

    它在高分上表现非常出色,并且总是会产生足够近的结果,但您必须处理比赛条件和所有积分更新,这可能非常烦人并且容易出错!

我的选择是1。这要求计算结果可以用sql或dql表示,这也许可以做到。