Извлечение кэшированной коллекции Eloquent из 10 элементов - PullRequest
0 голосов
/ 30 декабря 2018

Я создаю функцию поиска, которая возвращает большие коллекции, разбитые на страницы с использованием LengthAwarePaginator.Я пытаюсь кэшировать результаты, используя ключ с именем $ searchFilter_ $ query_ $ offsetPages для одной страницы возвращаемых результатов (10 элементов).Идет в кеш просто отлично.Однако время ожидания истекает, когда я пытаюсь проверить с помощью Cache :: has ($ key) или извлечь с помощью Cache :: get ($ key).

Та же проблема возникает в браузере, а также в ремесленном Tinker.,Как ни странно, когда я помещаю случайный набор из 10 элементов в кэш в Tinker и извлекаю их обратно, все работает нормально.Я использую Redis в качестве драйвера кеша.

Вот мой метод контроллера:

public function search($filter, $query, $layout, Request $request) {
  if($layout == "list-map") {
    return view("list-map")->with(['filter' => $filter, 'query' => $query, 'layout' => 'list-map']);      
  } else {
    $offsetPages = $request->input('page', 1) - 1;
    $cacheKey = $filter . "_" . $query . "_" . $offsetPages;

    if(Cache::has($cacheKey)) {
      \Log::info("fetching results from cache");
      $data = Cache::get($cacheKey);

      $totalCt = $data[0];
      $results = $data[1];
    } else {
      $results = $this->getResults($filter, $query);
      $totalCt = $results->count();
      $results = $results->slice($offsetPages, $this->resultsPerPage);

      \Log::info("caching results");
      Cache::put($cacheKey, [$totalCt, $results], 5);
    }

  $results = new LengthAwarePaginator($results,
    $totalCt,
    $this->resultsPerPage,
    $request->input('page', 1),
    ['path' => LengthAwarePaginator::resolveCurrentPath()]
  );

    return view($layout)->with(['filter' => $filter, 'query' => $query, 'layout' => $layout, 'results' => $results]);
  }

}

Ответы [ 2 ]

0 голосов
/ 31 декабря 2018

Таким образом, проблема заключалась в том, что многие модели в коллекции, возвращенные из моего метода getResults (), были получены с помощью запросов отношений.Когда я использовал dd ($ results) на одной странице из 10 результатов, я мог видеть, что в каждой модели есть поле «отношения».Внутри этого массива были тысячи рекурсивно связанных моделей, основанных на отношениях, которые я первоначально запрашивал.Мне не удалось найти какую-либо информацию о возможности не загружать эти связанные модели.Вместо этого я придумал хакерский обходной путь для непосредственного извлечения моделей:

  $results = $results->slice($offsetPages, $this->resultsPerPage);

      //load models directly so they don't include related models.
      $temp = new \Illuminate\Database\Eloquent\Collection;
      foreach($results as $result) {
        if(get_class($result) == "App\Doctor") {
          $result = Doctor::find($result->id);
        } else if(get_class($result == "App\Organization")) {
          $result = Organization::find($result->id);
        }
        $temp->push($result);
      }
      $results = $temp;

      \Log::info("caching results");
      Cache::put($cacheKey, [$totalCt, $results], 5);

Если кто-нибудь знает наилучшую практику в этой ситуации, пожалуйста, дайте мне знать.Спасибо!

Редактировать: я нашел лучшее решение вместо вышеуказанного обходного пути.Если я запрашиваю мои отношения следующим образом: $ taxonomy-> докторов () -> get (), а не $ taxonomy-> докторов, он не загружается в огромных рекурсивных отношениях.

0 голосов
/ 31 декабря 2018

Я действительно не понимаю, почему ваш код не работает.Единственными потенциальными проблемами, которые я вижу, являются ключи кеша, которые могут содержать проблемные символы, а также способ проверки кэшированного значения.Поскольку вы используете Cache::has($key) до Cache::get($key), вы можете получить состояние гонки, при котором первый вызов возвращает true, а последний null, поскольку значение в кэше истекло сразу между двумя вызовами.

Я попытался решить обе проблемы в следующем фрагменте:

public function search($filter, $query, $layout, Request $request)
{
    if($layout == "list-map") {
        return view("list-map")->with(['filter' => $filter, 'query' => $query, 'layout' => 'list-map']);      
    } else {
        $offsetPages = $request->input('page', 1) - 1;
        $cacheKey = md5("{$filter}_{$query}_{$offsetPages}");
        $duration = 5; // todo: make this configurable or a constant

        [$totalCount, $results] = Cache::remember($cacheKey, $duration, function () use ($filter, $query) {
            $results = $this->getResults($filter, $query);
            $totalCount = $results->count();
            $filteredResults = $results->slice($offsetPages, $this->resultsPerPage);

            return [$totalCount, $filteredResults];
        });

        $results = new LengthAwarePaginator($results,
            $totalCount,
            $this->resultsPerPage,
            $request->input('page', 1),
            ['path' => LengthAwarePaginator::resolveCurrentPath()]
        );

        return view($layout)->with(compact('filter', 'query', 'layout', 'results'));
    }
}

Встроенная функция Cache::remember() не использует Cache::has() под капотом.Вместо этого он просто позвонит Cache::get().Поскольку эта функция будет возвращать null по умолчанию, если кэш не был задействован, функция может легко определить, должно ли оно выполнить закрытие или нет.

Я также обернул $cacheKey в md5(), чтодает последовательно действительный ключ.


Глядя на следующую часть вашего кода

$results = $this->getResults($filter, $query);
$totalCount = $results->count();
$filteredResults = $results->slice($offsetPages, $this->resultsPerPage);

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...