start work on field changes, and add search_cards to col

This commit is contained in:
Damien Elmes 2020-04-14 16:47:26 +10:00
parent cc297f1dc6
commit 04f0ea8599
6 changed files with 138 additions and 51 deletions

View File

@ -24,7 +24,7 @@ use crate::{
notetype::{all_stock_notetypes, NoteTypeID, NoteTypeSchema11},
sched::cutoff::{local_minutes_west_for_stamp, sched_timing_today},
sched::timespan::{answer_button_time, learning_congrats, studied_today, time_span},
search::{search_cards, SortMode},
search::SortMode,
template::{
render_card, without_legacy_template_directives, FieldMap, FieldRequirements,
ParsedTemplate, RenderedNode,
@ -696,7 +696,7 @@ impl Backend {
} else {
SortMode::FromConfig
};
let cids = search_cards(col, &input.search, order)?;
let cids = col.search_cards(&input.search, order)?;
Ok(pb::SearchCardsOut {
card_ids: cids.into_iter().map(|v| v.0).collect(),
})

View File

@ -3,6 +3,7 @@
mod fields;
mod schema11;
mod schemachange;
mod stock;
mod templates;
@ -16,7 +17,9 @@ pub use stock::all_stock_notetypes;
pub use templates::CardTemplate;
use crate::{
collection::Collection,
define_newtype,
err::{AnkiError, Result},
template::{without_legacy_template_directives, FieldRequirements, ParsedTemplate},
text::ensure_string_in_nfc,
timestamp::TimestampSecs,
@ -175,3 +178,16 @@ impl From<NoteType> for NoteTypeProto {
}
}
}
impl Collection {
pub fn update_notetype(&mut self, nt: &mut NoteType) -> Result<()> {
self.transact(None, |col| {
let existing_notetype = col
.storage
.get_full_notetype(nt.id)?
.ok_or_else(|| AnkiError::invalid_input("no such notetype"))?;
col.update_notes_for_changed_fields(nt, existing_notetype.fields.len())?;
Ok(())
})
}
}

View File

@ -0,0 +1,73 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
use super::{NoteField, NoteType};
use crate::{collection::Collection, err::Result};
/// If any fields added, removed or reordered, returns a list of the new
/// field length, comprised of the original ordinals.
fn field_change_map(fields: &[NoteField], previous_field_count: usize) -> Option<Vec<Option<u32>>> {
let map: Vec<_> = fields.iter().map(|f| f.ord).collect();
let changed = map.len() != previous_field_count
|| map
.iter()
.enumerate()
.any(|(idx, f)| f != &Some(idx as u32));
if changed {
Some(map)
} else {
None
}
}
impl Collection {
/// Caller must create transaction
pub(crate) fn update_notes_for_changed_fields(
&mut self,
nt: &NoteType,
previous_field_count: usize,
) -> Result<()> {
let change_map = match field_change_map(&nt.fields, previous_field_count) {
None => {
// nothing to do
return Ok(());
}
Some(map) => map,
};
let nids = self.search_notes(&format!("mid:{}", nt.id))?;
let usn = self.usn()?;
for nid in nids {
let mut note = self.storage.get_note(nid)?.unwrap();
note.fields = change_map
.iter()
.map(|f| {
if let Some(idx) = f {
note.fields
.get(*idx as usize)
.map(AsRef::as_ref)
.unwrap_or("")
} else {
""
}
})
.map(Into::into)
.collect();
note.prepare_for_update(nt.config.sort_field_idx as usize, usn);
self.storage.update_note(&note)?;
}
Ok(())
}
}
#[cfg(test)]
mod test {
use crate::collection::open_test_collection;
#[test]
fn fields() {
let mut _col = open_test_collection();
// fixme: need note adding before we can check this
}
}

View File

@ -10,51 +10,49 @@ use crate::err::Result;
use crate::search::parser::parse;
use rusqlite::params;
pub(crate) enum SortMode {
pub enum SortMode {
NoOrder,
FromConfig,
Builtin { kind: SortKind, reverse: bool },
Custom(String),
}
pub(crate) fn search_cards<'a, 'b>(
req: &'b mut Collection,
search: &'a str,
order: SortMode,
) -> Result<Vec<CardID>> {
let top_node = Node::Group(parse(search)?);
let (sql, args) = node_to_sql(req, &top_node)?;
impl Collection {
pub fn search_cards(&mut self, search: &str, order: SortMode) -> Result<Vec<CardID>> {
let top_node = Node::Group(parse(search)?);
let (sql, args) = node_to_sql(self, &top_node)?;
let mut sql = format!(
"select c.id from cards c, notes n where c.nid=n.id and {}",
sql
);
let mut sql = format!(
"select c.id from cards c, notes n where c.nid=n.id and {}",
sql
);
match order {
SortMode::NoOrder => (),
SortMode::FromConfig => {
let kind = req.get_browser_sort_kind();
prepare_sort(req, &kind)?;
sql.push_str(" order by ");
write_order(&mut sql, &kind, req.get_browser_sort_reverse())?;
}
SortMode::Builtin { kind, reverse } => {
prepare_sort(req, &kind)?;
sql.push_str(" order by ");
write_order(&mut sql, &kind, reverse)?;
}
SortMode::Custom(order_clause) => {
sql.push_str(" order by ");
sql.push_str(&order_clause);
match order {
SortMode::NoOrder => (),
SortMode::FromConfig => {
let kind = self.get_browser_sort_kind();
prepare_sort(self, &kind)?;
sql.push_str(" order by ");
write_order(&mut sql, &kind, self.get_browser_sort_reverse())?;
}
SortMode::Builtin { kind, reverse } => {
prepare_sort(self, &kind)?;
sql.push_str(" order by ");
write_order(&mut sql, &kind, reverse)?;
}
SortMode::Custom(order_clause) => {
sql.push_str(" order by ");
sql.push_str(&order_clause);
}
}
let mut stmt = self.storage.db.prepare(&sql)?;
let ids: Vec<_> = stmt
.query_map(&args, |row| row.get(0))?
.collect::<std::result::Result<_, _>>()?;
Ok(ids)
}
let mut stmt = req.storage.db.prepare(&sql)?;
let ids: Vec<_> = stmt
.query_map(&args, |row| row.get(0))?
.collect::<std::result::Result<_, _>>()?;
Ok(ids)
}
/// Add the order clause to the sql.
@ -96,24 +94,24 @@ fn write_order(sql: &mut String, kind: &SortKind, reverse: bool) -> Result<()> {
// In the future these items should be moved from JSON into separate SQL tables,
// - for now we use a temporary deck to sort them.
fn prepare_sort(req: &mut Collection, kind: &SortKind) -> Result<()> {
fn prepare_sort(col: &mut Collection, kind: &SortKind) -> Result<()> {
use SortKind::*;
match kind {
CardDeck | NoteType => {
prepare_sort_order_table(req)?;
let mut stmt = req
prepare_sort_order_table(col)?;
let mut stmt = col
.storage
.db
.prepare("insert into sort_order (k,v) values (?,?)")?;
match kind {
CardDeck => {
for (k, v) in req.storage.get_all_decks()? {
for (k, v) in col.storage.get_all_decks()? {
stmt.execute(params![k, v.name()])?;
}
}
NoteType => {
for (k, v) in req.storage.get_all_notetypes_as_schema11()? {
for (k, v) in col.storage.get_all_notetypes_as_schema11()? {
stmt.execute(params![k, v.name])?;
}
}
@ -121,13 +119,13 @@ fn prepare_sort(req: &mut Collection, kind: &SortKind) -> Result<()> {
}
}
CardTemplate => {
prepare_sort_order_table2(req)?;
let mut stmt = req
prepare_sort_order_table2(col)?;
let mut stmt = col
.storage
.db
.prepare("insert into sort_order (k1,k2,v) values (?,?,?)")?;
for (ntid, nt) in req.storage.get_all_notetypes_as_schema11()? {
for (ntid, nt) in col.storage.get_all_notetypes_as_schema11()? {
for tmpl in nt.tmpls {
stmt.execute(params![ntid, tmpl.ord, tmpl.name])?;
}
@ -139,15 +137,15 @@ fn prepare_sort(req: &mut Collection, kind: &SortKind) -> Result<()> {
Ok(())
}
fn prepare_sort_order_table(req: &mut Collection) -> Result<()> {
req.storage
fn prepare_sort_order_table(col: &mut Collection) -> Result<()> {
col.storage
.db
.execute_batch(include_str!("sort_order.sql"))?;
Ok(())
}
fn prepare_sort_order_table2(req: &mut Collection) -> Result<()> {
req.storage
fn prepare_sort_order_table2(col: &mut Collection) -> Result<()> {
col.storage
.db
.execute_batch(include_str!("sort_order2.sql"))?;
Ok(())

View File

@ -3,4 +3,4 @@ mod notes;
mod parser;
mod sqlwriter;
pub(crate) use cards::{search_cards, SortMode};
pub use cards::SortMode;

View File

@ -73,7 +73,7 @@ impl SqliteStorage {
.collect()
}
fn get_full_notetype(&self, ntid: NoteTypeID) -> Result<Option<NoteType>> {
pub(crate) fn get_full_notetype(&self, ntid: NoteTypeID) -> Result<Option<NoteType>> {
match self.get_notetype_core(ntid)? {
Some(mut nt) => {
nt.fields = self.get_notetype_fields(ntid)?;