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