diff --git a/lektor_search/src/lib.rs b/lektor_search/src/lib.rs
index 1b16a0340da685744eb0be241e4e4f96e17b980a..c9f1a91b61bc46504759dfcca5b3f22cf7a520d2 100644
--- a/lektor_search/src/lib.rs
+++ b/lektor_search/src/lib.rs
@@ -3,7 +3,7 @@ mod search;
 mod traits;
 
 use futures::{prelude::*, stream::FuturesUnordered};
-use lektor_payloads::{Kara, KaraBy};
+use lektor_payloads::{KId, KaraBy};
 
 pub use crate::{batch::*, traits::*};
 
@@ -13,33 +13,31 @@ pub async fn search<const BATCH_SIZE: usize>(
     store: &impl KaraStore,
     extractor: impl KaraIdExtractor,
     search: Vec<KaraBy>,
-) -> Vec<&Kara> {
+) -> Vec<KId> {
     let Some(search) = search::Search::new(search) else {
         return Default::default();
     };
 
     stream::unfold(extractor, |mut state| async move {
-        if state.is_empty().await {
-            return None;
-        }
-        Some((state.next_id_batch::<BATCH_SIZE>().await, state)) // Get the chuncks of IDs
+        (!state.is_empty().await).then_some(())?;
+        Some((state.next_id_batch::<BATCH_SIZE>().await, state))
     })
-    //
-    // Get the karas out of the ids
-    //
     .then(|ids| store.get_kara_batch(ids))
-    .map(Batch::into_array)
-    //
-    // Filter karas only if they are matched by the search
-    //
-    .map(|karas| karas.map(|maybe| maybe.and_then(|kara| search.matches_and_map(kara))))
-    //
-    // Await the thing, build the return vector.
-    //
+    .map(|karas| {
+        (karas.into_array().into_iter())
+            .flat_map(|maybe| maybe.and_then(|kara| search.matches(kara).then_some(kara.id)))
+    })
     .collect::<FuturesUnordered<_>>()
     .await
     .into_iter()
     .flatten()
-    .flatten()
     .collect()
 }
+
+pub async fn count(
+    _store: &impl KaraStore,
+    _extractor: impl KaraIdExtractor,
+    _search: Vec<KaraBy>,
+) -> usize {
+    todo!()
+}
diff --git a/lektor_search/src/search.rs b/lektor_search/src/search.rs
index 9f8a8d0d27bd72bd2e794bf4d61d42882639eb00..68b259df8dcecf5cde3c992a29949014a864db2b 100644
--- a/lektor_search/src/search.rs
+++ b/lektor_search/src/search.rs
@@ -36,7 +36,7 @@ impl Search {
     }
 
     /// See if we matches a kara or not.
