1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
use crate::graph::GraphStorage;
use crate::*;
/// `key()`: the property key name of a `VertexProperty`/`EdgeProperty` traverser, as a plain
/// string — the read half of the pair with [`value()`](GraphTraversalSource::value). The key is
/// already carried in the reference's own first field (`properties()`/`values(key)` construct it
/// as `VertexProperty(key, ...)`/`EdgeProperty(key, ...)`, `flatmap_steps/step_values.rs`), so
/// this never touches storage, unlike `value()`.
#[derive(Default, Clone)]
struct StepKey {}
impl fmt::Display for StepKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "key()")
}
}
impl<'g, S: GraphStorage + 'g> GraphTraversalSource<'g, S> {
/// The key name of a property-object traverser (produced by
/// [`properties()`](Self::properties) or a raw [`values()`](Self::values) reference), as a
/// string. Errors (not panics, rule 1) when the traverser isn't a property object at all —
/// e.g. called directly on a bare vertex without going through `properties()`/`values()`
/// first, matching vanilla Gremlin's own behaviour.
///
/// ```rust
/// use graphersal::prelude::*;
///
/// let graph = GraphSource::tinkerpop_modern();
/// let lock = graph.read();
/// let keys = lock.traversal().v("1").properties(None).key().to_list().unwrap();
/// assert_eq!(keys.len(), 2);
/// ```
pub fn key(self) -> Self {
self.push_step(GraphStep::Map("key", Box::new(StepKey::default())))
}
}
impl __ {
/// Anonymous `key()`; see [`GraphTraversalSource::key`].
pub fn key<'g, S: GraphStorage + 'g>() -> AnonymousTraversal<'g, S> {
AnonymousTraversal::new().key()
}
}
impl<'g, S: GraphStorage + 'g> AnonymousTraversal<'g, S> {
/// Anonymous `key()`; see [`GraphTraversalSource::key`].
pub fn key(self) -> Self {
let step = GraphStep::Map("key", Box::new(StepKey::default()));
self.push_step(step)
}
}
impl<'g, S: GraphStorage + 'g> GraphStepMap<'g, S> for StepKey {
#[inline]
fn process(
&self,
_context: &mut TraverserContext<'_, S>,
source: Traverser<S>,
) -> TraverserResult<Traverser<S>> {
match &source.value {
TraverserValue::PathProperty(prop) => Ok(Traverser::with_parts(
TraverserValue::Computed(ElementProperty::String(prop.0.clone()), None),
source.path,
source.bulk,
source.sack,
)),
TraverserValue::VertexProperty(key, _, _) | TraverserValue::EdgeProperty(key, _, _) => {
Ok(Traverser::with_parts(
TraverserValue::Computed(ElementProperty::String(key.clone()), None),
source.path,
source.bulk,
source.sack,
))
}
v => Err(Box::new(TraverserError::cast_exception(
&["vertexProperty", "edgeProperty", "pathProperty"],
v,
))),
}
}
fn clone_box(&self) -> Box<dyn GraphStepMap<'g, S> + 'g> {
Box::new(self.clone())
}
}
#[cfg(test)]
mod tests {
use super::*;
/// `g.V().properties().key()`: the correct multiset of vertex property
/// key names across all six `tinkerpop_modern` vertices — four `person`s (`name`, `age`
/// each) and two `software`s (`name`, `lang` each): six `name`s, four `age`s, two `lang`s.
#[test]
fn v_all_keys() {
test_dual!(
graph: GraphSource::tinkerpop_modern(),
rust: |g| g.v(None).properties(None).key().to_list(),
rhai: [
r#"g.V().properties().key()"#,
r#"g.v().properties().key()"#,
],
assert: |res| {
let mut keys: Vec<&str> = res.iter().map(|v| v.as_string().unwrap()).collect();
keys.sort_unstable();
assert_eq!(
keys,
vec!["age", "age", "age", "age", "lang", "lang", "name", "name", "name", "name", "name", "name"]
);
},
);
}
#[test]
fn on_bare_vertex_is_cast_exception_not_panic() {
let graph = GraphSource::tinkerpop_modern();
let g_lock = graph.read();
let g = g_lock.traversal();
let err = g
.v("1")
.key()
.next()
.expect_err("a bare vertex is not a property object");
assert!(matches!(
err.root_cause(),
TraverserError::Value(ValueError::CastException { .. })
));
}
#[test]
fn profile() {
let graph = GraphSource::tinkerpop_modern();
let g_lock = graph.read();
let g = g_lock.traversal();
let result = g.v("1").properties("name").key().profile().unwrap();
assert!(result.to_string().contains("key("));
}
}