edgedb-rust icon indicating copy to clipboard operation
edgedb-rust copied to clipboard

Derive `QueryArgs` for structs to provide named query arguments

Open AdrienPensart opened this issue 1 year ago • 8 comments

Hello!

Would it be possible to execute queries like this :

#[derive(Queryable)]
struct MusicOutput {
    pub id: Uuid,
    pub name: String
}

let parameters = HashMap::from([
    ("artist", "John 5"),
]);
let music_output: Result<MusicOutput, _> = conn.query_required_single(
"select (
        insert Artist {
        name := <str>$artist
   } unless conflict on .name else (select Artist)
) {id, name}", &parameters).await;

AdrienPensart avatar Jun 21 '23 19:06 AdrienPensart

The problem with using HashMap is that arguments can be of different types. So dynamic typing should be used. I.e. edgedb_protocol::value::Value type. Unforutately, we don't have a nice constructor for that at least yet.

The plan in, however, to make a derive:

#[derive(QueryArgs)]
struct Artist {
  name: String,
}

But this is still on our todo list. For now, tuples are what is the easiest to use as query arguments.

tailhook avatar Jun 22 '23 08:06 tailhook

Before edgedb-rust offers a convenient way to construct named parameters from your struct, you can use named parameters with Client.query by:

  • Construct a edgedb_protocol::value::Value::Object from your map of Map<String, Value>.
  • Pass that Value::Object to Client.query_xxx

To construct edgedb_protocol::value::Value::Object from a map, you can reference https://github.com/edgedb/edgedb-rust/blob/releases/edgedb-protocol/v0.6.0/edgedb-protocol/src/value.rs#L100. Note that, the example is to build SparseObject, we just learn it to build Value:Object.

You can take a look at my examples:

https://github.com/hongquan/QuanWeb/blob/2bcb09b4e62dd7742e8ebb9fee809a7e9970111c/src/stores/blog.rs#L123-L157

https://github.com/hongquan/QuanWeb/blob/2bcb09b4e62dd7742e8ebb9fee809a7e9970111c/src/api/structs.rs#L168-L206

hongquan avatar Jul 07 '23 05:07 hongquan

Also: https://quan.hoabinh.vn/post/2023/8/querying-edgedb-with-name-parameters-in-rust

AlexDaniel avatar Sep 05 '23 00:09 AlexDaniel

https://github.com/edgedb/edgedb-rust/pull/304 should solve this

MrFoxPro avatar Apr 17 '24 20:04 MrFoxPro

Am I wrong, or do the QueryArgs docs (incorrectly) imply this is already possible with a derive macro?

You can derive it for a structure in this case it’s treated as a named tuple (i.e. query should include named arguments rather than numeric ones).

KevinMGranger avatar Sep 09 '24 14:09 KevinMGranger

@KevinMGranger But such a derive macro doesn't exist yet.

hongquan avatar Sep 09 '24 15:09 hongquan

I don't really understand what docs are trying to say here. I think they are saying that you could implement this trait to use named parameters.

Regardless, as @hongquan points out derive macro does not exist yet.

aljazerzen avatar Sep 09 '24 16:09 aljazerzen

For anyone finding this thread in the future, named_args! is currently recommended way to pass named params into client.query_xxx():

let query = "SELECT (<str>$my_str, <int64>$my_int)";
let args = edgedb_protocol::named_args! {
    "my_str" => "Hello world!".to_string(),
    "my_int" => Value::Int64(42),
};
let res = client.query(query, &args).await.unwrap();

This issue will remain open to track derive macro for QueryArgs on structs, as outlined by this comment.

aljazerzen avatar Sep 09 '24 16:09 aljazerzen