meilisearch/meilisearch-rust

Create a macro to ease the configuration of indexes

irevoire opened this issue · 6 comments

Description
It would be cool if we could somehow create a kind of builder macro but with the settings of Meilisearch.
Some settings do not apply here; you can't set your stopwords or synonyms from a document for example.
But anything related to a field should be handled without any issue I think 🤔

Basic example

#[derive(Serialize, Deserialize, Document)]
struct Movie {
  #[document(primary_key)]
  movie_id: u64,
  #[document(displayed, searchable)]
  title: String,
  #[document(displayed)]
  description: String,
  #[document(filterable, sortable, displayed)]
  release_date: String,
  #[document(filterable, displayed)]
  genres: Vec<String>,
}

I'm not sure Document is the best name here.

This should generate something like that;

// this trait is not generated, it's just hanging somewhere in our crate
pub trait Document {
  fn generate_settings() -> Settings;
  async fn generate_index() -> Result<Index>;
}

impl Document for Movie {
  fn generate_settings() -> Settings {
    Settings::new()
        .with_displayed_attributes(["title", "description", "release_date", "genres"])
        .with_searchable_attributes(["title"])
        .with_filterable_attributes(["release_date", "genres"])
        .with_sortable_attributes(["release_date"])
  }

  async fn generate_index(client: &Client) -> Result<Index> {
    client.create_index("movie", Some("movie_id"))?
        .wait_for_completion(client)?
        .try_make_index(client)
  }
}

The macro should check as many errors as possible at compile time.
Like setting two primary keys or two distinct attributes.

I think that would be a great addition to the crate and make it way more accessible / easier to test.

Other

I don't think I'll have the time to work on this issue, so if someone else thinks it's a good addition and wants to implement it, please do not hesitate to ask questions when you're not sure about something!

Also, if you don't know anything about rust macros but would like to learn, you can start by following this workshop; https://github.com/dtolnay/proc-macro-workshop

I'll try this.

Could you point me to the Document trait mentioned in the description? I can see a mention in the lib.rs as well, but I'm unable to find its declaration in the code base. It's the only piece left in the implementation.

Hey @romilpunetha :) Document is a structure that you define yourself containing the structure of your documents!

For example

#[derive(Debug, Serialize, Deserialize, PartialEq)]
struct Document {
id: usize,
value: String,
kind: String,
nested: Nested,
}
impl PartialEq<Map<String, Value>> for Document {
fn eq(&self, rhs: &Map<String, Value>) -> bool {
self.id.to_string() == rhs["id"]
&& self.value == rhs["value"]
&& self.kind == rhs["kind"]
}
}
async fn setup_test_index(client: &Client, index: &Index) -> Result<(), Error> {
let t0 = index.add_documents(&[
Document { id: 0, kind: "text".into(), value: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.".to_string(), nested: Nested { child: "first".to_string() } },
Document { id: 1, kind: "text".into(), value: "dolor sit amet, consectetur adipiscing elit".to_string(), nested: Nested { child: "second".to_string() } },
Document { id: 2, kind: "title".into(), value: "The Social Network".to_string(), nested: Nested { child: "third".to_string() } },
Document { id: 3, kind: "title".into(), value: "Harry Potter and the Sorcerer's Stone".to_string(), nested: Nested { child: "fourth".to_string() } },
Document { id: 4, kind: "title".into(), value: "Harry Potter and the Chamber of Secrets".to_string(), nested: Nested { child: "fift".to_string() } },
Document { id: 5, kind: "title".into(), value: "Harry Potter and the Prisoner of Azkaban".to_string(), nested: Nested { child: "sixth".to_string() } },
Document { id: 6, kind: "title".into(), value: "Harry Potter and the Goblet of Fire".to_string(), nested: Nested { child: "seventh".to_string() } },
Document { id: 7, kind: "title".into(), value: "Harry Potter and the Order of the Phoenix".to_string(), nested: Nested { child: "eighth".to_string() } },
Document { id: 8, kind: "title".into(), value: "Harry Potter and the Half-Blood Prince".to_string(), nested: Nested { child: "ninth".to_string() } },
Document { id: 9, kind: "title".into(), value: "Harry Potter and the Deathly Hallows".to_string(), nested: Nested { child: "tenth".to_string() } },

The mentioned lib provided the structures related to the documents API:

pub struct DocumentsResults<T> {
pub results: Vec<T>,
pub limit: u32,
pub offset: u32,
pub total: u32,
}
#[derive(Debug, Clone, Serialize)]
pub struct DocumentQuery<'a> {
#[serde(skip_serializing)]
pub index: &'a Index,
/// The fields that should appear in the documents. By default all of the fields are present.
#[serde(skip_serializing_if = "Option::is_none")]
pub fields: Option<Vec<&'a str>>,
}

I don't think I did a good job of explaining the problem. Let me try again:

// this trait is not generated, it's just hanging somewhere in our crate
pub trait Document {
  fn generate_settings() -> Settings;
  async fn generate_index() -> Result<Index>;
}

This part in the description of the issue states that a Document trait exists somewhere in the crates. I couldn't find the trait and the requirement is that this trait needs to be implemented for the structs with the macro. If this trait does not exist, I'll create it. If it does, I'm unable to locate it.
Or am I still missing something here?

I think, from what I understand of the issue, is that Document does not exist and should be created with a better name.

@irevoire can you clarify the situation?

Fixed by #358