-    fn matches(&self, kara: &Kara) -> bool {
+    pub fn matches(&self, kara: &Kara) -> bool {
         macro_rules! ensure {
             ($expr:expr) => {{
                 if !($expr) {
@@ -81,11 +81,6 @@ impl Search {
         true
     }
 
-    /// If we match a kara (see [Self::matches]), then we return said kara.
-    pub fn matches_and_map<'a>(&self, kara: &'a Kara) -> Option<&'a Kara> {
-        self.matches(kara).then_some(kara)
-    }
-
     fn with_queries(self, queries: Vec<String>) -> Option<Self> {
         let queries = AhoCorasickBuilder::new()
             .ascii_case_insensitive(true)
diff --git a/lektor_search/src/traits.rs b/lektor_search/src/traits.rs
index 94ae174dc5b52828232f9a5a9fa0510ba1aa9b8a..a750f71e438a6ea71fbd08c9cc38cc9828c340b2 100644
--- a/lektor_search/src/traits.rs
+++ b/lektor_search/src/traits.rs
@@ -1,5 +1,5 @@
 use crate::batch::Batch;
-use lektor_payloads::{KId, Kara};
+use lektor_payloads::{KId, Kara, KaraBy};
 
 #[allow(async_fn_in_trait)]
 pub trait KaraIdExtractor {
@@ -7,7 +7,13 @@ pub trait KaraIdExtractor {
     async fn next_id(&mut self) -> Option<KId>;
 
     /// Get a next batch of kara id, to reduce any lock usage.
-    async fn next_id_batch<const SIZE: usize>(&mut self) -> Batch<SIZE, KId>;
+    async fn next_id_batch<const SIZE: usize>(&mut self) -> Batch<SIZE, KId> {
+        let mut ret = [None; SIZE];
+        for ret in &mut ret {
+            *ret = self.next_id().await;
+        }
+        ret.into()
+    }
 
     /// Get the number of karas to process until the extractor is empty.
     async fn count(&self) -> usize;
@@ -34,4 +40,25 @@ pub trait KaraStore {
         }
         Batch::<SIZE, &Kara>::from_array_maybe(ret)
     }
+
+    /// Search the the database with a subset with the [KaraIdExtractor] and a search filter with a
+    /// [Vec<KaraBy>] vector.
+    async fn search<const SIZE: usize>(
+        &self,
+        extractor: impl KaraIdExtractor,
+        search: Vec<KaraBy>,
+    ) -> Vec<KId>
+    where
+        Self: Sized,
+    {
+        crate::search::<SIZE>(self, extractor, search).await
+    }
+
+    /// Count the number of matches. The logic is the same as [KaraStore::search].
+    async fn count(&self, extractor: impl KaraIdExtractor, search: Vec<KaraBy>) -> usize
+    where
+        Self: Sized,
+    {
+        crate::count(self, extractor, search).await
+    }
 }
diff --git a/lektord/src/app/routes.rs b/lektord/src/app/routes.rs
index b158691e89739c36dbad823e61a10035cc188361..d71f918f0ff79c2afd357a8ff80c684471403b2c 100644
--- a/lektord/src/app/routes.rs
+++ b/lektord/src/app/routes.rs
@@ -7,6 +7,7 @@
 
 use crate::*;
 use anyhow::anyhow;
+use app::search_adaptors;
 use axum::{
     extract::{Path, State},
     http::{header::CONTENT_TYPE, HeaderValue},
@@ -15,6 +16,7 @@ use axum::{
 };
 use lektor_nkdb::*;
 use lektor_payloads::*;
+use lektor_search::KaraStore;
 use lektor_utils::decode_base64_json;
 use rand::{seq::SliceRandom, thread_rng};
 use std::ops::RangeBounds;
@@ -128,30 +130,6 @@ pub(crate) async fn get_kara_by_kid(
     Ok(kara)
 }
 
-/// Search some karas from a search set with a regex. We take the json argument as base64 encoded
-/// string, we decode it and deserialize it.
-#[axum::debug_handler(state = LektorStatePtr)]
-pub(crate) async fn search(
-    State(state): State<LektorStatePtr>,
-    Path(uri): Path<String>,
-) -> Result<Json<Vec<KId>>, LektordError> {
-    let SearchData { from, regex } = decode_base64_json(uri)?;
-    // Ok(Json(state.database.search(from, regex).await?))
-    todo!()
-}
-
-/// Count karas available in a search set. We take the json argument as base64 encoded
-/// string, we decode it and deserialize it.
-#[axum::debug_handler(state = LektorStatePtr)]
-pub(crate) async fn count(
-    State(state): State<LektorStatePtr>,
-    Path(uri): Path<String>,
-) -> Result<Json<usize>, LektordError> {
-    let SearchData { from, regex } = decode_base64_json(uri)?;
-    // Ok(Json(state.database.count(from, regex).await))
-    todo!()
-}
-
 /// Get all playlists from the database with their associated informations.
 #[axum::debug_handler(state = LektorStatePtr)]
 pub(crate) async fn get_playlists(State(state): State<LektorStatePtr>) -> Json<Vec<(KId, String)>> {
@@ -584,3 +562,72 @@ pub(crate) async fn delete_history_range(
             .await,
     )
 }
+
+macro_rules! extractor {
+    (
+        match $matched:expr
+        => { $($match_pat:pat => $match_handle:expr,)+ }
+        => |$arg:ident| $handle:expr $(,)?
+    ) => {
+        async move {
+            tokio::runtime::Builder::new_current_thread()
+                .build()
+                .context("failed to build the runtime")?
+                .block_on(async move {
+                    match $matched {
+                        $($match_pat => { let $arg = $match_handle; $handle })+
+                    }
+                })
+        }
+    };
+}
+
+/// Search some karas from a search set with a regex. We take the json argument as base64 encoded
+/// string, we decode it and deserialize it.
+#[axum::debug_handler(state = LektorStatePtr)]
+pub(crate) async fn search(
+    State(state): State<LektorStatePtr>,
+    Path(uri): Path<String>,
+) -> Result<Json<Vec<KId>>, LektordError> {
+    let SearchData { from, regex } = decode_base64_json(uri)?;
+    Ok(tokio::spawn(extractor! {
+        match from
+        => {
+            SearchFrom::Database      => search_adaptors::database_extractor(&state).await,
+            SearchFrom::Queue         => search_adaptors::queue_extractor(&state),
+            SearchFrom::History       => search_adaptors::history_extractor(&state),
+            SearchFrom::Playlist(plt) => search_adaptors::playlist_extractor(&state, plt),
+        }
+        => |extractor| Ok::<_, anyhow::Error>(Json(search_adaptors::kara_store(&state)
+            .search::<10>(extractor, regex)
+            .await
+        ))
+    })
+    .await
+    .context("failed to join the task")??)
+}
+
+/// Count karas available in a search set. We take the json argument as base64 encoded
+/// string, we decode it and deserialize it.
+#[axum::debug_handler(state = LektorStatePtr)]
+pub(crate) async fn count(
+    State(state): State<LektorStatePtr>,
+    Path(uri): Path<String>,
+) -> Result<Json<usize>, LektordError> {
+    let SearchData { from, regex } = decode_base64_json(uri)?;
+    Ok(tokio::spawn(extractor! {
+        match from
+        => {
+            SearchFrom::Database      => search_adaptors::database_extractor(&state).await,
+            SearchFrom::Queue         => search_adaptors::queue_extractor(&state),
+            SearchFrom::History       => search_adaptors::history_extractor(&state),
+            SearchFrom::Playlist(plt) => search_adaptors::playlist_extractor(&state, plt),
+        }
+        => |extractor| Ok::<_, anyhow::Error>(Json(search_adaptors::kara_store(&state)
+            .count(extractor, regex)
+            .await
+        ))
+    })
+    .await
+    .context("failed to join the task")??)
+}
diff --git a/lektord/src/app/search_adaptors.rs b/lektord/src/app/search_adaptors.rs
index dd414fe8471d1c63c0b55efb973b75ef438bcd8c..1a4136d0703ed417346a83629421ace260057887 100644
--- a/lektord/src/app/search_adaptors.rs
+++ b/lektord/src/app/search_adaptors.rs
@@ -18,17 +18,17 @@ pub async fn database_extractor(state: &LektorState) -> impl KaraIdExtractor + u
 }
 
 /// Get a thing to iterate over all the queue.
-pub async fn queue_extractor(state: &LektorState) -> impl KaraIdExtractor + use<'_> {
+pub fn queue_extractor(state: &LektorState) -> impl KaraIdExtractor + use<'_> {
     Queue(state, 0)
 }
 
 /// Get a thing to iterate over all the history.
-pub async fn history_extractor(state: &LektorState) -> impl KaraIdExtractor + use<'_> {
+pub fn history_extractor(state: &LektorState) -> impl KaraIdExtractor + use<'_> {
     History(state, 0)
 }
 
 /// Get a thing to iterate over all the kara ids of a playlist.
-pub async fn playlist_extractor(state: &LektorState, plt: KId) -> impl KaraIdExtractor + use<'_> {
+pub fn playlist_extractor(state: &LektorState, plt: KId) -> impl KaraIdExtractor + use<'_> {
     Playlist {
         handle: state.database.playlists(),
         playlist: plt,
@@ -37,15 +37,19 @@ pub async fn playlist_extractor(state: &LektorState, plt: KId) -> impl KaraIdExt
 }
 
 /// A database wrapper to implement [lektor_search::KaraStore].
+#[derive(Clone, Copy)]
 struct KaraStore<'a>(&'a lektor_nkdb::Database);
 
 /// A database wrapper to implement [lektor_search::KaraStore] to iterate over the history.
+#[derive(Clone, Copy)]
 struct History<'a>(&'a LektorState, usize);
 
 /// A database wrapper to implement [lektor_search::KaraStore] to iterate over the queue.
+#[derive(Clone, Copy)]
 struct Queue<'a>(&'a LektorState, usize);
 
 /// A database wrapper to implement [KaraIdExtractor] for a whole epoch.
