Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fetching users that have events in a certain time interval #6

Open
lucadegasperi opened this issue May 31, 2018 · 0 comments
Open

Fetching users that have events in a certain time interval #6

lucadegasperi opened this issue May 31, 2018 · 0 comments

Comments

@lucadegasperi
Copy link

Hello Jonathan,
Following your reply to the email I wrote you, Here's the problem I'm facing in more detail:

Given an events table that has begins_at, ends_at and user_id fields, the query I want to run should return the users (plus a relationship on the users table) that have at least an event in a given hour. The problem is that I need to run this query for every hour in a day. Totaling 24 queries.
What I came up with so far is the following query scope on the User model:

public function scopeWithEventsBetween($query, Date $beginning, Date $ending)
{
    return $query->whereHas('events', function ($query) use ($beginning, $ending) {
        $query->between($beginning, $ending);
    });
}

and this scope on the Event model:

public function scopeBetween($query, Date $beginning, Date $ending)
{
    return $query->where(function ($query) use ($beginning, $ending) {

        // ENDS IN THE INTERVAL
        $query->where($this->getTable() .'.ends_at', '>', $beginning)->where($this->getTable() . '.ends_at', '<=', $ending);

    })->orWhere(function ($query) use ($beginning, $ending) {

        // BEGINS IN THE INTERVAL
        $query->where($this->getTable() . '.begins_at', '>=', $beginning)->where($this->getTable() . '.begins_at', '<', $ending);

    })->orWhere(function ($query) use ($beginning, $ending) {

        // BEGINS AND ENDS OUTSIDE THE INTERVAL
        $query->where($this->getTable() . '.begins_at', '<', $beginning)->where($this->getTable() . '.ends_at', '>', $ending);

    });
}

The code to get the users with event in the next 24 hours is the following:

$situation = collect();
$beginning = Date::today();
$ending = Date::today()->addDay();

for ($day = $beginning->copy(); $day->lt($ending); $day->addHour()) {
    $usersForHour = User::with('skills')->withEventsBetween(
        $day->copy(),
        $day->copy()->addHour(),
    )->get();

    $situation->push($usersForHour);
}

I've tried to simplify the problem as much as possible but on my production database this fairly simple query makes the CPU jump to 100% (only caused by mysql) and the page averages 12 seconds to load (sometimes even 20s depending on the number of events). This makes it impossible to increase the time resolution to 30mins or 15mins as the number of queries would double or quadruple and the page load time would skyrocket.

So I wanted to know if you might help me figuring out a way to fetch the users in a more performant way. I'm fine even with just the user ids being fetched.

For the sake of being precise the actual query scope I'm running is the following:

public function scopeSituationBetween($query, Date $beginning, Date $ending)
{
    // has a shift but is not absent
    // present
    // substituting someone

    return $query->where(function ($query) use ($beginning, $ending) {
        $query->whereHas('shifts', function ($query) use ($beginning, $ending) {
            $query->between($beginning, $ending);
        })->whereDoesntHave('availabilities', function ($query) use ($beginning, $ending) {
            $query->between($beginning, $ending)->whereStatus('unavailable');
        });
    })->orWhere(function ($query) use ($beginning, $ending) {
        $query->whereHas('availabilities', function ($query) use ($beginning, $ending) {
            $query->between($beginning, $ending)->whereStatus('available');
        });
    })->orWhere(function ($query) use ($beginning, $ending) {
        $query->whereHas('shiftSubstitutions', function ($query) use ($beginning, $ending) {
            $query->between($beginning, $ending);
        });
    });
}

Thank you for your time.
Luca

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant