Improve temporary table handling (#1976)
* Use all_cards_for_search() helper * Add CardTableGuard * Add for_each_card_in_search() helper * Add all_cards_for_search_in_order() helper * Add all_cards_for_ids() helper * Return siblings for bury instead of only searching * Remove redundant clear_searched_cards_table() calls * Add with_searched_cards_table() * Remove false comment * Add NoteTableGuard * Add with_ids_in_searched_notes_table() helper * Make some last routines use table helpers
This commit is contained in:
parent
09841c7c0f
commit
173a5bfed5
@ -274,12 +274,11 @@ impl Collection {
|
||||
))?;
|
||||
let config = self.get_deck_config(config_id, true)?.unwrap();
|
||||
let mut steps_adjuster = RemainingStepsAdjuster::new(&config);
|
||||
self.storage.set_search_table_to_card_ids(cards, false)?;
|
||||
let sched = self.scheduler_version();
|
||||
let usn = self.usn()?;
|
||||
self.transact(Op::SetCardDeck, |col| {
|
||||
let mut count = 0;
|
||||
for mut card in col.storage.all_searched_cards()? {
|
||||
for mut card in col.all_cards_for_ids(cards, false)? {
|
||||
if card.deck_id == deck_id {
|
||||
continue;
|
||||
}
|
||||
@ -299,11 +298,10 @@ impl Collection {
|
||||
}
|
||||
let flag = flag as u8;
|
||||
|
||||
self.storage.set_search_table_to_card_ids(cards, false)?;
|
||||
let usn = self.usn()?;
|
||||
self.transact(Op::SetFlag, |col| {
|
||||
let mut count = 0;
|
||||
for mut card in col.storage.all_searched_cards()? {
|
||||
for mut card in col.all_cards_for_ids(cards, false)? {
|
||||
let original = card.clone();
|
||||
if card.set_flag(flag) {
|
||||
// To avoid having to rebuild the study queues, we mark the card as requiring
|
||||
|
@ -12,6 +12,7 @@ use crate::{
|
||||
latex::extract_latex,
|
||||
prelude::*,
|
||||
revlog::RevlogEntry,
|
||||
search::{CardTableGuard, NoteTableGuard},
|
||||
text::{extract_media_refs, extract_underscored_css_imports, extract_underscored_references},
|
||||
};
|
||||
|
||||
@ -37,21 +38,22 @@ impl ExchangeData {
|
||||
) -> Result<()> {
|
||||
self.days_elapsed = col.timing_today()?.days_elapsed;
|
||||
self.creation_utc_offset = col.get_creation_utc_offset();
|
||||
self.notes = col.gather_notes(search)?;
|
||||
self.cards = col.gather_cards()?;
|
||||
self.decks = col.gather_decks()?;
|
||||
self.notetypes = col.gather_notetypes()?;
|
||||
let (notes, guard) = col.gather_notes(search)?;
|
||||
self.notes = notes;
|
||||
let (cards, guard) = guard.col.gather_cards()?;
|
||||
self.cards = cards;
|
||||
self.decks = guard.col.gather_decks()?;
|
||||
self.notetypes = guard.col.gather_notetypes()?;
|
||||
self.check_ids()?;
|
||||
|
||||
if with_scheduling {
|
||||
self.revlog = col.gather_revlog()?;
|
||||
self.deck_configs = col.gather_deck_configs(&self.decks)?;
|
||||
self.revlog = guard.col.gather_revlog()?;
|
||||
self.deck_configs = guard.col.gather_deck_configs(&self.decks)?;
|
||||
} else {
|
||||
self.remove_scheduling_information(col);
|
||||
self.remove_scheduling_information(guard.col);
|
||||
};
|
||||
|
||||
col.storage.clear_searched_notes_table()?;
|
||||
col.storage.clear_searched_cards_table()
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn gather_media_names(
|
||||
@ -171,14 +173,22 @@ fn svg_getter(notetypes: &[Notetype]) -> impl Fn(NotetypeId) -> bool {
|
||||
}
|
||||
|
||||
impl Collection {
|
||||
fn gather_notes(&mut self, search: impl TryIntoSearch) -> Result<Vec<Note>> {
|
||||
self.search_notes_into_table(search)?;
|
||||
self.storage.all_searched_notes()
|
||||
fn gather_notes(&mut self, search: impl TryIntoSearch) -> Result<(Vec<Note>, NoteTableGuard)> {
|
||||
let guard = self.search_notes_into_table(search)?;
|
||||
guard
|
||||
.col
|
||||
.storage
|
||||
.all_searched_notes()
|
||||
.map(|notes| (notes, guard))
|
||||
}
|
||||
|
||||
fn gather_cards(&mut self) -> Result<Vec<Card>> {
|
||||
self.storage.search_cards_of_notes_into_table()?;
|
||||
self.storage.all_searched_cards()
|
||||
fn gather_cards(&mut self) -> Result<(Vec<Card>, CardTableGuard)> {
|
||||
let guard = self.search_cards_of_notes_into_table()?;
|
||||
guard
|
||||
.col
|
||||
.storage
|
||||
.all_searched_cards()
|
||||
.map(|cards| (cards, guard))
|
||||
}
|
||||
|
||||
fn gather_decks(&mut self) -> Result<Vec<Deck>> {
|
||||
|
@ -53,16 +53,15 @@ impl Collection {
|
||||
progress.call(ExportProgress::File)?;
|
||||
let mut incrementor = progress.incrementor(ExportProgress::Notes);
|
||||
|
||||
self.search_notes_into_table(request.search_node())?;
|
||||
let ctx = NoteContext::new(&request, self)?;
|
||||
let guard = self.search_notes_into_table(request.search_node())?;
|
||||
let ctx = NoteContext::new(&request, guard.col)?;
|
||||
let mut writer = note_file_writer_with_header(&request.out_path, &ctx)?;
|
||||
self.storage.for_each_note_in_search(|note| {
|
||||
guard.col.storage.for_each_note_in_search(|note| {
|
||||
incrementor.increment()?;
|
||||
writer.write_record(ctx.record(¬e))?;
|
||||
Ok(())
|
||||
})?;
|
||||
writer.flush()?;
|
||||
self.storage.clear_searched_notes_table()?;
|
||||
|
||||
Ok(incrementor.count())
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use std::collections::{HashMap, HashSet};
|
||||
use super::{CardGenContext, Notetype, NotetypeKind};
|
||||
use crate::{
|
||||
prelude::*,
|
||||
search::{JoinSearches, Node, SearchNode, SortMode, TemplateKind},
|
||||
search::{JoinSearches, Node, SearchNode, TemplateKind},
|
||||
storage::comma_separated_ids,
|
||||
};
|
||||
|
||||
@ -294,11 +294,9 @@ impl Collection {
|
||||
if !map.removed.is_empty() {
|
||||
let ords =
|
||||
SearchBuilder::any(map.removed.iter().map(|o| TemplateKind::Ordinal(*o as u16)));
|
||||
self.search_cards_into_table(nids.and(ords), SortMode::NoOrder)?;
|
||||
for card in self.storage.all_searched_cards()? {
|
||||
for card in self.all_cards_for_search(nids.and(ords))? {
|
||||
self.remove_card_and_add_grave_undoable(card, usn)?;
|
||||
}
|
||||
self.storage.clear_searched_cards_table()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -316,14 +314,12 @@ impl Collection {
|
||||
.keys()
|
||||
.map(|o| TemplateKind::Ordinal(*o as u16)),
|
||||
);
|
||||
self.search_cards_into_table(nids.and(ords), SortMode::NoOrder)?;
|
||||
for mut card in self.storage.all_searched_cards()? {
|
||||
for mut card in self.all_cards_for_search(nids.and(ords))? {
|
||||
let original = card.clone();
|
||||
card.template_idx =
|
||||
*map.remapped.get(&(card.template_idx as usize)).unwrap() as u16;
|
||||
self.update_card_inner(&mut card, original, usn)?;
|
||||
}
|
||||
self.storage.clear_searched_cards_table()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -349,7 +345,6 @@ impl Collection {
|
||||
{
|
||||
self.remove_card_and_add_grave_undoable(card, usn)?;
|
||||
}
|
||||
self.storage.clear_searched_notes_table()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -8,7 +8,7 @@ use std::collections::HashMap;
|
||||
use super::{CardGenContext, CardTemplate, Notetype};
|
||||
use crate::{
|
||||
prelude::*,
|
||||
search::{JoinSearches, SortMode, TemplateKind},
|
||||
search::{JoinSearches, TemplateKind},
|
||||
};
|
||||
|
||||
/// True if any ordinals added, removed or reordered.
|
||||
@ -148,22 +148,18 @@ impl Collection {
|
||||
if !changes.removed.is_empty() {
|
||||
let ords =
|
||||
SearchBuilder::any(changes.removed.iter().cloned().map(TemplateKind::Ordinal));
|
||||
self.search_cards_into_table(nt.id.and(ords), SortMode::NoOrder)?;
|
||||
for card in self.storage.all_searched_cards()? {
|
||||
for card in self.all_cards_for_search(nt.id.and(ords))? {
|
||||
self.remove_card_and_add_grave_undoable(card, usn)?;
|
||||
}
|
||||
self.storage.clear_searched_cards_table()?;
|
||||
}
|
||||
// update ordinals for cards with a repositioned template
|
||||
if !changes.moved.is_empty() {
|
||||
let ords = SearchBuilder::any(changes.moved.keys().cloned().map(TemplateKind::Ordinal));
|
||||
self.search_cards_into_table(nt.id.and(ords), SortMode::NoOrder)?;
|
||||
for mut card in self.storage.all_searched_cards()? {
|
||||
for mut card in self.all_cards_for_search(nt.id.and(ords))? {
|
||||
let original = card.clone();
|
||||
card.template_idx = *changes.moved.get(&card.template_idx).unwrap();
|
||||
self.update_card_inner(&mut card, original, usn)?;
|
||||
}
|
||||
self.storage.clear_searched_cards_table()?;
|
||||
}
|
||||
|
||||
if should_generate_cards(&changes, nt, old_templates) {
|
||||
|
@ -10,7 +10,7 @@ use crate::{
|
||||
unbury_deck_request::Mode as UnburyDeckMode,
|
||||
},
|
||||
prelude::*,
|
||||
search::{JoinSearches, SearchNode, SortMode, StateKind},
|
||||
search::{JoinSearches, SearchNode, StateKind},
|
||||
};
|
||||
|
||||
impl Card {
|
||||
@ -42,32 +42,29 @@ impl Collection {
|
||||
/// Unbury cards from the previous day.
|
||||
/// Done automatically, and does not mark the cards as modified.
|
||||
pub(crate) fn unbury_on_day_rollover(&mut self, today: u32) -> Result<()> {
|
||||
self.search_cards_into_table("is:buried", SortMode::NoOrder)?;
|
||||
self.storage.for_each_card_in_search(|mut card| {
|
||||
self.for_each_card_in_search(StateKind::Buried, |col, mut card| {
|
||||
card.restore_queue_after_bury_or_suspend();
|
||||
self.storage.update_card(&card)
|
||||
col.storage.update_card(&card)
|
||||
})?;
|
||||
self.storage.clear_searched_cards_table()?;
|
||||
self.set_last_unburied_day(today)
|
||||
}
|
||||
|
||||
/// Unsuspend/unbury cards in search table, and clear it.
|
||||
/// Marks the cards as modified.
|
||||
fn unsuspend_or_unbury_searched_cards(&mut self) -> Result<()> {
|
||||
/// Unsuspend/unbury cards. Marks the cards as modified.
|
||||
fn unsuspend_or_unbury_searched_cards(&mut self, cards: Vec<Card>) -> Result<()> {
|
||||
let usn = self.usn()?;
|
||||
for original in self.storage.all_searched_cards()? {
|
||||
for original in cards {
|
||||
let mut card = original.clone();
|
||||
if card.restore_queue_after_bury_or_suspend() {
|
||||
self.update_card_inner(&mut card, original, usn)?;
|
||||
}
|
||||
}
|
||||
self.storage.clear_searched_cards_table()
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unbury_or_unsuspend_cards(&mut self, cids: &[CardId]) -> Result<OpOutput<()>> {
|
||||
self.transact(Op::UnburyUnsuspend, |col| {
|
||||
col.storage.set_search_table_to_card_ids(cids, false)?;
|
||||
col.unsuspend_or_unbury_searched_cards()
|
||||
let cards = col.all_cards_for_ids(cids, false)?;
|
||||
col.unsuspend_or_unbury_searched_cards(cards)
|
||||
})
|
||||
}
|
||||
|
||||
@ -78,17 +75,19 @@ impl Collection {
|
||||
UnburyDeckMode::SchedOnly => StateKind::SchedBuried,
|
||||
};
|
||||
self.transact(Op::UnburyUnsuspend, |col| {
|
||||
col.search_cards_into_table(
|
||||
SearchNode::DeckIdWithChildren(deck_id).and(state),
|
||||
SortMode::NoOrder,
|
||||
)?;
|
||||
col.unsuspend_or_unbury_searched_cards()
|
||||
let cards =
|
||||
col.all_cards_for_search(SearchNode::DeckIdWithChildren(deck_id).and(state))?;
|
||||
col.unsuspend_or_unbury_searched_cards(cards)
|
||||
})
|
||||
}
|
||||
|
||||
/// Bury/suspend cards in search table, and clear it.
|
||||
/// Marks the cards as modified.
|
||||
fn bury_or_suspend_searched_cards(&mut self, mode: BuryOrSuspendMode) -> Result<usize> {
|
||||
fn bury_or_suspend_cards_inner(
|
||||
&mut self,
|
||||
cards: Vec<Card>,
|
||||
mode: BuryOrSuspendMode,
|
||||
) -> Result<usize> {
|
||||
let mut count = 0;
|
||||
let usn = self.usn()?;
|
||||
let sched = self.scheduler_version();
|
||||
@ -105,7 +104,7 @@ impl Collection {
|
||||
}
|
||||
};
|
||||
|
||||
for original in self.storage.all_searched_cards()? {
|
||||
for original in cards {
|
||||
let mut card = original.clone();
|
||||
if card.queue != desired_queue {
|
||||
// do not bury suspended cards as that would unsuspend them
|
||||
@ -121,8 +120,6 @@ impl Collection {
|
||||
}
|
||||
}
|
||||
|
||||
self.storage.clear_searched_cards_table()?;
|
||||
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
@ -136,8 +133,8 @@ impl Collection {
|
||||
BuryOrSuspendMode::BurySched | BuryOrSuspendMode::BuryUser => Op::Bury,
|
||||
};
|
||||
self.transact(op, |col| {
|
||||
col.storage.set_search_table_to_card_ids(cids, false)?;
|
||||
col.bury_or_suspend_searched_cards(mode)
|
||||
let cards = col.all_cards_for_ids(cids, false)?;
|
||||
col.bury_or_suspend_cards_inner(cards, mode)
|
||||
})
|
||||
}
|
||||
|
||||
@ -149,14 +146,14 @@ impl Collection {
|
||||
include_reviews: bool,
|
||||
include_day_learn: bool,
|
||||
) -> Result<usize> {
|
||||
self.storage.search_siblings_for_bury(
|
||||
let cards = self.storage.all_siblings_for_bury(
|
||||
cid,
|
||||
nid,
|
||||
include_new,
|
||||
include_reviews,
|
||||
include_day_learn,
|
||||
)?;
|
||||
self.bury_or_suspend_searched_cards(BuryOrSuspendMode::BurySched)
|
||||
self.bury_or_suspend_cards_inner(cards, BuryOrSuspendMode::BurySched)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,8 +135,7 @@ impl Collection {
|
||||
);
|
||||
let order = order_and_limit_for_search(term, ctx.today);
|
||||
|
||||
self.search_cards_into_table(&search, SortMode::Custom(order))?;
|
||||
for mut card in self.storage.all_searched_cards_in_search_order()? {
|
||||
for mut card in self.all_cards_for_search_in_order(&search, SortMode::Custom(order))? {
|
||||
let original = card.clone();
|
||||
card.move_into_filtered_deck(ctx, position);
|
||||
self.update_card_inner(&mut card, original, ctx.usn)?;
|
||||
|
@ -155,8 +155,7 @@ impl Collection {
|
||||
let usn = self.usn()?;
|
||||
let mut position = self.get_next_card_position();
|
||||
self.transact(Op::ScheduleAsNew, |col| {
|
||||
col.storage.set_search_table_to_card_ids(cids, true)?;
|
||||
let cards = col.storage.all_searched_cards_in_search_order()?;
|
||||
let cards = col.all_cards_for_ids(cids, true)?;
|
||||
for mut card in cards {
|
||||
let original = card.clone();
|
||||
if card.schedule_as_new(position, reset_counts, restore_position) {
|
||||
@ -168,7 +167,6 @@ impl Collection {
|
||||
col.update_card_inner(&mut card, original, usn)?;
|
||||
}
|
||||
col.set_next_card_position(position)?;
|
||||
col.storage.clear_searched_cards_table()?;
|
||||
|
||||
match context {
|
||||
Some(ScheduleAsNewContext::Browser) => {
|
||||
@ -234,8 +232,7 @@ impl Collection {
|
||||
if shift {
|
||||
self.shift_existing_cards(starting_from, step * cids.len() as u32, usn, v2)?;
|
||||
}
|
||||
self.storage.set_search_table_to_card_ids(cids, true)?;
|
||||
let cards = self.storage.all_searched_cards_in_search_order()?;
|
||||
let cards = self.all_cards_for_ids(cids, true)?;
|
||||
let sorter = NewCardSorter::new(&cards, starting_from, step, order);
|
||||
let mut count = 0;
|
||||
for mut card in cards {
|
||||
@ -245,7 +242,6 @@ impl Collection {
|
||||
self.update_card_inner(&mut card, original, usn)?;
|
||||
}
|
||||
}
|
||||
self.storage.clear_searched_cards_table()?;
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
@ -286,13 +282,11 @@ impl Collection {
|
||||
}
|
||||
|
||||
fn shift_existing_cards(&mut self, start: u32, by: u32, usn: Usn, v2: bool) -> Result<()> {
|
||||
self.storage.search_cards_at_or_above_position(start)?;
|
||||
for mut card in self.storage.all_searched_cards()? {
|
||||
for mut card in self.storage.all_cards_at_or_above_position(start)? {
|
||||
let original = card.clone();
|
||||
card.set_new_position(card.due as u32 + by, v2);
|
||||
self.update_card_inner(&mut card, original, usn)?;
|
||||
}
|
||||
self.storage.clear_searched_cards_table()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -112,8 +112,7 @@ impl Collection {
|
||||
let distribution = Uniform::from(spec.min..=spec.max);
|
||||
let mut decks_initial_ease: HashMap<DeckId, f32> = HashMap::new();
|
||||
self.transact(Op::SetDueDate, |col| {
|
||||
col.storage.set_search_table_to_card_ids(cids, false)?;
|
||||
for mut card in col.storage.all_searched_cards()? {
|
||||
for mut card in col.all_cards_for_ids(cids, false)? {
|
||||
let deck_id = card.original_deck_id.or(card.deck_id);
|
||||
let ease_factor = match decks_initial_ease.get(&deck_id) {
|
||||
Some(ease) => *ease,
|
||||
@ -139,7 +138,6 @@ impl Collection {
|
||||
col.log_manually_scheduled_review(&card, &original, usn)?;
|
||||
col.update_card_inner(&mut card, original, usn)?;
|
||||
}
|
||||
col.storage.clear_searched_cards_table()?;
|
||||
if let Some(key) = context {
|
||||
col.set_config_string_inner(key, days)?;
|
||||
}
|
||||
|
@ -130,21 +130,21 @@ impl Collection {
|
||||
}
|
||||
|
||||
fn upgrade_cards_to_v2(&mut self) -> Result<()> {
|
||||
let count = self.search_cards_into_table(
|
||||
let guard = self.search_cards_into_table(
|
||||
// can't add 'is:learn' here, as it matches on card type, not card queue
|
||||
"deck:filtered OR is:review",
|
||||
SortMode::NoOrder,
|
||||
)?;
|
||||
if count > 0 {
|
||||
let decks = self.storage.get_decks_map()?;
|
||||
let configs = self.storage.get_deck_config_map()?;
|
||||
self.storage.for_each_card_in_search(|mut card| {
|
||||
if guard.cards > 0 {
|
||||
let decks = guard.col.storage.get_decks_map()?;
|
||||
let configs = guard.col.storage.get_deck_config_map()?;
|
||||
guard.col.storage.for_each_card_in_search(|mut card| {
|
||||
let filtered_info = get_filter_info_for_card(&card, &decks, &configs);
|
||||
card.upgrade_to_v2(filtered_info);
|
||||
self.storage.update_card(&card)
|
||||
guard.col.storage.update_card(&card)
|
||||
})?;
|
||||
}
|
||||
self.storage.clear_searched_cards_table()
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
#[cfg(test)]
|
||||
|
@ -122,6 +122,32 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CardTableGuard<'a> {
|
||||
pub col: &'a mut Collection,
|
||||
pub cards: usize,
|
||||
}
|
||||
|
||||
impl Drop for CardTableGuard<'_> {
|
||||
fn drop(&mut self) {
|
||||
if let Err(err) = self.col.storage.clear_searched_cards_table() {
|
||||
println!("{err:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NoteTableGuard<'a> {
|
||||
pub col: &'a mut Collection,
|
||||
pub notes: usize,
|
||||
}
|
||||
|
||||
impl Drop for NoteTableGuard<'_> {
|
||||
fn drop(&mut self) {
|
||||
if let Err(err) = self.col.storage.clear_searched_notes_table() {
|
||||
println!("{err:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Collection {
|
||||
pub fn search_cards<N>(&mut self, search: N, mode: SortMode) -> Result<Vec<CardId>>
|
||||
where
|
||||
@ -188,12 +214,14 @@ impl Collection {
|
||||
}
|
||||
|
||||
/// Place the matched card ids into a temporary 'search_cids' table
|
||||
/// instead of returning them. Use clear_searched_cards() to remove it.
|
||||
/// Returns number of added cards.
|
||||
pub(crate) fn search_cards_into_table<N>(&mut self, search: N, mode: SortMode) -> Result<usize>
|
||||
where
|
||||
N: TryIntoSearch,
|
||||
{
|
||||
/// instead of returning them. Returns a guard with a collection reference
|
||||
/// and the number of added cards. When the guard is dropped, the temporary
|
||||
/// table is cleaned up.
|
||||
pub(crate) fn search_cards_into_table(
|
||||
&mut self,
|
||||
search: impl TryIntoSearch,
|
||||
mode: SortMode,
|
||||
) -> Result<CardTableGuard> {
|
||||
let top_node = search.try_into_search()?;
|
||||
let writer = SqlWriter::new(self, ReturnItemType::Cards);
|
||||
let want_order = mode != SortMode::NoOrder;
|
||||
@ -209,30 +237,64 @@ impl Collection {
|
||||
}
|
||||
let sql = format!("insert into search_cids {}", sql);
|
||||
|
||||
self.storage
|
||||
let cards = self
|
||||
.storage
|
||||
.db
|
||||
.prepare(&sql)?
|
||||
.execute(params_from_iter(args))
|
||||
.map_err(Into::into)
|
||||
.execute(params_from_iter(args))?;
|
||||
|
||||
Ok(CardTableGuard { cards, col: self })
|
||||
}
|
||||
|
||||
pub(crate) fn all_cards_for_search<N>(&mut self, search: N) -> Result<Vec<Card>>
|
||||
where
|
||||
N: TryIntoSearch,
|
||||
{
|
||||
self.search_cards_into_table(search, SortMode::NoOrder)?;
|
||||
let cards = self.storage.all_searched_cards();
|
||||
self.storage.clear_searched_cards_table()?;
|
||||
cards
|
||||
pub(crate) fn all_cards_for_search(&mut self, search: impl TryIntoSearch) -> Result<Vec<Card>> {
|
||||
let guard = self.search_cards_into_table(search, SortMode::NoOrder)?;
|
||||
guard.col.storage.all_searched_cards()
|
||||
}
|
||||
|
||||
/// Place the matched note ids into a temporary 'search_nids' table
|
||||
/// instead of returning them. Use clear_searched_notes() to remove it.
|
||||
/// Returns number of added notes.
|
||||
pub(crate) fn search_notes_into_table<N>(&mut self, search: N) -> Result<usize>
|
||||
where
|
||||
N: TryIntoSearch,
|
||||
{
|
||||
pub(crate) fn all_cards_for_search_in_order(
|
||||
&mut self,
|
||||
search: impl TryIntoSearch,
|
||||
mode: SortMode,
|
||||
) -> Result<Vec<Card>> {
|
||||
let guard = self.search_cards_into_table(search, mode)?;
|
||||
guard.col.storage.all_searched_cards_in_search_order()
|
||||
}
|
||||
|
||||
pub(crate) fn all_cards_for_ids(
|
||||
&self,
|
||||
cards: &[CardId],
|
||||
preserve_order: bool,
|
||||
) -> Result<Vec<Card>> {
|
||||
self.storage.with_searched_cards_table(preserve_order, || {
|
||||
self.storage.set_search_table_to_card_ids(cards)?;
|
||||
if preserve_order {
|
||||
self.storage.all_searched_cards_in_search_order()
|
||||
} else {
|
||||
self.storage.all_searched_cards()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn for_each_card_in_search(
|
||||
&mut self,
|
||||
search: impl TryIntoSearch,
|
||||
mut func: impl FnMut(&Collection, Card) -> Result<()>,
|
||||
) -> Result<()> {
|
||||
let guard = self.search_cards_into_table(search, SortMode::NoOrder)?;
|
||||
guard
|
||||
.col
|
||||
.storage
|
||||
.for_each_card_in_search(|card| func(guard.col, card))
|
||||
}
|
||||
|
||||
/// Place the matched card ids into a temporary 'search_nids' table
|
||||
/// instead of returning them. Returns a guard with a collection reference
|
||||
/// and the number of added notes. When the guard is dropped, the temporary
|
||||
/// table is cleaned up.
|
||||
pub(crate) fn search_notes_into_table(
|
||||
&mut self,
|
||||
search: impl TryIntoSearch,
|
||||
) -> Result<NoteTableGuard> {
|
||||
let top_node = search.try_into_search()?;
|
||||
let writer = SqlWriter::new(self, ReturnItemType::Notes);
|
||||
let mode = SortMode::NoOrder;
|
||||
@ -242,11 +304,21 @@ impl Collection {
|
||||
self.storage.setup_searched_notes_table()?;
|
||||
let sql = format!("insert into search_nids {}", sql);
|
||||
|
||||
self.storage
|
||||
let notes = self
|
||||
.storage
|
||||
.db
|
||||
.prepare(&sql)?
|
||||
.execute(params_from_iter(args))
|
||||
.map_err(Into::into)
|
||||
.execute(params_from_iter(args))?;
|
||||
|
||||
Ok(NoteTableGuard { notes, col: self })
|
||||
}
|
||||
|
||||
/// Place the ids of cards with notes in 'search_nids' into 'search_cids'.
|
||||
/// Returns number of added cards.
|
||||
pub(crate) fn search_cards_of_notes_into_table(&mut self) -> Result<CardTableGuard> {
|
||||
self.storage.setup_searched_cards_table()?;
|
||||
let cards = self.storage.search_cards_of_notes_into_table()?;
|
||||
Ok(CardTableGuard { cards, col: self })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,9 +15,9 @@ impl Collection {
|
||||
search: &str,
|
||||
days: u32,
|
||||
) -> Result<pb::GraphsResponse> {
|
||||
self.search_cards_into_table(search, SortMode::NoOrder)?;
|
||||
let guard = self.search_cards_into_table(search, SortMode::NoOrder)?;
|
||||
let all = search.trim().is_empty();
|
||||
self.graph_data(all, days)
|
||||
guard.col.graph_data(all, days)
|
||||
}
|
||||
|
||||
fn graph_data(&mut self, all: bool, days: u32) -> Result<pb::GraphsResponse> {
|
||||
@ -41,8 +41,6 @@ impl Collection {
|
||||
.get_pb_revlog_entries_for_searched_cards(revlog_start)?
|
||||
};
|
||||
|
||||
self.storage.clear_searched_cards_table()?;
|
||||
|
||||
Ok(pb::GraphsResponse {
|
||||
cards: cards.into_iter().map(Into::into).collect(),
|
||||
revlog,
|
||||
|
@ -408,14 +408,15 @@ impl super::SqliteStorage {
|
||||
note_ids: &[NoteId],
|
||||
ordinal: usize,
|
||||
) -> Result<Vec<Card>> {
|
||||
self.set_search_table_to_note_ids(note_ids)?;
|
||||
self.db
|
||||
.prepare_cached(concat!(
|
||||
include_str!("get_card.sql"),
|
||||
" where nid in (select nid from search_nids) and ord > ?"
|
||||
))?
|
||||
.query_and_then([ordinal as i64], |r| row_to_card(r).map_err(Into::into))?
|
||||
.collect()
|
||||
self.with_ids_in_searched_notes_table(note_ids, || {
|
||||
self.db
|
||||
.prepare_cached(concat!(
|
||||
include_str!("get_card.sql"),
|
||||
" where nid in (select nid from search_nids) and ord > ?"
|
||||
))?
|
||||
.query_and_then([ordinal as i64], |r| row_to_card(r).map_err(Into::into))?
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn all_card_ids_of_note_in_template_order(
|
||||
@ -455,16 +456,14 @@ impl super::SqliteStorage {
|
||||
Ok(cids)
|
||||
}
|
||||
|
||||
/// Place matching card ids into the search table.
|
||||
pub(crate) fn search_siblings_for_bury(
|
||||
pub(crate) fn all_siblings_for_bury(
|
||||
&self,
|
||||
cid: CardId,
|
||||
nid: NoteId,
|
||||
include_new: bool,
|
||||
include_reviews: bool,
|
||||
include_day_learn: bool,
|
||||
) -> Result<()> {
|
||||
self.setup_searched_cards_table()?;
|
||||
) -> Result<Vec<Card>> {
|
||||
let params = named_params! {
|
||||
":card_id": cid,
|
||||
":note_id": nid,
|
||||
@ -475,10 +474,27 @@ impl super::SqliteStorage {
|
||||
":review_queue": CardQueue::Review as i8,
|
||||
":daylearn_queue": CardQueue::DayLearn as i8,
|
||||
};
|
||||
self.db
|
||||
.prepare_cached(include_str!("siblings_for_bury.sql"))?
|
||||
.execute(params)?;
|
||||
Ok(())
|
||||
self.with_searched_cards_table(false, || {
|
||||
self.db
|
||||
.prepare_cached(include_str!("siblings_for_bury.sql"))?
|
||||
.execute(params)?;
|
||||
self.all_searched_cards()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn with_searched_cards_table<T>(
|
||||
&self,
|
||||
preserve_order: bool,
|
||||
func: impl FnOnce() -> Result<T>,
|
||||
) -> Result<T> {
|
||||
if preserve_order {
|
||||
self.setup_searched_cards_table_to_preserve_order()?;
|
||||
} else {
|
||||
self.setup_searched_cards_table()?;
|
||||
}
|
||||
let result = func();
|
||||
self.clear_searched_cards_table()?;
|
||||
result
|
||||
}
|
||||
|
||||
pub(crate) fn note_ids_of_cards(&self, cids: &[CardId]) -> Result<HashSet<NoteId>> {
|
||||
@ -500,7 +516,6 @@ impl super::SqliteStorage {
|
||||
/// Place the ids of cards with notes in 'search_nids' into 'search_cids'.
|
||||
/// Returns number of added cards.
|
||||
pub(crate) fn search_cards_of_notes_into_table(&self) -> Result<usize> {
|
||||
self.setup_searched_cards_table()?;
|
||||
self.db
|
||||
.prepare(include_str!("search_cards_of_notes_into_table.sql"))?
|
||||
.execute([])
|
||||
@ -576,12 +591,13 @@ impl super::SqliteStorage {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn search_cards_at_or_above_position(&self, start: u32) -> Result<()> {
|
||||
self.setup_searched_cards_table()?;
|
||||
self.db
|
||||
.prepare(include_str!("at_or_above_position.sql"))?
|
||||
.execute([start, CardType::New as u32])?;
|
||||
Ok(())
|
||||
pub(crate) fn all_cards_at_or_above_position(&self, start: u32) -> Result<Vec<Card>> {
|
||||
self.with_searched_cards_table(false, || {
|
||||
self.db
|
||||
.prepare(include_str!("at_or_above_position.sql"))?
|
||||
.execute([start, CardType::New as u32])?;
|
||||
self.all_searched_cards()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn setup_searched_cards_table(&self) -> Result<()> {
|
||||
@ -603,24 +619,13 @@ impl super::SqliteStorage {
|
||||
|
||||
/// Injects the provided card IDs into the search_cids table, for
|
||||
/// when ids have arrived outside of a search.
|
||||
/// Clear with clear_searched_cards_table().
|
||||
pub(crate) fn set_search_table_to_card_ids(
|
||||
&mut self,
|
||||
cards: &[CardId],
|
||||
preserve_order: bool,
|
||||
) -> Result<()> {
|
||||
if preserve_order {
|
||||
self.setup_searched_cards_table_to_preserve_order()?;
|
||||
} else {
|
||||
self.setup_searched_cards_table()?;
|
||||
}
|
||||
pub(crate) fn set_search_table_to_card_ids(&self, cards: &[CardId]) -> Result<()> {
|
||||
let mut stmt = self
|
||||
.db
|
||||
.prepare_cached("insert into search_cids values (?)")?;
|
||||
for cid in cards {
|
||||
stmt.execute([cid])?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -222,22 +222,18 @@ impl super::SqliteStorage {
|
||||
.transpose()
|
||||
}
|
||||
|
||||
pub(crate) fn get_note_tags_by_id_list(
|
||||
&mut self,
|
||||
note_ids: &[NoteId],
|
||||
) -> Result<Vec<NoteTags>> {
|
||||
self.set_search_table_to_note_ids(note_ids)?;
|
||||
let out = self
|
||||
.db
|
||||
.prepare_cached(&format!(
|
||||
"{} where id in (select nid from search_nids)",
|
||||
include_str!("get_tags.sql")
|
||||
))?
|
||||
.query_and_then([], row_to_note_tags)?
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
self.clear_searched_notes_table()?;
|
||||
Ok(out)
|
||||
pub(crate) fn get_note_tags_by_id_list(&self, note_ids: &[NoteId]) -> Result<Vec<NoteTags>> {
|
||||
self.with_ids_in_searched_notes_table(note_ids, || {
|
||||
self.db
|
||||
.prepare_cached(&format!(
|
||||
"{} where id in (select nid from search_nids)",
|
||||
include_str!("get_tags.sql")
|
||||
))?
|
||||
.query_and_then([], row_to_note_tags)?
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn for_each_note_tag_in_searched_notes<F>(&self, mut func: F) -> Result<()>
|
||||
where
|
||||
F: FnMut(&str),
|
||||
@ -297,20 +293,23 @@ impl super::SqliteStorage {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Injects the provided card IDs into the search_nids table, for
|
||||
/// when ids have arrived outside of a search.
|
||||
/// Clear with clear_searched_notes_table().
|
||||
/// Executes the closure with the note ids placed in the search_nids table.
|
||||
/// WARNING: the column name is nid, not id.
|
||||
pub(crate) fn set_search_table_to_note_ids(&mut self, notes: &[NoteId]) -> Result<()> {
|
||||
pub(crate) fn with_ids_in_searched_notes_table<T>(
|
||||
&self,
|
||||
note_ids: &[NoteId],
|
||||
func: impl FnOnce() -> Result<T>,
|
||||
) -> Result<T> {
|
||||
self.setup_searched_notes_table()?;
|
||||
let mut stmt = self
|
||||
.db
|
||||
.prepare_cached("insert into search_nids values (?)")?;
|
||||
for nid in notes {
|
||||
for nid in note_ids {
|
||||
stmt.execute([nid])?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
let result = func();
|
||||
self.clear_searched_notes_table()?;
|
||||
result
|
||||
}
|
||||
|
||||
/// Cards will arrive in card id order, not search order.
|
||||
|
@ -10,16 +10,18 @@ use crate::{prelude::*, search::SearchNode};
|
||||
|
||||
impl Collection {
|
||||
pub(crate) fn all_tags_in_deck(&mut self, deck_id: DeckId) -> Result<HashSet<UniCase<String>>> {
|
||||
self.search_notes_into_table(SearchNode::DeckIdWithChildren(deck_id))?;
|
||||
let guard = self.search_notes_into_table(SearchNode::DeckIdWithChildren(deck_id))?;
|
||||
let mut all_tags: HashSet<UniCase<String>> = HashSet::new();
|
||||
self.storage.for_each_note_tag_in_searched_notes(|tags| {
|
||||
for tag in split_tags(tags) {
|
||||
// A benchmark on a large deck indicates that nothing is gained by using a Cow and skipping
|
||||
// an allocation in the duplicate case, and this approach is simpler.
|
||||
all_tags.insert(UniCase::new(tag.to_string()));
|
||||
}
|
||||
})?;
|
||||
self.storage.clear_searched_notes_table()?;
|
||||
guard
|
||||
.col
|
||||
.storage
|
||||
.for_each_note_tag_in_searched_notes(|tags| {
|
||||
for tag in split_tags(tags) {
|
||||
// A benchmark on a large deck indicates that nothing is gained by using a Cow and skipping
|
||||
// an allocation in the duplicate case, and this approach is simpler.
|
||||
all_tags.insert(UniCase::new(tag.to_string()));
|
||||
}
|
||||
})?;
|
||||
Ok(all_tags)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user