нумерация клавиш с полнотекстовым поиском на postgresql - PullRequest
1 голос
/ 08 февраля 2020

У меня есть таблица «пользователи» с +100 000 записей. Я хочу начать использовать пагинацию набора ключей, чтобы ускорить процесс извлечения записей.

Следующий запрос работает. Этот запрос извлекает страницу набора записей second (начиная с user_id: 1001 и вплоть до user_id: 2000).

SELECT
    user_id,
    username
FROM
    users
WHERE
    user_id > 1000
ORDER BY
    user_id ASC
LIMIT
    1000

Проблема в том, что я не хочу заказывать записи на user_id. У меня есть столбец с именем «токены», который является столбцом to_tsvector. Я хочу выполнить полнотекстовый поиск по набору записей и упорядочить пользователей по рангу. Новый запрос:

SELECT
    user_id,
    username,
    to_tsrank(tokens, plainto_tsquery('search query')) AS rank
FROM
    users
WHERE
    tokens @@ plainto_tsquery('search query')

Как применить пагинацию набора ключей к этому второму запросу, чтобы результаты упорядочивались по рангу вместо user_id?

Важно:

Я пробовал это, но это не работает!

SELECT
    user_id,
    username,
    to_tsrank(tokens, plainto_tsquery('search query')) AS rank
FROM
    users
WHERE
    tokens @@ plainto_tsquery('search query')
AND
    to_tsrank(tokens, plainto_tsquery('search query')) < $1 // $1 = last fetched rank
ORDER BY
    rank DESC
LIMIT
    1000

Скажем, когда результаты ранжируются по «рангу», тысячный результат имеет ранг 0,5. $ 1 (последний выбранный ранг) будет 0,5, поэтому я бы выбрал все результаты с рангом <0,5. Проблема в том, что некоторые результаты могут иметь <strong>такой же ранг. Поэтому, если 1 001-я запись также будет иметь ранг = 0,5, она не будет выбрана, потому что я говорю в своем запросе rank < 0.5. Я также не могу сказать rank <= 0.5, потому что это снова приведет к получению предыдущих результатов с рангом = 0,5.

Кто-нибудь знает решение этой проблемы?

1 Ответ

1 голос
/ 08 февраля 2020

Вы должны предоставить полностью определенную информацию c ORDER BY. Предполагая, что user_id уникален:

ORDER BY rank desc, user_id

Тогда ваш WHERE будет включать в себя:

AND (rank < :last_rank or (rank = :last_rank and user_id > :last_user_id))

Но это не будет эффективным, так что вы также можете просто сделать OFFSET.

Еще лучше, не делай этого вообще. Ни один человек реально не собирается читать 1000 результатов и думать: «Знаете, я бы хотел сделать это еще несколько раз». Единственный, кто это сделает, - это веб-скребок, и единственная причина, по которой он это сделает, - это то, что это единственный метод, который вы предлагаете. Просто позвольте им установить ПРЕДЕЛ, который так велик, как они хотят, и не предлагайте нумерацию страниц.

...