ЁЯУЪ рдзрдбрд╛ 08 тАФ Pagination рдЖрдгрд┐ filtering: "рдкреБрдврдЪреЗ 20, рдХреГрдкрдпрд╛"
ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: 12 рдкреИрдХреА рдзрдбрд╛ 08 ┬╖ рдорд╛рдЧреЗ: lesson-07-errors-idempotency ┬╖ рдкреБрдвреЗ: lesson-09-versioning
ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ
рдзрдбреЗ 01тАУ07, рдЖрдгрд┐ рддреНрдпрд╛рд╕реЛрдмрдд рджрд╣рд╛ рд╣рдЬрд╛рд░ drawers рдЕрд╕рд▓реЗрд▓реЗ рдХрдкрд╛рдЯ counter рдХрд╕реЗ рд╕реЛрдкрд╡рддреЛ: filter тЖТ sort тЖТ paginate, рдЖрдгрд┐ рд╡рд╛рдврдд рдЬрд╛рдгрд╛рд▒реНрдпрд╛ рдХреЛрдгрддреНрдпрд╛рд╣реА рдЧреЛрд╖реНрдЯреАрд╡рд░ cursors offsets рдкреЗрдХреНрд╖рд╛ рд╕рд░рд╕ рдХрд╛ рдард░рддрд╛рдд.
ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ
Clerk рдХрдбреЗ рдХреЛрдгреАрдЪ "рд╕рдЧрд│реЗ рд╡рд┐рджреНрдпрд╛рд░реНрдереА" рдорд╛рдЧрдд рдирд╛рд╣реА. рддреБрдореНрд╣реА рдкреБрдврдЪреЗ
рдереЛрдбреЗ рдорд╛рдЧрддрд╛: ?limit=2. Clerk рджреЛрди drawers рдЖрдгрд┐ рдПрдХ рдЦреВрдг (bookmark) рджреЗрддреЛ тАФ
next_cursor: 2, "рдореА id 2 рдирдВрддрд░ рдерд╛рдВрдмрд▓реЛ". рдкреБрдврдЪреНрдпрд╛ рд╡реЗрд│реА рддреБрдореНрд╣реА
?limit=2&cursor=2 рдореНрд╣рдгрддрд╛ рдЖрдгрд┐ рддреНрдпрд╛ рдЦреБрдгреЗрдЪреНрдпрд╛ рдирдВрддрд░рдЪреЗ рджреЛрди рдорд┐рд│рддрд╛рдд. рдЬреЗрд╡реНрд╣рд╛
рдЦреВрдг null рдореНрд╣рдгреВрди рдкрд░рдд рдпреЗрддреЗ, рддреЗрд╡реНрд╣рд╛ рдХрдкрд╛рдЯ рд╕рдВрдкрд▓реЗ.
"рдкрд╛рди 7" рдРрд╡рдЬреА рдЦреВрдг рдХрд╛? рдХрд╛рд░рдг рддреБрдореНрд╣реА рд╡рд╛рдЪрдд рдЕрд╕рддрд╛рдирд╛ рдХрдкрд╛рдЯ рд╡рд╛рдврдд рдЕрд╕рддреЗ. рддреБрдореНрд╣реА рдкрд╛рди 3 рд╡рд░ рдЕрд╕рддрд╛рдирд╛ рдирд╡реАрди рд╡рд┐рджреНрдпрд╛рд░реНрдереА рдиреЛрдВрджрд▓рд╛ рдЧреЗрд▓рд╛ рддрд░, "рдкрд╛рди 4" рдЖрддрд╛ рдПрдХ drawer рдЖрдзреА рд╕реБрд░реВ рд╣реЛрддреЗ тАФ рджрд┐рд╢реЗрдиреБрд╕рд╛рд░, рддреБрдореНрд╣рд╛рд▓рд╛ рдПрдХ drawer рджреЛрдирджрд╛ рджрд┐рд╕рддреЛ рдЖрдгрд┐ рдХрд╛рд╣реА рд╕реБрдЯрдд рдирд╛рд╣реА, рдХрд┐рдВрд╡рд╛ рдПрдХ рд╕реБрдЯрддреЛ рдЖрдгрд┐ рдХрд╛рд╣реАрдЪ рджреЛрдирджрд╛ рджрд┐рд╕рдд рдирд╛рд╣реА. рдЦреВрдг ("id 2 рдирдВрддрд░") рддрд┐рдЪреНрдпрд╛рдкреБрдвреЗ drawers рдЬреЛрдбрд▓реЗ рддрд░реА рд╣рд▓рдд рдирд╛рд╣реА.
Paginate рдХрд░рдгреНрдпрд╛рдЖрдзреА clerk рдЖрдгрдЦреА рджреЛрди slips рд╕реНрд╡реАрдХрд╛рд░рддреЛ: filter
(?class=3A тАФ рдлрдХреНрдд рддреЛ рд╡рд░реНрдЧ) рдЖрдгрд┐ sort (?sort=name). рдХреНрд░рдо
рдиреЗрд╣рдореА рддреЛрдЪ: рдЖрдзреА filter, рдордЧ sort, рдордЧ рдкреБрдврдЪреЗ рдереЛрдбреЗ рдШреНрдпрд╛.
ЁЯЧ║я╕П рдЖрдХреГрддреА
flowchart LR
all["ЁЯЧДя╕П all students"] -->|"1 filter ?class=3A"| f["3 students"]
f -->|"2 sort ?sort=name"| s["Aishwarya, Dipika, Katrina"]
s -->|"3 paginate ?limit=2"| p1["page: Aishwarya, Dipika<br/>next_cursor: 2"]
p1 -.->|"4 ?cursor=2"| p2["page: Katrina<br/>next_cursor: null"]
тЭУ рдХрд╛рдп
- Response рдЖрдХрд╛рд░:
{"items": [...], "next_cursor": тАж|null, "total": N}.totalрдРрдЪреНрдЫрд┐рдХ рдЖрд╣реЗ рдЖрдгрд┐ рдореЛрдареНрдпрд╛ tables рд╡рд░ рдорд╣рд╛рдЧ рдкрдбреВ рд╢рдХрддреЛ тАФ рдЕрдиреЗрдХ APIs рддреЛ рд╡рдЧрд│рддрд╛рдд рдХрд┐рдВрд╡рд╛ рдЕрдВрджрд╛рдЬреЗ рджреЗрддрд╛рдд. - Cursor: "рдореА рдХреБрдареЗ рдерд╛рдВрдмрд▓реЛ" рд╣реЗ рд╕рд╛рдард╡рдгрд╛рд░рд╛ рдЕрдкрд╛рд░рджрд░реНрд╢рдХ token. рдЖрдордЪрд╛ cursor рдореНрд╣рдгрдЬреЗ
рд╢реЗрд╡рдЯрдЪрд╛ id (рд╢рд┐рдХрдгреНрдпрд╛рд╕рд╛рдареА, рд╡рд╛рдЪрддрд╛ рдпреЗрдгрд╛рд░рд╛); рдЦрд░реЗ APIs
(sort key, id)base64 рдордзреНрдпреЗ encode рдХрд░рддрд╛рдд, рдореНрд╣рдгрдЬреЗ рддреЛ рдХреЛрдгрддреНрдпрд╛рд╣реА sort рдХреНрд░рдорд╛рд╕рд╛рдареА рдЪрд╛рд▓рддреЛ. - Offset (
?page=7&size=20тЖТOFFSET 120): рд╕реЛрдкрд╛, "рдкрд╛рди 7 рд╡рд░ рдЙрдбреА рдорд╛рд░рд╛" рдХрд░реВ рджреЗрддреЛ, рдкрдг рдирд╡реАрди rows рдЖрд▓реНрдпрд╛рд╡рд░ rows рд╕реЛрдбрддреЛ рдХрд┐рдВрд╡рд╛ рджреЛрдирджрд╛ рджреЗрддреЛ рдЖрдгрд┐ рдЬрд┐рддрдХреЗ рдЦреЛрд▓ рдЬрд╛рд▓ рддрд┐рддрдХрд╛ рдордВрдж рд╣реЛрддреЛ (database 120 rows рд╕реЛрдбрдгреНрдпрд╛рд╕рд╛рдареА рддреНрдпрд╛ рдореЛрдЬрддреЛ). - Limit рд▓рд╛ рдиреЗрд╣рдореА рдХрдорд╛рд▓ рдорд░реНрдпрд╛рджрд╛ рдЕрд╕рддреЗ (рдЖрдордЪреА 100 рд╡рд░ рдерд╛рдВрдмрддреЗ) тАФ рдирд╛рд╣реАрддрд░
?limit=1000000рд╣реА denial-of-service slip рдард░рддреЗ. - Filters рдЖрдгрд┐ sorts allow-list рдХреЗрд▓реЗрд▓реЗ рдЕрд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗрдд (
sort тИИ {id, name, grade}) тАФ client рдЪреА string рдХрдзреАрдЪ рдереЗрдЯ query рдордзреНрдпреЗ рдЯрд╛рдХреВ рдирдХрд╛.
ЁЯдФ рдХрд╛
рдХрд╛рд░рдг "рд╕рдЧрд│реЗ рдкрд░рдд рджреНрдпрд╛" 50 rows рд╕рд╛рдареА рдареАрдХ рдЖрд╣реЗ рдЖрдгрд┐ 50,000 рд╕рд╛рдареА рдШрд╛рддрдХ: timeouts, memory, рдЖрдгрд┐ рдХрд╛рдпрдо spinner рдлрд┐рд░рд╡рдд рдмрд╕рд▓реЗрд▓реЗ mobile apps. рдЖрдгрд┐ рдХрд╛рд░рдг рд╡рд╛рдврддреНрдпрд╛ рдпрд╛рджреАрдЪрд╛ bug tests рдордзреНрдпреЗ рдЕрджреГрд╢реНрдп рдЕрд╕рддреЛ (unit test рдЪрд╛рд▓реВ рдЕрд╕рддрд╛рдирд╛ tables рд╡рд╛рдврдд рдирд╛рд╣реАрдд) рдЖрдгрд┐ production рдордзреНрдпреЗ рдЙрдШрдб рджрд┐рд╕рддреЛ (items рдкреБрдиреНрд╣рд╛ рджрд╛рдЦрд╡рдгрд╛рд░рд╛ feed). Cursors рдЪреА рдХрд┐рдВрдордд рдПрдХ рдЬрд╛рджрд╛ рд╕рдВрдХрд▓реНрдкрдирд╛ рдЖрд╣реЗ рдЖрдгрд┐ рддреЗ bugs рдЪрд╛ рдПрдХ рдкреВрд░реНрдг рд╡рд░реНрдЧ рдХрд╛рдвреВрди рдЯрд╛рдХрддрд╛рдд.
ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)
school_api.py рдордзрд▓реЗ list_students(): class рдиреБрд╕рд╛рд░ filter, allow-list рдХреЗрд▓реЗрд▓рд╛
sort, 100 рд╡рд░ рдорд░реНрдпрд╛рджрд┐рдд limit, рдЖрдгрд┐ id рдиреБрд╕рд╛рд░ sort рдХрд░рддрд╛рдирд╛ id рд╡рд░ cursor
(рджреБрд╕рд▒реНрдпрд╛ field рдиреБрд╕рд╛рд░ sort рдХрд░рддрд╛рдирд╛ offset тАФ рд╣реЗ рдЬрд╛рдгреВрдирдмреБрдЬреВрди рдХреЗрд▓реЗрд▓реЗ рд╕реЛрдкреЗрдкрдг рдЖрд╣реЗ;
рдЦрд░реЗ APIs рддреЗ рдХрд╕реЗ рд╕рд░реНрд╡рд╕рд╛рдзрд╛рд░рдг рдХрд░рддрд╛рдд рддреЗ "рдХрд╛рдп" рд╡рд┐рднрд╛рдЧ рд╕рд╛рдВрдЧрддреЛ).
ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛
B=http://127.0.0.1:8080/v1; K='X-API-Key: hall-pass-123'; J='Content-Type: application/json'
curl -s "$B/students?limit=2" # page 1 тЖТ next_cursor 2
curl -s "$B/students?limit=2&cursor=2" # page 2 тЖТ next_cursor 4
curl -s "$B/students?limit=2&cursor=4" # page 3 тЖТ next_cursor null
# the growing-list experiment: start paging, insert, keep paging тАФ the cursor does not skip
curl -s -X POST $B/students -H "$K" -H "$J" -d '{"name":"Zoya","class":"3B"}' > /dev/null
curl -s "$B/students?limit=2&cursor=4" # still starts after id 4 тАФ Zoya appears at the end, once
curl -s "$B/students?class=3A&sort=name&limit=10"
curl -s "$B/students?limit=999999" | head -c 200 # capped, not exploded
тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ
рджреЛрди-рджреЛрдирдЪреА рддреАрди рдкрд╛рдиреЗ, рдордЧ next_cursor: null; рдирд╡реАрди рдиреЛрдВрджреАрдирдВрддрд░, cursor=4 рдкрд╛рд╕реВрди paging рдХреЗрд▓реНрдпрд╛рд╡рд░ рдирд╡реАрди рд╡рд┐рджреНрдпрд╛рд░реНрдереА рд╢реЗрд╡рдЯреА рдиреЗрдордХрд╛ рдПрдХрджрд╛рдЪ рджрд┐рд╕рддреЛ. Class filter рдирд╛рд╡рд╛рдиреБрд╕рд╛рд░ sort рдХреЗрд▓реЗрд▓реЗ рддреАрди 3A рд╡рд┐рджреНрдпрд╛рд░реНрдереА рдкрд░рдд рджреЗрддреЛ; рдкреНрд░рдЪрдВрдб limit рдЬрд╛рд╕реНрддреАрдд рдЬрд╛рд╕реНрдд 100 items рдкрд░рдд рджреЗрддреЛ.
ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ
Filter тЖТ sort тЖТ paginate, рдирд╡реАрди рдиреЛрдВрджреАрдВрдирдВрддрд░рд╣реА рдЯрд┐рдХрдгрд╛рд▒реНрдпрд╛ рдЦреБрдгреЗрд╕рд╣ тАФ рдЖрдгрд┐ counter рдЪреЗ рд╕рдВрд░рдХреНрд╖рдг рдХрд░рдгрд╛рд░реА рдорд░реНрдпрд╛рджрд╛.
тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛
limitрд▓рд╛ рдХрдорд╛рд▓ рдорд░реНрдпрд╛рджрд╛ рдирд╕рдгреЗ- feed рдХрд┐рдВрд╡рд╛ log рд╡рд░ offset pagination (rows рд╕реБрдЯрддрд╛рдд рдЖрдгрд┐ рджреЛрдирджрд╛ рдпреЗрддрд╛рдд)
- client рд╕рд╛рдВрдЧреЗрд▓ рддреНрдпрд╛ рдХреЛрдгрддреНрдпрд╛рд╣реА field рдиреБрд╕рд╛рд░ sort рдХрд░рдгреЗ тАФ рддреЗ allow-list рдХрд░рд╛ (SQL injection рдЖрдгрд┐ рдордВрдж queries рдЗрдереЗрдЪ рд▓рдкрддрд╛рдд)
- рдкреНрд░рдЪрдВрдб table рдЪреНрдпрд╛ рдкреНрд░рддреНрдпреЗрдХ рдкрд╛рдирд╛рд╡рд░ рд╕рдВрдкреВрд░реНрдг table рдореЛрдЬреВрди
totalрдкрд░рдд рджреЗрдгреЗ - versions рджрд░рдореНрдпрд╛рди cursor рдЪрд╛ рдЕрд░реНрде рдмрджрд▓рдгреЗ тАФ рддреЛ contract рдЪрд╛ рднрд╛рдЧ рдЖрд╣реЗ (рдЕрдкрд╛рд░рджрд░реНрд╢рдХ рдЕрд╕рд▓рд╛ рддрд░реА)
ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: infinite-scroll feed рд╡рд░рдЪреЗ рдкреНрд░рддреНрдпреЗрдХ "app рдиреЗ рддреЛ item рджреЛрдирджрд╛ рдХрд╛ рджрд╛рдЦрд╡рд▓рд╛?" ticket рдореНрд╣рдгрдЬреЗ offset pagination. Index рдЕрд╕рд▓реЗрд▓реНрдпрд╛ column рд╡рд░рдЪреНрдпрд╛ cursors рдореБрд│реЗрдЪ рдореЛрдареЗ feeds scale рд╣реЛрддрд╛рдд тАФ Database school рдЪрд╛ рдзрдбрд╛ 06 рдпрд╛ рдЧреЛрд╖реНрдЯреАрдЪрд╛ index рд╡рд╛рд▓рд╛ рдЕрд░реНрдзрд╛ рднрд╛рдЧ рдЖрд╣реЗ.
тПня╕П рдкреБрдвреЗ
Counter рдЖрдзреАрдкрд╛рд╕реВрди рд╡рд╛рдкрд░рдгрд╛рд▒реНрдпрд╛ apps рдирд╛ рди рдореЛрдбрддрд╛ рдмрджрд▓рд╛рдпрд▓рд╛ рд╣рд╡рд╛:
versioning тАФ additive рдмрджрд▓, /v2, deprecation рдЖрдгрд┐ sunset.
git checkout lesson-09-versioning