Я бы ЛЮБОВАЛ, чтобы был лучший способ, но вот...
Одна вещь, которую я обнаружил с курсорами, заключается в том, что они возвращают одно и то же значение для повторяющихся поисковых запросов при поиске в одном и том же наборе данных, поэтому не думайте о них как о сеансах. Пока ваши данные не обновляются, вы можете эффективно кэшировать аспекты разбиения на страницы для использования несколькими пользователями.
Я придумал это решение и протестировал его с более чем 75 000 записей.
1) Определите, будет ли ваш старт меньше 10 КБ, если это так, используйте поиск без курсора, в противном случае при поиске за пределами 10 КБ сначала выполните поиск с курсором initial
и размером 10 КБ. и вернуть _no_fields
. Это дает наше начальное смещение, а отсутствие полей ускоряет объем данных, которые мы должны потреблять, нам все равно не нужны эти идентификаторы.
2) Выясните целевое смещение и спланируйте, сколько итераций потребуется, чтобы установить курсор непосредственно перед целевой страницей результатов. Затем я повторяю и кеширую результаты, используя свой запрос в качестве хэша кеша.
Для моей итерации я начал с блоков 10 КБ, затем уменьшил размер до 5 КБ, а затем блоков 1 КБ, поскольку я начинаю приближаться к целевому смещению, это означает, что последующая разбивка на страницы использует предыдущий курсор, который немного ближе к последнему фрагменту.
например, как это может выглядеть:
- Получить 10000 записей (начальный курсор)
- Получить 5000 записей
- Получить 5000 записей
- Получить 5000 записей
- Получить 5000 записей
- Получить 1000 записей
- Получить 1000 записей
Это поможет мне добраться до блока, который находится около отметки смещения 32 000. Если мне затем нужно добраться до 33 000, я могу использовать свои кешированные результаты, чтобы получить курсор, который вернет предыдущие 1000, и начать снова с этого смещения...
- Получить 10000 записей (кэшировано)
- Получить 5000 записей (кэшировано)
- Получить 5000 записей (кэшировано)
- Получить 5000 записей (кэшировано)
- Получить 5000 записей (кэшировано)
- Получить 1000 записей (кэшировано)
- Получить 1000 записей (кэшировано)
- Выбрать 1000 записей (работает с использованием кэшированного курсора)
3) теперь, когда мы находимся "по соседству" с вашим целевым смещением результата, вы можете начать указывать размеры страницы непосредственно перед вашим пунктом назначения. а затем вы выполняете окончательный поиск, чтобы получить фактическую страницу результатов.
4) Если вы добавляете или удаляете документы из своего индекса, вам понадобится механизм для аннулирования ваших предыдущих кэшированных результатов. Я сделал это, сохранив отметку времени последнего обновления индекса и используя ее как часть процедуры генерации ключа кэша.
Что важно, так это аспект кеша, вы должны создать механизм кеша, который использует массив запросов в качестве хеш-ключа кеша, чтобы его можно было легко создать/ссылаться.
Для незаполненного кеша этот подход МЕДЛЕННЫЙ, но если вы можете разогреть кеш и истечь только при изменении проиндексированных документов (а затем снова разогреть), ваши пользователи будут не могу сказать.
Эта идея кода работает с 20 элементами на странице, я бы хотел поработать над этим и посмотреть, как я могу кодировать его умнее/эффективнее, но концепция есть...
// Build $request here and set $request['start'] to be the offset you want to reach
// Craft getCache() and setCache() functions or methods for cache handling.
// have $cloudSearchClient as your client
if(isset($request['start']) === true and $request['start'] >= 10000)
{
$originalRequest = $request;
$cursorSeekTarget = $request['start'];
$cursorSeekAmount = 10000; // first one should be 10K since there's no pagination under this
$cursorSeekOffset = 0;
$request['return'] = '_no_fields';
$request['cursor'] = 'initial';
unset($request['start'],$request['facet']);
// While there is outstanding work to be done...
while( $cursorSeekAmount > 0 )
{
$request['size'] = $cursorSeekAmount;
// first hit the local cache
if(empty($result = getCache($request)) === true)
{
$result = $cloudSearchClient->Search($request);
// store the results in the cache
setCache($request,$result);
}
if(empty($result) === false and empty( $hits = $result->get('hits') ) === false and empty( $hits['hit'] ) === false )
{
// prepare the next request with the cursor
$request['cursor'] = $hits['cursor'];
}
$cursorSeekOffset = $cursorSeekOffset + $request['size'];
if($cursorSeekOffset >= $cursorSeekTarget)
{
$cursorSeekAmount = 0; // Finished, no more work
}
// the first request needs to get 10k, but after than only get 5K
elseif($cursorSeekAmount >= 10000 and ($cursorSeekTarget - $cursorSeekOffset) > 5000)
{
$cursorSeekAmount = 5000;
}
elseif(($cursorSeekOffset + $cursorSeekAmount) > $cursorSeekTarget)
{
$cursorSeekAmount = $cursorSeekTarget - $cursorSeekOffset;
// if we still need to seek more than 5K records, limit it back again to 5K
if($cursorSeekAmount > 5000)
{
$cursorSeekAmount = 5000;
}
// if we still need to seek more than 1K records, limit it back again to 1K
elseif($cursorSeekAmount > 1000)
{
$cursorSeekAmount = 1000;
}
}
}
// Restore aspects of the original request (the actual 20 items)
$request['size'] = 20;
$request['facet'] = $originalRequest['facet'];
unset($request['return']); // get the default returns
if(empty($result = getCache($request)) === true)
{
$result = $cloudSearchClient->Search($request);
setCache($request,$result);
}
}
else
{
// No cursor required
$result = $cloudSearchClient->Search( $request );
}
Обратите внимание, что это было сделано с использованием пользовательского клиента AWS, а не официального класса SDK, но структуры запроса и поиска должны быть сопоставимы.
person
Scuzzy
schedule
05.06.2015