+#[derive(Clone)]
 struct Database<'a>(EpochIds<'a>, usize);
 
 /// A database wrapper to implement [KaraIdExtractor] for a playlist.
@@ -60,17 +64,6 @@ impl<'a> KaraIdExtractor for Database<'a> {
         self.0.next().inspect(|_| self.1 -= 1).copied()
     }
 
-    async fn next_id_batch<const SIZE: usize>(&mut self) -> Batch<SIZE, KId> {
-        std::iter::from_fn(|| self.0.next().inspect(|_| self.1 -= 1).copied())
-            .enumerate()
-            .take(SIZE)
-            .fold([None; SIZE], |mut array, (idx, id)| {
-                array[idx] = Some(id);
-                array
-            })
-            .into()
-    }
-
     async fn count(&self) -> usize {
         self.1
     }
@@ -78,19 +71,11 @@ impl<'a> KaraIdExtractor for Database<'a> {
 
 impl<'a> KaraIdExtractor for History<'a> {
     async fn next_id(&mut self) -> Option<KId> {
-        let item = (self.0.queue())
+        let mut item = (self.0.queue())
             .read(|content, _| content.history((Bound::Included(self.1), Bound::Included(self.1))))
             .await;
-
-        match &item[..] {
-            [] => None,
-            [id] => {
-                self.1 += 1;
-                Some(*id)
-            }
-
-            _ => unreachable!(),
-        }
+        debug_assert!(item.len() <= 1);
+        item.pop().inspect(|_| self.1 += 1)
     }
 
     async fn next_id_batch<const SIZE: usize>(&mut self) -> Batch<SIZE, KId> {
@@ -119,19 +104,11 @@ impl<'a> KaraIdExtractor for History<'a> {
 
 impl<'a> KaraIdExtractor for Queue<'a> {
     async fn next_id(&mut self) -> Option<KId> {
-        let item = (self.0.queue())
+        let mut item = (self.0.queue())
             .read(|content, _| content.get((Bound::Included(self.1), Bound::Included(self.1))))
             .await;
-
-        match &item[..] {
-            [] => None,
-            [(_, id)] => {
-                self.1 += 1;
-                Some(*id)
-            }
-
-            _ => unreachable!(),
-        }
+        debug_assert!(item.len() <= 1);
+        item.pop().inspect(|_| self.1 += 1).map(|(_, id)| id)
     }
 
     async fn next_id_batch<const SIZE: usize>(&mut self) -> Batch<SIZE, KId> {