Я столкнулся с тем же вопросом/проблемой и пришел к тому же выводу, что и @Dan Crews. Курсор должен содержать все необходимое для выполнения запроса к базе данных, кроме LIMIT
.
Когда ваш первоначальный запрос выглядит примерно так
SELECT *
FROM DataTable
WHERE filterField = 42
ORDER BY sortingField,ASC
LIMIT 10
-- with implicit OFFSET 0
тогда вы могли бы в основном (не делать это в реальном приложении из-за SQL-инъекций!) использовать именно этот запрос в качестве курсора. Вам просто нужно удалить LIMIT x
и добавить OFFSET y
для каждого узла.
Ответ:
{
edges: [
{
cursor: "SELECT ... WHERE ... ORDER BY ... OFFSET 0",
node: { ... }
},
{
cursor: "SELECT ... WHERE ... ORDER BY ... OFFSET 1",
node: { ... }
},
...,
{
cursor: "SELECT ... WHERE ... ORDER BY ... OFFSET 9",
node: { ... }
}
]
pageInfo: {
startCursor: "SELECT ... WHERE ... ORDER BY ... OFFSET 0"
endCursor: "SELECT ... WHERE ... ORDER BY ... OFFSET 9"
}
}
Следующий запрос будет использовать after: CURSOR, first: 10
. Затем вы возьмете аргумент after
и установите LIMIT
и OFFSET
:
LIMIT = first
OFFSET = OFFSET + 1
Тогда результирующий запрос к базе данных будет таким при использовании after = endCursor
:
SELECT *
FROM DataTable
WHERE filterField = 42
ORDER BY sortingField,ASC
LIMIT 10
OFFSET 10
Как уже упоминалось выше: Это всего лишь пример, и он очень уязвим для SQL-инъекций!
В реальном приложении вы можете просто закодировать предоставленные аргументы filter
и orderBy
внутри курсора, а также добавить offset
:
function handleGraphQLRequest(first, after, filter, orderBy) {
let offset = 0; // initial offset, if after isn't provided
if(after != null) {
// combination of after + filter/orderBy is not allowed!
if(filter != null || orderBy != null) {
throw new Error("You can't combine after with filter and/or orderBy");
}
// parse filter, orderBy, offset from after cursor
cursorData = fromBase64String(after);
filter = cursorData.filter;
orderBy = cursorData.orderBy;
offset = cursorData.offset;
}
const databaseResult = executeDatabaseQuery(
filter, // = WHERE ...
orderBy, // = ORDER BY ...
first, // = LIMIT ...
offset // = OFFSET ...
);
const edges = []; // this is the resulting edges array
let currentOffset = offset; // this is used to calc the offset for each node
for(let node of databaseResult.nodes) { // iterate over the database results
currentOffset++;
const currentCursor = createCursorForNode(filter, orderBy, currentOffset);
edges.push({
cursor = currentCursor,
node = node
});
}
return {
edges: edges,
pageInfo: buildPageInfo(edges, totalCount, offset) // instead of
// of providing totalCount, you could also fetch (limit+1) from
// database to check if there is a next page available
}
}
// this function returns the cursor string
function createCursorForNode(filter, orderBy, offset) {
return toBase64String({
filter: filter,
orderBy: orderBy,
offset: offset
});
}
// function to build pageInfo object
function buildPageInfo(edges, totalCount, offset) {
return {
startCursor: edges.length ? edges[0].cursor : null,
endCursor: edges.length ? edges[edges.length - 1].cursor : null,
hasPreviousPage: offset > 0 && totalCount > 0,
hasNextPage: offset + edges.length < totalCount
}
}
Содержимое cursor
зависит главным образом от вашей базы данных и ее макета.
Приведенный выше код эмулирует простую разбивку на страницы с ограничением и смещением. Но вы могли бы (если поддерживается вашей базой данных), конечно, использовать что-то еще.
person
Benjamin M
schedule
09.02.2019