From 529105d3409182d11e4899c23c097cc6205ce1dc Mon Sep 17 00:00:00 2001 From: Dmitriy Pleshevskiy Date: Mon, 24 Oct 2022 18:06:28 +0300 Subject: [PATCH] commands/list: implement new list command to the search channel Closes #22 --- docker-compose.test.yml | 2 +- flake.lock | 42 +++++++++++++++++++------ flake.nix | 19 +++++++++--- src/channels/search.rs | 31 ++++++++++++++++++ src/commands.rs | 6 ++-- src/commands/list.rs | 69 +++++++++++++++++++++++++++++++++++++++++ src/protocol.rs | 19 ++++++++++++ tests/list_command.rs | 27 ++++++++++++++++ 8 files changed, 198 insertions(+), 17 deletions(-) create mode 100644 src/commands/list.rs create mode 100644 tests/list_command.rs diff --git a/docker-compose.test.yml b/docker-compose.test.yml index 768ccf2..d922674 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -2,7 +2,7 @@ version: '3' services: sonic: - image: valeriansaliou/sonic:v1.3.5 + image: valeriansaliou/sonic:v1.4.0 ports: - 36999:1491 volumes: diff --git a/flake.lock b/flake.lock index e9d3810..72f9a74 100644 --- a/flake.lock +++ b/flake.lock @@ -1,5 +1,20 @@ { "nodes": { + "flake-utils": { + "locked": { + "lastModified": 1659877975, + "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, "nixpkgs": { "locked": { "lastModified": 1662096612, @@ -18,22 +33,31 @@ }, "root": { "inputs": { + "flake-utils": "flake-utils", "nixpkgs": "nixpkgs", - "utils": "utils" + "sonic-server": "sonic-server" } }, - "utils": { + "sonic-server": { + "inputs": { + "flake-utils": [ + "flake-utils" + ], + "nixpkgs": [ + "nixpkgs" + ] + }, "locked": { - "lastModified": 1659877975, - "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "lastModified": 1666618211, + "narHash": "sha256-kKeIM9dXUi8eYcvSomx8kqSqq9zmqMx4mE5wdpEZ6O8=", + "owner": "pleshevskiy", + "repo": "sonic", + "rev": "e83de87ebd2e29b210c8cc6e7dfed5d2f14a0d24", "type": "github" }, "original": { - "owner": "numtide", - "repo": "flake-utils", + "owner": "pleshevskiy", + "repo": "sonic", "type": "github" } } diff --git a/flake.nix b/flake.nix index 7089b0e..015bbfa 100644 --- a/flake.nix +++ b/flake.nix @@ -1,17 +1,26 @@ { inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; - utils.url = "github:numtide/flake-utils"; + flake-utils.url = "github:numtide/flake-utils"; + + sonic-server = { + url = "github:pleshevskiy/sonic"; + inputs.nixpkgs.follows = "nixpkgs"; + inputs.flake-utils.follows = "flake-utils"; + }; }; - outputs = { self, nixpkgs, utils }: - utils.lib.eachDefaultSystem (system: + outputs = inputs @ { self, nixpkgs, flake-utils, ... }: + flake-utils.lib.eachDefaultSystem (system: let - pkgs = import nixpkgs { inherit system; }; + pkgs = import nixpkgs { + inherit system; + overlays = [ inputs.sonic-server.overlays.default ]; + }; in { devShell = pkgs.mkShell { - packages = with pkgs; [ cargo rustc rustfmt clippy rust-analyzer ]; + packages = with pkgs; [ cargo rustc rustfmt clippy rust-analyzer sonic-server inetutils ]; RUST_SRC_PATH = pkgs.rustPlatform.rustLibSrc; }; }); diff --git a/src/channels/search.rs b/src/channels/search.rs index 98c2309..3be6d34 100644 --- a/src/channels/search.rs +++ b/src/channels/search.rs @@ -130,4 +130,35 @@ impl SearchChannel { req: SuggestRequest, ); ); + + init_command!( + /// Enumerates all words in an index. + /// + /// Note: This method requires enabling the `search` feature and start + /// connection in Search mode. + /// + /// ```rust,no_run + /// # use sonic_channel::*; + /// # fn main() -> result::Result<()> { + /// let search_channel = SearchChannel::start( + /// "localhost:1491", + /// "SecretPassword", + /// )?; + /// + /// let result = search_channel.list( + /// ListRequest::new(Dest::col("search")) + /// )?; + /// dbg!(result); + /// + /// let result = search_channel.list( + /// ListRequest::new(Dest::col("search")).limit(2) + /// )?; + /// dbg!(result); + /// # Ok(()) + /// # } + /// ``` + use ListCommand for fn list( + req: ListRequest, + ); + ); } diff --git a/src/commands.rs b/src/commands.rs index de6c1ce..16ab06d 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -11,6 +11,8 @@ mod pop; #[cfg(feature = "ingest")] mod push; +#[cfg(feature = "search")] +mod list; #[cfg(feature = "search")] mod query; #[cfg(feature = "search")] @@ -29,9 +31,9 @@ pub(crate) use self::{ pub use self::{count::CountRequest, flush::FlushRequest, pop::PopRequest, push::PushRequest}; #[cfg(feature = "search")] -pub(crate) use self::{query::QueryCommand, suggest::SuggestCommand}; +pub(crate) use self::{list::ListCommand, query::QueryCommand, suggest::SuggestCommand}; #[cfg(feature = "search")] -pub use self::{query::QueryRequest, suggest::SuggestRequest}; +pub use self::{list::ListRequest, query::QueryRequest, suggest::SuggestRequest}; #[cfg(feature = "control")] pub(crate) use trigger::TriggerCommand; diff --git a/src/commands/list.rs b/src/commands/list.rs new file mode 100644 index 0000000..773959d --- /dev/null +++ b/src/commands/list.rs @@ -0,0 +1,69 @@ +use super::StreamCommand; +use crate::misc::Dest; +use crate::protocol; +use crate::result::*; + +/// Parameters for the `suggest` command. +#[derive(Debug)] +pub struct ListRequest { + /// Collection and bucket where we should enumerate all words in index. + pub dest: Dest, + /// Limit of result words. + pub limit: Option, + /// Offset of result words. + pub offset: Option, +} + +impl ListRequest { + /// Creates a base suggest request. + pub fn new(dest: Dest) -> Self { + Self { + dest, + limit: None, + offset: None, + } + } + + /// Set a limit for the request. + pub fn limit(mut self, limit: usize) -> Self { + self.limit = Some(limit); + self + } + + /// Set an offset for the request. + pub fn offset(mut self, offset: usize) -> Self { + self.offset = Some(offset); + self + } +} + +#[derive(Debug)] +pub struct ListCommand { + pub(crate) req: ListRequest, +} + +impl StreamCommand for ListCommand { + type Response = Vec; + + fn request(&self) -> protocol::Request { + let dest = &self.req.dest; + + protocol::Request::List { + collection: dest.collection().clone(), + bucket: dest + .bucket_opt() + .cloned() + .unwrap_or_else(|| String::from("default")), + limit: self.req.limit, + offset: self.req.offset, + } + } + + fn receive(&self, res: protocol::Response) -> Result { + if let protocol::Response::Event(protocol::EventKind::List, _id, words) = res { + Ok(words) + } else { + Err(Error::WrongResponse) + } + } +} diff --git a/src/protocol.rs b/src/protocol.rs index fcf4dcf..9160f13 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -76,6 +76,17 @@ impl Protocol { } } + #[rustfmt::skip] + Request::List { collection, bucket, limit, offset } => { + write!(res, "LIST {} {}", collection, bucket)?; + if let Some(limit) = limit { + write!(res, " LIMIT({})", limit)?; + } + if let Some(offset) = offset { + write!(res, " OFFSET({})", offset)?; + } + } + Request::Trigger(triger_req) => match triger_req { TriggerRequest::Consolidate => write!(res, "TRIGGER consolidate")?, TriggerRequest::Backup(path) => { @@ -123,6 +134,7 @@ impl Protocol { let event_kind = match segments.next() { Some("SUGGEST") => Ok(EventKind::Suggest), Some("QUERY") => Ok(EventKind::Query), + Some("LIST") => Ok(EventKind::List), _ => Err(Error::WrongResponse), }?; @@ -202,6 +214,7 @@ pub struct StartedPayload { pub enum EventKind { Suggest, Query, + List, } //===========================================================================// @@ -223,6 +236,12 @@ pub enum Request { word: String, limit: Option, }, + List { + collection: String, + bucket: String, + limit: Option, + offset: Option, + }, Query { collection: String, bucket: String, diff --git a/tests/list_command.rs b/tests/list_command.rs new file mode 100644 index 0000000..8a259ce --- /dev/null +++ b/tests/list_command.rs @@ -0,0 +1,27 @@ +mod common; +use common::*; + +const COLLECTION: &str = "Search"; + +#[test] +fn should_list_all_words() { + let bucket = "suggest_nearest"; + let title = "Sweet Teriyaki Beef Skewers"; + + let dest = Dest::col_buc(COLLECTION, bucket); + + let ingest_channel = ingest_start(); + ingest_channel + .push(PushRequest::new(dest.clone().obj("1"), title)) + .unwrap(); + + consolidate(); + + let search_channel = search_start(); + match search_channel.list(ListRequest::new(dest.clone())) { + Ok(object_ids) => assert_eq!(object_ids, vec!["beef", "skewers", "sweet", "teriyaki"]), + Err(e) => unreachable!(), + } + + flush_bucket(COLLECTION, bucket); +}