I have a two tables: User and Track. For simplicity, items in these tables do not have any data (just their ids). These are then setup with a graph relationship: user ->owns track
Below is the code for setting this up:
#[tokio::main]
async fn main() -> surrealdb::Result<()> {
let db = surrealdb::engine::any::connect("mem://").await?;
db.use_ns("test").use_db("test").await?;
db.query("CREATE user:0").await?;
db.query("CREATE user:1").await?;
db.query("CREATE track:0").await?;
db.query("CREATE track:1").await?;
db.query("CREATE track:2").await?;
db.query("CREATE track:3").await?;
db.query("CREATE track:10").await?;
db.query("RELATE user:0->owns->track:0").await?;
db.query("RELATE user:0->owns->track:1").await?;
db.query("RELATE user:0->owns->track:2").await?;
db.query("RELATE user:0->owns->track:3").await?;
db.query("RELATE user:1->owns->track:0").await?;
db.query("RELATE user:1->owns->track:10").await?;
let res = db.query("SELECT ->owns->track FROM user:0").await?;
dbg!(res);
This output:
&res = Response(
{
0: Ok(
[
Object(
Object(
{
"->owns": Object(
Object(
{
"->track": Array(
Array(
[
Thing(
Thing {
tb: "track",
id: Number(
3,
),
},
),
Thing(
Thing {
tb: "track",
id: Number(
0,
),
},
),
Thing(
Thing {
tb: "track",
id: Number(
2,
),
},
),
Thing(
Thing {
tb: "track",
id: Number(
1,
),
},
),
],
),
),
},
),
),
},
),
),
],
),
},
)
The problem comes when I try to deserialize the Response:
let ids: Vec<?> = res.take(0)?;
dbg!(&ids);
I have tried every surrealdb::sql
type I found (i.e. Thing, Id, Graph, Array, Object, Value) and cannot figure out how to get the underlying array Things.
For example:
let ids: Vec<Thing> = res.take(0)?;
dbg!(&ids);
in which I get the error:
Error: Api(
FromValue {
value: Array(Array([
Object(
Object(
{
"->owns": Object(
Object(
{
"->track": Array(
Array(
[
Thing(
Thing {
tb: "track",
id: Number(
3,
),
},
),
Thing(
Thing {
tb: "track",
id: Number(
0,
),
},
),
Thing(
Thing {
tb: "track",
id: Number(
2,
),
},
),
Thing(
Thing {
tb: "track",
id: Number(
1,
),
},
),
],
),
),
},
),
),
},
),
),
],
),
}, error: "missing field `tb`" })
Alternatively, tried:
let ids: Vec<Array> = res.take(0)?;
Error:
Error: Api(...
, error: "invalid type: map, expected a sequence" })
I can't for the life of me figure out how to deserialize. Please help.
In this case you are selecting tracks from a single user, user:0
. You will only have one object back. So an Option<T>
would serve you well even though the official SurrealDB Rust SDK is still smart enough to deserialise it into a Vec<T>
if you prefer. You only have one query statement so take(0)
will do, like you already deduced. However, since you are only interested in the track IDs, you can just grab the ->owns
object directly like so:
let user: Option<User> = res.take("->owns")?;
take("->owns")
is short for take((0, "->owns"))
and User
is defined as
#[derive(Debug, Deserialize)]
struct User {
#[serde(rename = "->track")]
tracks: Vec<Thing>,
}
Here is the code in full:
use serde::Deserialize;
use surrealdb::sql::Thing;
#[derive(Debug, Deserialize)]
struct User {
#[serde(rename = "->track")]
tracks: Vec<Thing>,
}
#[tokio::main]
async fn main() -> surrealdb::Result<()> {
let db = surrealdb::engine::any::connect("mem://").await?;
db.use_ns("test").use_db("test").await?;
db.query("CREATE user:0").await?.check()?;
db.query("CREATE user:1").await?.check()?;
db.query("CREATE track:0").await?.check()?;
db.query("CREATE track:1").await?.check()?;
db.query("CREATE track:2").await?.check()?;
db.query("CREATE track:3").await?.check()?;
db.query("CREATE track:10").await?.check()?;
db.query("RELATE user:0->owns->track:0").await?.check()?;
db.query("RELATE user:0->owns->track:1").await?.check()?;
db.query("RELATE user:0->owns->track:2").await?.check()?;
db.query("RELATE user:0->owns->track:3").await?.check()?;
db.query("RELATE user:1->owns->track:0").await?.check()?;
db.query("RELATE user:1->owns->track:10").await?.check()?;
let mut res = db.query("SELECT ->owns->track FROM user:0").await?;
let user: Option<User> = res.take("->owns")?;
dbg!(user);
Ok(())
}
Running that prints this:
user = Some(
User {
tracks: [
Thing {
tb: "track",
id: Number(
1,
),
},
Thing {
tb: "track",
id: Number(
0,
),
},
Thing {
tb: "track",
id: Number(
2,
),
},
Thing {
tb: "track",
id: Number(
3,
),
},
],
},
)
The type information in the response object can be a bit overwhelming when you are trying to understand how to deserialise it. Perhaps pretty printing it as JSON would give you output that would be easier to understand. This
let user: Option<serde_json::Value> = res.take(0)?;
println!("{:#}", user.unwrap());
would have given you this
{
"->owns": {
"->track": [
{
"tb": "track",
"id": {
"Number": 2
}
},
{
"tb": "track",
"id": {
"Number": 1
}
},
{
"tb": "track",
"id": {
"Number": 3
}
},
{
"tb": "track",
"id": {
"Number": 0
}
}
]
}
}
NB: Just in case you were not aware of this, I added .check()?
at the end of every query whose output you were not consuming because each query statement has its own result. The first error you were propagating is for network and query parse errors. .check()?
checks if there were any errors when executing any of the statements and propagates the first error. You don't need it when using take
because that gives you back the result as well. You could also write those simple create queries as
db.create(Resource::from("user:1")).await?;
or
db.create(Resource::from(("user", 1))).await?;
I'm using surrealdb::opt::Resource
here so that the compiler knows what the query will return. That makes it possible to ignore the response without type hinting to the compiler what it should expect back.