pub struct Batches<T> { /* private fields */ }Expand description
Batches of rows by increasing id, from Query::batches: Rails’
find_in_batches without holding a cursor open.
next runs one query and returns the next batch, or
None when every row was read. after is the last id
seen: store it (in a job’s arguments, in KV) to resume later with
resume_after.
§Examples
use ocre::{Ctx, Query, Result};
#[derive(serde::Deserialize)]
struct User {
id: i64,
email: String,
}
// A scheduled task: at most 4 batches (4 queries) per run.
async fn send_digests(ctx: &Ctx, resume: Option<i64>) -> Result<Option<i64>> {
let db = ctx.db()?;
let mut batches = Query::<User>::table("users").batches(100, |user| user.id).resume_after(resume);
for _ in 0..4 {
let Some(users) = batches.next(&db).await? else { return Ok(None) };
for user in users {
// find_each: one row at a time.
let _ = user.email;
}
}
Ok(batches.after())
}Implementations§
Source§impl<T> Batches<T>
impl<T> Batches<T>
Sourcepub fn resume_after(self, id: Option<i64>) -> Self
pub fn resume_after(self, id: Option<i64>) -> Self
Starts after id (Rails’ start:, exclusive), or from the first row with None.
§Examples
let batches = ocre::Query::<Post>::table("posts").batches(100, |p| p.id).resume_after(Some(41));
assert_eq!(batches.after(), Some(41));
assert!(batches.statement().sql.contains("posts.id > ?1"));Sourcepub fn after(&self) -> Option<i64>
pub fn after(&self) -> Option<i64>
The last id read so far (None before the first batch).
§Examples
assert_eq!(ocre::Query::<Post>::table("posts").batches(10, |p| p.id).after(), None);Sourcepub fn is_done(&self) -> bool
pub fn is_done(&self) -> bool
Whether the last batch was shorter than the batch size: no row is left.
§Examples
let mut batches = ocre::Query::<Post>::table("posts").batches(2, |p| p.id);
batches.advance(&[Post { id: 1 }]);
assert!(batches.is_done());Sourcepub fn statement(&self) -> Statement
pub fn statement(&self) -> Statement
The SELECT of the next batch.
§Examples
let batches = ocre::Query::<Post>::table("posts").limit(3).batches(10, |p| p.id);
assert_eq!(batches.statement().sql, "SELECT * FROM posts ORDER BY posts.id ASC LIMIT ?1");Sourcepub fn advance(&mut self, rows: &[T])
pub fn advance(&mut self, rows: &[T])
Records a batch just read: its last id, and whether it was the last batch.
next calls it; call it yourself after running
statement through Db::all.
§Examples
let mut batches = ocre::Query::<Post>::table("posts").batches(2, |p| p.id);
batches.advance(&[Post { id: 3 }, Post { id: 5 }]);
assert_eq!((batches.after(), batches.is_done()), (Some(5), false));Source§impl<T: DeserializeOwned> Batches<T>
impl<T: DeserializeOwned> Batches<T>
Sourcepub async fn next(&mut self, db: &Db) -> Result<Option<Vec<T>>>
pub async fn next(&mut self, db: &Db) -> Result<Option<Vec<T>>>
Runs the next batch: up to the batch size of rows after the last id
read, or None once every row was read.
See Query::batches.
§Errors
Error::Internal (500) when D1 rejects the
statement or a row does not deserialize into T.
§Free plan
One query reading at most the batch size of rows (plus the rows the conditions skip without an index).
§Examples
use ocre::{Ctx, Query, Result};
#[derive(serde::Deserialize)]
struct Post {
id: i64,
}
async fn count_by_hand(ctx: &Ctx) -> Result<usize> {
let db = ctx.db()?;
let mut batches = Query::<Post>::table("posts").batches(500, |post| post.id);
let mut total = 0;
while let Some(posts) = batches.next(&db).await? {
total += posts.len();
}
Ok(total)
}