VoteQuery.php 3.96 KB
<?php

namespace FootyRoom\Queries;

use FootyRoom\Queries\Vote\Vote;
use FootyRoom\Support\AutoMapper;
use FootyRoom\Support\MongoClient;

class VoteQuery
{
    /**
     * @var \FootyRoom\Support\MongoClient
     */
    protected $mongo;

    /**
     * Constructor.
     * 
     * @param \FootyRoom\Support\MongoClient $mongo
     */
    public function __construct(MongoClient $mongo)
    {
        $this->mongo = $mongo->footyroom;
    }

    /**
     * Finds votes by specified tracker that have no user id.
     *
     * @param string $pollRef
     * @param string $tracker
     * @param array $fields
     *
     * @return array
     */
    public function findByTrackerOnly($pollRef, $tracker, $fields = []): array
    {
        $cursor = $this->mongo->votes->find([
            'pollRef' => $pollRef,
            'tracker' => $tracker,
            'userId' => ['$exists' => false],
        ], ['projection' => $fields]);

        return iterator_to_array($cursor);
    }

    /**
     * Finds votes by specified user id.
     *
     * @param string $pollRef
     * @param int $userId
     * @param array $fields
     *
     * @return array
     */
    public function findByUserId($pollRef, $userId, $fields = []): array
    {
        $cursor = $this->mongo->votes->find([
            'pollRef' => $pollRef,
            'userId' => $userId ?: ['$exists' => false],
        ], ['projection' => $fields]);

        return iterator_to_array($cursor, false);
    }

    /**
     * Returns an aggregate of votes for specified poll.
     *
     * @param string $pollId
     * @param int $limit
     *
     * @return object
     */
    public function aggregate($pollId, $limit = null)
    {
        $pipeline[] = ['$match' => ['pollRef' => $pollId]];

        $pipeline[] = ['$group' => [
            '_id' => ['value' => '$value', 'valueRef' => '$valueRef'],
            'votes' => ['$sum' => 1],
        ]];

        $pipeline[] = ['$project' => [
            'vote' => ['votes' => '$votes', 'value' => '$_id.value', 'valueRef' => '$_id.valueRef'],
        ]];

        $pipeline[] = ['$group' => [
            '_id' => $pollId,
            'votes' => ['$addToSet' => '$vote'],
            'totalVotes' => ['$sum' => '$vote.votes'],
            'maxVotes' => ['$max' => '$vote.votes'],
        ]];

        if (isset($limit)) {
            $pipeline[] = ['$unwind' => '$votes'];
            $pipeline[] = ['$sort' => ['votes.votes' => -1]];
            $pipeline[] = ['$limit' => $limit];
            $pipeline[] = ['$group' => [
                '_id' => $pollId,
                'votes' => ['$addToSet' => '$votes'],
                'totalVotes' => ['$first' => '$totalVotes'],
                'maxVotes' => ['$first' => '$maxVotes'],
            ]];
        }

        $pipeline[] = ['$project' => [
            'pollRef' => '$_id', 'votes' => 1, 'totalVotes' => 1, 'maxVotes' => 1, '_id' => 0, ],
        ];

        $result = $this->mongo->votes->aggregate($pipeline)->toArray();

        if (!isset($result[0])) {
            return null;
        }

        return $result[0];
    }

    /**
     * Finds votes by specified pollRef.
     *
     * @return \FootyRoom\Queries\Vote\Vote[]
     */
    public function findByPollRef(string $pollRef): array
    {
        $cursor = $this->mongo->votes->aggregate([
            ['$match' => [
                'pollRef' => $pollRef,
                'userId' => [
                    '$exists' => true,
                    '$ne' => null,
                ]
            ]],
            ['$lookup' => [
                'from' => 'users',
                'localField' => 'userId',
                'foreignField' => 'userId',
                'as' => 'user',
            ]],
            ['$project' => [
                '_id' => 0,
                'value' => 1,
                'username' => ['$arrayElemAt' => ['$user.username', 0]]
            ]],
        ]);

        foreach ($cursor as $vote) {
            $votes[] = AutoMapper::map($vote, Vote::class);
        }

        return $votes ?? [];
    }
}