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

View file

@ -90,6 +90,16 @@ impl Collection {
Ok(()) 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( pub(crate) fn update_note_tags_undoable(
&mut self, &mut self,
tags: &NoteTags, 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() .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. /// 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<()> { pub(crate) fn update_note(&self, note: &Note) -> Result<()> {
assert!(note.id.0 != 0); assert!(note.id.0 != 0);
@ -78,6 +85,24 @@ impl super::SqliteStorage {
Ok(()) 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. /// Add or update the provided note, preserving ID. Used by the syncing code.
pub(crate) fn add_or_update_note(&self, note: &Note) -> Result<()> { pub(crate) fn add_or_update_note(&self, note: &Note) -> Result<()> {
let mut stmt = self.db.prepare_cached(include_str!("add_or_update.sql"))?; let mut stmt = self.db.prepare_cached(include_str!("add_or_update.sql"))?;