redis_graph/
types.rs

1use redis::{from_redis_value, FromRedisValue, RedisError, RedisResult, Value};
2use std::collections::HashMap;
3
4/// Contains the result of a Redis graph operation. All types of graph
5/// operations will return a result in this format. Some (for example
6/// CREATE) will only return data for select fields.
7#[derive(Default, Clone, Debug)]
8pub struct GraphResultSet {
9    /// A list of string keys occuring in the RETURN statement of a query.
10    pub header: Vec<String>,
11
12    /// A list of GraphResults with one entry per match of the query.
13    pub data: Vec<GraphResult>,
14
15    /// List of metadata returned with the query (eg. affected rows).
16    pub metadata: Vec<String>,
17}
18
19/// A graph query can return one or multiple values for every matching entry.
20/// A GraphResult contains a map of values for a single match. The map keys
21/// are the query RETURN statements, values can be of any GraphValue type in
22/// any order.
23/// The impl also contains some helper methods for easier extraction of graph
24/// values.
25///
26/// ```rust
27///use redis_graph::GraphResult;
28///let res = GraphResult::default();
29///
30///let name:Option<String> = res.get_scalar("person2.name");
31///let person = res.get_node("person");
32///let friend_rel = res.get_relation("friend");
33///
34/// ```
35///
36#[derive(Default, Clone, Debug)]
37pub struct GraphResult {
38    /// A map of raw return keys to GraphValues.
39    pub data: HashMap<String, GraphValue>,
40}
41
42/// Redis graph values can be one of 3 different types. Scalars are single
43/// values of any type supported by Redis. Nodes are sort of objects which
44/// can contain multiple name value pairs and Relations are relations between
45/// Nodes which themself can contain multiple name value pairs.
46#[derive(Clone, Debug)]
47pub enum GraphValue {
48    Scalar(Value),
49    Node(NodeValue),
50    Relation(RelationValue),
51}
52
53/// Represents a Redis graph node which is an object like structure with
54/// potentially multiple named fields.
55#[derive(Default, Clone, Debug)]
56pub struct NodeValue {
57    pub id: u64,
58    pub labels: Vec<String>,
59    pub properties: HashMap<String, Value>,
60}
61
62/// Represents a Redis graph relation between two nodes. Like a node it can
63/// potentially contain one or multiple named fields.
64#[derive(Default, Clone, Debug)]
65pub struct RelationValue {
66    pub id: u64,
67    pub rel_type: String,
68    pub src_node: u64,
69    pub dest_node: u64,
70    pub properties: HashMap<String, Value>,
71}
72
73/// Represents an entry returned from the GRAPH.SLOWLOG command.
74#[derive(Default, Clone, Debug)]
75pub struct SlowLogEntry {
76    /// A unix timestamp at which the log entry was processed.
77    pub timestamp: u64,
78    /// The issued command.
79    pub command: String,
80    /// The issued query.
81    pub query: String,
82    /// The amount of time needed for its execution, in milliseconds.
83    pub time: f64,
84}
85
86/// Simple wrapper around a graph config map that allows derserializing config
87/// values into rust types.
88#[derive(Default, Clone, Debug)]
89pub struct GraphConfig {
90    pub values: HashMap<String, Value>,
91}
92
93impl GraphConfig {
94    /// Extracts a config Redis value at key into an Option of the desired type. Will
95    /// return None in case the key did not exists. Will return an error in case the
96    /// value at key failed to be parsed into T.
97    pub fn get_value<T: FromRedisValue>(&self, key: &str) -> RedisResult<Option<T>> {
98        match self.values.get(key) {
99            Some(value) => from_redis_value(value),
100            _ => Ok(None),
101        }
102    }
103}
104
105impl GraphResultSet {
106    fn from_metadata(metadata: Vec<String>) -> Self {
107        GraphResultSet {
108            header: Vec::default(),
109            data: Vec::default(),
110            metadata,
111        }
112    }
113}
114
115/// Represents a group of returned graph values for a single matched result in
116/// the query. Contains some helper methods for easier extraction of graph values.
117impl GraphResult {
118    /// Returns a single GraphValue by it's key.
119    pub fn get_value(&self, key: &str) -> Option<&GraphValue> {
120        self.data.get(key)
121    }
122
123    /// Tries to extract a graph Scalar value into target type T. Will return
124    /// None in case the key does not exist, the target value is not a Scalar
125    /// or the value could not be parsed into T.
126    pub fn get_scalar<T: FromRedisValue>(&self, key: &str) -> Option<T> {
127        match self.get_value(key) {
128            Some(GraphValue::Scalar(value)) => from_redis_value(value).unwrap_or(None),
129            _ => None,
130        }
131    }
132
133    /// Tries to extract a graph Node value from Value at key. Will return
134    /// None in case the key does not exist or the target value is not a  
135    /// Node.
136    pub fn get_node(&self, key: &str) -> Option<&NodeValue> {
137        match self.get_value(key) {
138            Some(GraphValue::Node(value)) => Some(value),
139            _ => None,
140        }
141    }
142
143    /// Tries to extract a graph Relation value from Value at key. Will return
144    /// None in case the key does not exist or the target value is not a  
145    /// Relation.
146    pub fn get_relation(&self, key: &str) -> Option<&RelationValue> {
147        match self.get_value(key) {
148            Some(GraphValue::Relation(value)) => Some(value),
149            _ => None,
150        }
151    }
152}
153
154/// Enhances object like graph values (Node, Relation) that contain a map of
155/// properties with extraction function that allow parsing of the inner
156/// Redis values into requested types.
157pub trait WithProperties {
158    /// Returns a raw Redis value at key.
159    fn get_property_value(&self, key: &str) -> Option<&Value>;
160
161    /// Extracts a property Redis value at key into an Option of the desired type. Will
162    /// return None in case the key did not exists. Will return an error in case the
163    /// value at key failed to be parsed into T.
164    fn get_property<T: FromRedisValue>(&self, key: &str) -> RedisResult<Option<T>> {
165        match self.get_property_value(key) {
166            Some(value) => from_redis_value(value),
167            _ => Ok(None),
168        }
169    }
170
171    /// Extracts a property Redis value at key into an Option of the desired type. Will
172    /// return None in case of the key did not exist or the value at key failed to be
173    /// parsed into T.
174    fn get_property_option<T: FromRedisValue>(&self, key: &str) -> Option<T> {
175        self.get_property(key).unwrap_or(None)
176    }
177}
178
179/// Allows property extraction on NodeValues.
180impl WithProperties for NodeValue {
181    fn get_property_value(&self, key: &str) -> Option<&Value> {
182        self.properties.get(key)
183    }
184}
185
186/// Allows property extraction on RelationValues.
187impl WithProperties for RelationValue {
188    fn get_property_value(&self, key: &str) -> Option<&Value> {
189        self.properties.get(key)
190    }
191}
192
193impl FromRedisValue for GraphResultSet {
194    fn from_redis_value(v: &Value) -> RedisResult<Self> {
195        match *v {
196            Value::Bulk(ref values) if values.is_empty() => Ok(GraphResultSet::default()),
197            Value::Bulk(ref values) if values.len() == 1 => match values.get(0) {
198                Some(v) => {
199                    let data: Vec<String> = from_redis_value(v)?;
200                    Ok(GraphResultSet::from_metadata(data))
201                }
202                _ => Ok(GraphResultSet::default()),
203            },
204            Value::Bulk(ref values) => {
205                let header: Vec<String> = match values.get(0) {
206                    Some(v) => from_redis_value(v)?,
207                    _ => Vec::default(),
208                };
209
210                let data: Vec<GraphResult> = match values.get(1) {
211                    Some(Value::Bulk(v)) => v
212                        .iter()
213                        .map(|bulk| {
214                            let items: Vec<GraphValue> = from_redis_value(bulk)?;
215                            let mut data: HashMap<String, GraphValue> = HashMap::new();
216                            for (idx, name) in header.iter().enumerate() {
217                                if let Some(value) = items.get(idx) {
218                                    data.insert(name.to_string(), value.to_owned());
219                                }
220                            }
221                            Ok(GraphResult { data })
222                        })
223                        .collect::<RedisResult<Vec<GraphResult>>>()?,
224                    _ => Vec::default(),
225                };
226
227                let metadata: Vec<String> = match values.get(2) {
228                    Some(v) => from_redis_value(v)?,
229                    _ => Vec::default(),
230                };
231
232                Ok(GraphResultSet {
233                    header,
234                    data,
235                    metadata,
236                })
237            }
238            _ => Err(create_error("Could not parse graph result")),
239        }
240    }
241}
242
243impl FromRedisValue for GraphValue {
244    fn from_redis_value(v: &Value) -> RedisResult<Self> {
245        match v {
246            Value::Bulk(ref values) if values.len() == 3 => {
247                let res: NodeValue = from_redis_value(v)?;
248                Ok(GraphValue::Node(res))
249            }
250            Value::Bulk(_) => {
251                let res: RelationValue = from_redis_value(v)?;
252                Ok(GraphValue::Relation(res))
253            }
254            value => Ok(GraphValue::Scalar(value.clone())),
255        }
256    }
257}
258
259impl FromRedisValue for NodeValue {
260    fn from_redis_value(v: &Value) -> RedisResult<Self> {
261        let values = to_property_map(v)?;
262        let id: u64 = values
263            .get("id")
264            .map_or(Ok(Some(0)), from_redis_value)?
265            .unwrap();
266        let labels: Vec<String> = if values.get("labels").is_some() {
267            from_redis_value(values.get("labels").unwrap())?
268        } else {
269            Vec::default()
270        };
271        let properties: HashMap<String, Value> = if values.get("properties").is_some() {
272            to_property_map(values.get("properties").unwrap())?
273        } else {
274            HashMap::default()
275        };
276
277        Ok(NodeValue {
278            id,
279            labels,
280            properties,
281        })
282    }
283}
284
285impl FromRedisValue for RelationValue {
286    fn from_redis_value(v: &Value) -> RedisResult<Self> {
287        let values = to_property_map(v)?;
288        let id: u64 = values
289            .get("id")
290            .map_or(Ok(Some(0)), from_redis_value)?
291            .unwrap();
292        let rel_type: String = values
293            .get("type")
294            .map_or(Ok(Some("".to_string())), from_redis_value)?
295            .unwrap();
296        let src_node: u64 = values
297            .get("src_node")
298            .map_or(Ok(Some(0)), from_redis_value)?
299            .unwrap();
300        let dest_node: u64 = values
301            .get("dest_node")
302            .map_or(Ok(Some(0)), from_redis_value)?
303            .unwrap();
304        let properties: HashMap<String, Value> = if values.get("properties").is_some() {
305            to_property_map(values.get("properties").unwrap())?
306        } else {
307            HashMap::new()
308        };
309
310        Ok(RelationValue {
311            id,
312            rel_type,
313            src_node,
314            dest_node,
315            properties,
316        })
317    }
318}
319
320impl FromRedisValue for SlowLogEntry {
321    fn from_redis_value(v: &Value) -> RedisResult<Self> {
322        match v {
323            Value::Bulk(ref values) if values.len() == 4 => Ok(SlowLogEntry {
324                timestamp: from_redis_value(values.get(0).unwrap())?,
325                command: from_redis_value(values.get(1).unwrap())?,
326                query: from_redis_value(values.get(2).unwrap())?,
327                time: from_redis_value(values.get(3).unwrap())?,
328            }),
329            _ => Err(create_error("invalid_slow_log_entry")),
330        }
331    }
332}
333
334impl FromRedisValue for GraphConfig {
335    fn from_redis_value(v: &Value) -> RedisResult<Self> {
336        match v {
337            Value::Bulk(_) => Ok(GraphConfig {
338                values: to_property_map(v)?,
339            }),
340            _ => Ok(GraphConfig {
341                values: HashMap::default(),
342            }),
343        }
344    }
345}
346
347// Wraps a string error msg into a RedisError
348pub fn create_error(msg: &str) -> RedisError {
349    RedisError::from(std::io::Error::new(
350        std::io::ErrorKind::Other,
351        msg.to_string(),
352    ))
353}
354
355// Extracts a list of name value pairs from a graph result
356pub fn to_property_map(v: &Value) -> RedisResult<HashMap<String, Value>> {
357    let t: Vec<Vec<Value>> = match from_redis_value(v) {
358        Ok(v) => v,
359        _ => vec![],
360    };
361    let mut values: HashMap<String, Value> = HashMap::default();
362    for pair in t {
363        if pair.len() == 2 {
364            let key: String = from_redis_value(&pair[0])?;
365            values.insert(key, pair[1].clone());
366        }
367    }
368    Ok(values)
369}
370
371pub fn value_from_pair<T: FromRedisValue>(v: &Value) -> RedisResult<T> {
372    let r: (String, T) = from_redis_value(v)?;
373    Ok(r.1)
374}