Thing/RecordId doesn't get serialized correctly
When using a RecordId (or Thing directly) to handle record references this crate (probably serde_json) doesn't correctly serialize them:
DEFINE FIELD owner ON anything TYPE record<user>
let id = <RecordId, Thing, etc.>
select(... Where(anything.owner, id) ...)
This will result in no query results and the vars look like this:
query = "SELECT * FROM trip WHERE owner = $owner"
params = Object(Object({"owner": Strand(Strand("user:ql6bh9y5kph22pcxqhzp"))}))
This is a workaround but it's very verbose:
(
Sql("WHERE id = type::thing($tb,$uid)"),
Bind(("tb", &*anything)),
Bind(("uid", id)),
),
This works as now the SurrealDB client will serialize the values, not this crate:
let query = QueryBuilder::new()
.select("*")
.from(&*anything)
.filter(anything.owner.equals_parameterized())
.build();
let params = ("owner", RecordId { ... });
Hi, indeed the values being serialized to JSON before you send them to the surrealdb .query causes the IDs to turn into a { id: { id }, tb } object whereas surrealdb expects all IDs to come in the form of strings so it can serialize them into Things or RecordIds.
This could be solved easily if this crate had surrealdb as a dependency but that's not the case unfortunately, the lack of dependency means you can use the querybuilder with any version of the surrealdb client.
So the way I avoid the problem is by creating myself a function that takes care of interfacing this querybuilding crate and the current surrealdb's client being used. That function iterates through the parameters from the querybuilding process, formats them if necessary, and binds them to the surrealdb query that's being constructed:
note that this example uses Thing while the version 2 of the surrealdb client recommends using RecordId now, you may have to adapt it
use surrealdb::method::Query;
use std::collections::HashMap;
fn db_query<N: Connection>(
query: String, params: HashMap<String, serde_json::Value>,
) -> Query<N> {
let mut query: Query<N> = DB.query(query);
for (key, value) in params {
match value {
serde_json::Value::Object(mut obj) => {
// if the current parameter is an object with id & tb keys (could have more though)
// manually convert it into a Thing and bind it
if obj.contains_key("id") && obj.contains_key("tb") {
use serde_json::Value;
use surrealdb::sql::Id;
let Some(Value::String(tb)) = obj.remove("tb") else {
continue;
};
let Some(Value::Object(mut id)) = obj.remove("id") else {
continue;
};
let Some(Value::String(id)) = id.remove("String") else {
continue;
};
query = query.bind((
key,
surrealdb::sql::Thing {
id: Id::from(id),
tb: tb,
},
));
} else {
query = query.bind((key, obj));
}
}
_ => {
query = query.bind((key, value));
}
};
}
query
}
You're then free to use it like so:
let (query, params) = select(... Where(anything.owner, id) ...);
let query = db_query(query, params);
let items = query.await?.take(0)?;
Hope this helps, and i'm interested if you have any suggestions or ideas on how to solve this flaw without introducing a dependency with surrealdb!
I see, thanks a bunch for the code, will add that to my query wrapper! I understand why the dependency on surrealdb might create more issues, unfortunately no idea how this could be solved better :(