Try to keep source ids of imported notes

This commit is contained in:
RumovZ 2022-04-09 11:40:36 +02:00
parent 5a76d2211a
commit 2a15ba5404
4 changed files with 79 additions and 3 deletions

View file

@ -39,6 +39,7 @@ struct Context<'a> {
guid_map: HashMap<String, NoteMeta>,
remapped_notetypes: HashMap<NotetypeId, NotetypeId>,
remapped_notes: HashMap<NoteId, NoteId>,
existing_notes: HashSet<NoteId>,
remapped_decks: HashMap<DeckId, DeckId>,
remapped_deck_configs: HashMap<DeckConfigId, DeckConfigId>,
data: ExchangeData,
@ -128,6 +129,7 @@ impl<'a> Context<'a> {
let guid_map = target_col.storage.note_guid_map()?;
let usn = target_col.usn()?;
let normalize_notes = target_col.get_config_bool(BoolKey::NormalizeNoteText);
let existing_notes = target_col.storage.get_all_note_ids()?;
Ok(Self {
target_col,
archive,
@ -137,6 +139,7 @@ impl<'a> Context<'a> {
conflicting_notes: HashSet::new(),
remapped_notetypes: HashMap::new(),
remapped_notes: HashMap::new(),
existing_notes,
remapped_decks: HashMap::new(),
remapped_deck_configs: HashMap::new(),
added_cards: HashSet::new(),
@ -248,12 +251,23 @@ impl<'a> Context<'a> {
let notetype = self.get_expected_notetype(note.notetype_id)?;
note.prepare_for_update(&notetype, self.normalize_notes)?;
note.usn = self.usn;
let old_id = std::mem::take(&mut note.id);
self.target_col.add_note_only_undoable(note)?;
self.remapped_notes.insert(old_id, note.id);
self.uniquify_note_id(note);
self.target_col.add_note_only_with_id_undoable(note)?;
self.existing_notes.insert(note.id);
Ok(())
}
fn uniquify_note_id(&mut self, note: &mut Note) {
let original = note.id;
while self.existing_notes.contains(&note.id) {
note.id.0 += 999;
}
if original != note.id {
self.remapped_notes.insert(original, note.id);
}
}
fn get_expected_notetype(&mut self, ntid: NotetypeId) -> Result<Arc<Notetype>> {
self.target_col
.get_notetype(ntid)?

View file

@ -90,6 +90,16 @@ impl Collection {
Ok(())
}
/// Add a note, not adding any cards. Caller guarantees id is unique.
pub(crate) fn add_note_only_with_id_undoable(&mut self, note: &mut Note) -> Result<()> {
if self.storage.add_note_if_unique(note)? {
self.save_undo(UndoableNoteChange::Added(Box::new(note.clone())));
Ok(())
} else {
Err(AnkiError::invalid_input("note id existed"))
}
}
pub(crate) fn update_note_tags_undoable(
&mut self,
tags: &NoteTags,

View file

@ -0,0 +1,27 @@
INSERT
OR IGNORE INTO notes (
id,
guid,
mid,
mod,
usn,
tags,
flds,
sfld,
csum,
flags,
data
)
VALUES (
?,
?,
?,
?,
?,
?,
?,
?,
?,
0,
""
)

View file

@ -42,6 +42,13 @@ impl super::SqliteStorage {
.transpose()
}
pub fn get_all_note_ids(&self) -> Result<HashSet<NoteId>> {
self.db
.prepare("SELECT id FROM notes")?
.query_and_then([], |row| Ok(row.get(0)?))?
.collect()
}
/// If fields have been modified, caller must call note.prepare_for_update() prior to calling this.
pub(crate) fn update_note(&self, note: &Note) -> Result<()> {
assert!(note.id.0 != 0);
@ -78,6 +85,24 @@ impl super::SqliteStorage {
Ok(())
}
pub(crate) fn add_note_if_unique(&self, note: &Note) -> Result<bool> {
self.db
.prepare_cached(include_str!("add_if_unique.sql"))?
.execute(params![
note.id,
note.guid,
note.notetype_id,
note.mtime,
note.usn,
join_tags(&note.tags),
join_fields(note.fields()),
note.sort_field.as_ref().unwrap(),
note.checksum.unwrap(),
])
.map(|added| added == 1)
.map_err(Into::into)
}
/// Add or update the provided note, preserving ID. Used by the syncing code.
pub(crate) fn add_or_update_note(&self, note: &Note) -> Result<()> {
let mut stmt = self.db.prepare_cached(include_str!("add_or_update.sql"))?;