make it more ergonomic to search directly via nodes in Rust
This commit is contained in:
parent
2902b64e82
commit
9d604f1ad0
@ -25,7 +25,7 @@ impl SearchService for Backend {
|
||||
fn search_cards(&self, input: pb::SearchIn) -> Result<pb::SearchOut> {
|
||||
self.with_col(|col| {
|
||||
let order = input.order.unwrap_or_default().value.into();
|
||||
let cids = col.search::<CardId>(&input.search, order)?;
|
||||
let cids = col.search_cards(&input.search, order)?;
|
||||
Ok(pb::SearchOut {
|
||||
ids: cids.into_iter().map(|v| v.0).collect(),
|
||||
})
|
||||
@ -35,7 +35,7 @@ impl SearchService for Backend {
|
||||
fn search_notes(&self, input: pb::SearchIn) -> Result<pb::SearchOut> {
|
||||
self.with_col(|col| {
|
||||
let order = input.order.unwrap_or_default().value.into();
|
||||
let nids = col.search::<NoteId>(&input.search, order)?;
|
||||
let nids = col.search_notes(&input.search, order)?;
|
||||
Ok(pb::SearchOut {
|
||||
ids: nids.into_iter().map(|v| v.0).collect(),
|
||||
})
|
||||
|
@ -120,7 +120,7 @@ mod test {
|
||||
note2.set_field(0, "three aaa")?;
|
||||
col.add_note(&mut note2, DeckId(1))?;
|
||||
|
||||
let nids = col.search_notes("")?;
|
||||
let nids = col.search_notes_unordered("")?;
|
||||
let out = col.find_and_replace(nids.clone(), "(?i)AAA", "BBB", None)?;
|
||||
assert_eq!(out.output, 2);
|
||||
|
||||
|
@ -361,7 +361,7 @@ where
|
||||
let notetypes = self.ctx.get_all_notetypes()?;
|
||||
let mut collection_modified = false;
|
||||
|
||||
let nids = self.ctx.search_notes("")?;
|
||||
let nids = self.ctx.search_notes_unordered("")?;
|
||||
let usn = self.ctx.usn()?;
|
||||
for nid in nids {
|
||||
self.checked += 1;
|
||||
|
@ -702,7 +702,7 @@ mod test {
|
||||
.unwrap();
|
||||
|
||||
let assert_initial = |col: &mut Collection| -> Result<()> {
|
||||
assert_eq!(col.search_notes("")?.len(), 0);
|
||||
assert_eq!(col.search_notes_unordered("")?.len(), 0);
|
||||
assert_eq!(col.search_cards("", SortMode::NoOrder)?.len(), 0);
|
||||
assert_eq!(
|
||||
col.storage.db_scalar::<u32>("select count() from graves")?,
|
||||
@ -713,7 +713,7 @@ mod test {
|
||||
};
|
||||
|
||||
let assert_after_add = |col: &mut Collection| -> Result<()> {
|
||||
assert_eq!(col.search_notes("")?.len(), 1);
|
||||
assert_eq!(col.search_notes_unordered("")?.len(), 1);
|
||||
assert_eq!(col.search_cards("", SortMode::NoOrder)?.len(), 2);
|
||||
assert_eq!(
|
||||
col.storage.db_scalar::<u32>("select count() from graves")?,
|
||||
@ -740,7 +740,7 @@ mod test {
|
||||
assert_initial(&mut col)?;
|
||||
|
||||
let assert_after_remove = |col: &mut Collection| -> Result<()> {
|
||||
assert_eq!(col.search_notes("")?.len(), 0);
|
||||
assert_eq!(col.search_notes_unordered("")?.len(), 0);
|
||||
assert_eq!(col.search_cards("", SortMode::NoOrder)?.len(), 0);
|
||||
// 1 note + 2 cards
|
||||
assert_eq!(
|
||||
@ -753,7 +753,7 @@ mod test {
|
||||
|
||||
col.redo()?;
|
||||
assert_after_add(&mut col)?;
|
||||
let nids = col.search_notes("")?;
|
||||
let nids = col.search_notes_unordered("")?;
|
||||
col.remove_notes(&nids)?;
|
||||
assert_after_remove(&mut col)?;
|
||||
col.undo()?;
|
||||
|
@ -485,7 +485,7 @@ impl Collection {
|
||||
fn remove_notetype_inner(&mut self, ntid: NotetypeId) -> Result<()> {
|
||||
// remove associated cards/notes
|
||||
let usn = self.usn()?;
|
||||
let note_ids = self.search_notes(&format!("mid:{}", ntid))?;
|
||||
let note_ids = self.search_notes_unordered(ntid)?;
|
||||
self.remove_notes_inner(¬e_ids, usn)?;
|
||||
|
||||
// remove notetype
|
||||
|
@ -61,7 +61,7 @@ impl Collection {
|
||||
if !ords_changed(&ords, previous_field_count) {
|
||||
if nt.config.sort_field_idx != previous_sort_idx {
|
||||
// only need to update sort field
|
||||
let nids = self.search_notes(&format!("mid:{}", nt.id))?;
|
||||
let nids = self.search_notes_unordered(nt.id)?;
|
||||
for nid in nids {
|
||||
let mut note = self.storage.get_note(nid)?.unwrap();
|
||||
note.prepare_for_update(nt, normalize_text)?;
|
||||
@ -75,8 +75,7 @@ impl Collection {
|
||||
}
|
||||
|
||||
self.set_schema_modified()?;
|
||||
|
||||
let nids = self.search_notes(&format!("mid:{}", nt.id))?;
|
||||
let nids = self.search_notes_unordered(nt.id)?;
|
||||
let usn = self.usn()?;
|
||||
for nid in nids {
|
||||
let mut note = self.storage.get_note(nid)?.unwrap();
|
||||
@ -258,9 +257,7 @@ mod test {
|
||||
col.add_note(&mut note, DeckId(1))?;
|
||||
|
||||
assert_eq!(
|
||||
col.search_cards(&format!("nid:{}", note.id), SortMode::NoOrder)
|
||||
.unwrap()
|
||||
.len(),
|
||||
col.search_cards(note.id, SortMode::NoOrder).unwrap().len(),
|
||||
1
|
||||
);
|
||||
|
||||
@ -269,9 +266,7 @@ mod test {
|
||||
col.update_notetype(&mut nt, false)?;
|
||||
|
||||
assert_eq!(
|
||||
col.search_cards(&format!("nid:{}", note.id), SortMode::NoOrder)
|
||||
.unwrap()
|
||||
.len(),
|
||||
col.search_cards(note.id, SortMode::NoOrder).unwrap().len(),
|
||||
2
|
||||
);
|
||||
|
||||
|
@ -16,6 +16,7 @@ pub use crate::{
|
||||
notetype::{Notetype, NotetypeId},
|
||||
ops::{Op, OpChanges, OpOutput},
|
||||
revlog::RevlogId,
|
||||
search::TryIntoSearch,
|
||||
timestamp::{TimestampMillis, TimestampSecs},
|
||||
types::Usn,
|
||||
};
|
||||
|
@ -155,7 +155,7 @@ mod test {
|
||||
use crate::{
|
||||
card::{Card, CardQueue},
|
||||
collection::{open_test_collection, Collection},
|
||||
search::SortMode,
|
||||
search::{SortMode, StateKind},
|
||||
};
|
||||
|
||||
#[test]
|
||||
@ -168,7 +168,7 @@ mod test {
|
||||
col.add_card(&mut card).unwrap();
|
||||
let assert_count = |col: &mut Collection, cnt| {
|
||||
assert_eq!(
|
||||
col.search_cards("is:buried", SortMode::NoOrder)
|
||||
col.search_cards(StateKind::Buried, SortMode::NoOrder)
|
||||
.unwrap()
|
||||
.len(),
|
||||
cnt
|
||||
|
@ -8,8 +8,9 @@ use rand::seq::SliceRandom;
|
||||
use crate::{
|
||||
card::{CardQueue, CardType},
|
||||
deckconfig::NewCardOrder,
|
||||
match_all,
|
||||
prelude::*,
|
||||
search::SortMode,
|
||||
search::{Node, SortMode, StateKind},
|
||||
};
|
||||
|
||||
impl Card {
|
||||
@ -186,7 +187,7 @@ impl Collection {
|
||||
order: NewCardOrder,
|
||||
usn: Usn,
|
||||
) -> Result<usize> {
|
||||
let cids = self.search_cards(&format!("did:{} is:new", deck), SortMode::NoOrder)?;
|
||||
let cids = self.search_cards(match_all![deck, StateKind::New], SortMode::NoOrder)?;
|
||||
self.sort_cards_inner(&cids, 1, 1, order.into(), false, usn)
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,6 @@ use crate::{
|
||||
error::Result,
|
||||
notes::NoteId,
|
||||
prelude::AnkiError,
|
||||
search::parser::parse,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Copy)]
|
||||
@ -96,13 +95,62 @@ impl Column {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TryIntoSearch {
|
||||
fn try_into_search(self) -> Result<Node, AnkiError>;
|
||||
}
|
||||
|
||||
impl TryIntoSearch for &str {
|
||||
fn try_into_search(self) -> Result<Node, AnkiError> {
|
||||
parser::parse(self).map(Node::Group)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryIntoSearch for &String {
|
||||
fn try_into_search(self) -> Result<Node, AnkiError> {
|
||||
parser::parse(self).map(Node::Group)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> TryIntoSearch for T
|
||||
where
|
||||
T: Into<Node>,
|
||||
{
|
||||
fn try_into_search(self) -> Result<Node, AnkiError> {
|
||||
Ok(self.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl Collection {
|
||||
pub fn search<T>(&mut self, search: &str, mode: SortMode) -> Result<Vec<T>>
|
||||
pub fn search_cards<N>(&mut self, search: N, mode: SortMode) -> Result<Vec<CardId>>
|
||||
where
|
||||
N: TryIntoSearch,
|
||||
{
|
||||
self.search(search, mode)
|
||||
}
|
||||
|
||||
pub fn search_notes<N>(&mut self, search: N, mode: SortMode) -> Result<Vec<NoteId>>
|
||||
where
|
||||
N: TryIntoSearch,
|
||||
{
|
||||
self.search(search, mode)
|
||||
}
|
||||
|
||||
pub fn search_notes_unordered<N>(&mut self, search: N) -> Result<Vec<NoteId>>
|
||||
where
|
||||
N: TryIntoSearch,
|
||||
{
|
||||
self.search(search, SortMode::NoOrder)
|
||||
}
|
||||
}
|
||||
|
||||
impl Collection {
|
||||
fn search<T, N>(&mut self, search: N, mode: SortMode) -> Result<Vec<T>>
|
||||
where
|
||||
N: TryIntoSearch,
|
||||
T: FromSql + AsReturnItemType,
|
||||
{
|
||||
let item_type = T::as_return_item_type();
|
||||
let top_node = Node::Group(parse(search)?);
|
||||
let top_node = search.try_into_search()?;
|
||||
let writer = SqlWriter::new(self, item_type);
|
||||
|
||||
let (mut sql, args) = writer.build_query(&top_node, mode.required_table())?;
|
||||
@ -116,14 +164,6 @@ impl Collection {
|
||||
Ok(ids)
|
||||
}
|
||||
|
||||
pub fn search_cards(&mut self, search: &str, mode: SortMode) -> Result<Vec<CardId>> {
|
||||
self.search(search, mode)
|
||||
}
|
||||
|
||||
pub fn search_notes(&mut self, search: &str) -> Result<Vec<NoteId>> {
|
||||
self.search(search, SortMode::NoOrder)
|
||||
}
|
||||
|
||||
fn add_order(
|
||||
&mut self,
|
||||
sql: &mut String,
|
||||
@ -148,12 +188,11 @@ 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(
|
||||
&mut self,
|
||||
search: &str,
|
||||
mode: SortMode,
|
||||
) -> Result<usize> {
|
||||
let top_node = Node::Group(parse(search)?);
|
||||
pub(crate) fn search_cards_into_table<N>(&mut self, search: N, mode: SortMode) -> Result<usize>
|
||||
where
|
||||
N: TryIntoSearch,
|
||||
{
|
||||
let top_node = search.try_into_search()?;
|
||||
let writer = SqlWriter::new(self, ReturnItemType::Cards);
|
||||
let want_order = mode != SortMode::NoOrder;
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright: Ankitects Pty Ltd and contributors
|
||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
|
||||
use itertools::Itertools;
|
||||
use lazy_static::lazy_static;
|
||||
use nom::{
|
||||
branch::alt,
|
||||
@ -14,9 +15,8 @@ use nom::{
|
||||
use regex::{Captures, Regex};
|
||||
|
||||
use crate::{
|
||||
decks::DeckId,
|
||||
error::{ParseError, Result, SearchErrorKind as FailKind},
|
||||
notetype::NotetypeId,
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
type IResult<'a, O> = std::result::Result<(&'a str, O), nom::Err<ParseError<'a>>>;
|
||||
@ -57,6 +57,28 @@ impl Node {
|
||||
vec![self]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all(iter: impl IntoIterator<Item = Node>) -> Node {
|
||||
Node::Group(Itertools::intersperse(iter.into_iter(), Node::And).collect())
|
||||
}
|
||||
|
||||
pub fn any(iter: impl IntoIterator<Item = Node>) -> Node {
|
||||
Node::Group(Itertools::intersperse(iter.into_iter(), Node::Or).collect())
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! match_all {
|
||||
($($param:expr),+ $(,)?) => {
|
||||
Node::all(vec![$($param.into()),+])
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! match_any {
|
||||
($($param:expr),+ $(,)?) => {
|
||||
Node::any(vec![$($param.into()),+])
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
@ -151,6 +173,42 @@ pub fn parse(input: &str) -> Result<Vec<Node>> {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SearchNode> for Node {
|
||||
fn from(n: SearchNode) -> Self {
|
||||
Node::Search(n)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NotetypeId> for Node {
|
||||
fn from(id: NotetypeId) -> Self {
|
||||
Node::Search(SearchNode::NotetypeId(id))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TemplateKind> for Node {
|
||||
fn from(k: TemplateKind) -> Self {
|
||||
Node::Search(SearchNode::CardTemplate(k))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NoteId> for Node {
|
||||
fn from(n: NoteId) -> Self {
|
||||
Node::Search(SearchNode::NoteIds(format!("{}", n)))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<DeckId> for Node {
|
||||
fn from(id: DeckId) -> Self {
|
||||
Node::Search(SearchNode::DeckId(id))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StateKind> for Node {
|
||||
fn from(k: StateKind) -> Self {
|
||||
Node::Search(SearchNode::State(k))
|
||||
}
|
||||
}
|
||||
|
||||
/// Zero or more nodes inside brackets, eg 'one OR two -three'.
|
||||
/// Empty vec must be handled by caller.
|
||||
fn group_inner(input: &str) -> IResult<Vec<Node>> {
|
||||
|
@ -1428,7 +1428,7 @@ mod test {
|
||||
let deckid = deck.id;
|
||||
let dconfid = dconf.id;
|
||||
let noteid = note.id;
|
||||
let cardid = col1.search_cards(&format!("nid:{}", note.id), SortMode::NoOrder)?[0];
|
||||
let cardid = col1.search_cards(note.id, SortMode::NoOrder)?[0];
|
||||
let revlogid = RevlogId(123);
|
||||
|
||||
let compare_sides = |col1: &mut Collection, col2: &mut Collection| -> Result<()> {
|
||||
|
Loading…
Reference in New Issue
Block a user