use crate::graph::GraphStorage;
use crate::traversal::optimizer::path_analysis::{PathAnalysis, PathRequirement};
use crate::*;
struct StepFlatMap<'g, S: GraphStorage + 'g> {
child: AnonymousTraversal<'g, S>,
}
impl<'g, S: GraphStorage + 'g> Clone for StepFlatMap<'g, S> {
fn clone(&self) -> Self {
Self {
child: self.child.clone(),
}
}
}
impl<'g, S: GraphStorage + 'g> fmt::Display for StepFlatMap<'g, S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "flat_map({})", self.child)
}
}
fn flat_map_step<'g, S: GraphStorage + 'g>(child: AnonymousTraversal<'g, S>) -> GraphStep<'g, S> {
GraphStep::FlatMap("flat_map", Box::new(StepFlatMap { child }))
}
impl<'g, S: GraphStorage + 'g> GraphTraversalSource<'g, S> {
pub fn flat_map(self, traversal: AnonymousTraversal<'g, S>) -> Self {
self.push_step(flat_map_step(traversal))
}
}
impl __ {
pub fn flat_map<'g, S: GraphStorage + 'g>(
traversal: AnonymousTraversal<'g, S>,
) -> AnonymousTraversal<'g, S> {
AnonymousTraversal::new().flat_map(traversal)
}
}
impl<'g, S: GraphStorage + 'g> AnonymousTraversal<'g, S> {
pub fn flat_map(self, traversal: AnonymousTraversal<'g, S>) -> Self {
self.push_step(flat_map_step(traversal))
}
}
impl<'g, S: GraphStorage + 'g> GraphStepFlatMap<'g, S> for StepFlatMap<'g, S> {
fn process(
&self,
context: &mut TraverserContext<'_, S>,
source: Traverser<S>,
output: &mut Vec<Traverser<S>>,
) -> TraverserResult<()> {
let produced = self.child.apply_to(context, &source)?;
output.extend(
produced
.into_iter()
.map(|item| Traverser::with_parts(item.value, source.path, item.bulk, source.sack)),
);
Ok(())
}
fn child_traversals_mut(&mut self) -> Vec<&mut AnonymousTraversal<'g, S>> {
vec![&mut self.child]
}
fn analyze_nested_paths(
&mut self,
analysis: &PathAnalysis,
_downstream: &PathRequirement,
) -> PathRequirement {
self.child.analyze_paths(analysis, PathRequirement::None)
}
fn clone_box(&self) -> Box<dyn GraphStepFlatMap<'g, S> + 'g> {
Box::new(self.clone())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn every_result() {
test_dual!(
graph: GraphSource::tinkerpop_modern(),
rust: |g| g.v(None).values("name").flat_map(__::split("a").unfold()).to_list(),
rhai: [
r#"g.V().values("name").flatMap(__.split("a").unfold())"#,
r#"g.v().values("name").flat_map(__.split("a").unfold())"#,
],
assert: |res| assert_eq!(res.len(), 9),
);
}
#[test]
fn hides_the_child_path() {
test_dual!(
graph: GraphSource::tinkerpop_modern(),
rust: |g| g.v(None).flat_map(__::out(None).out(None)).path().to_list(),
rhai: [r#"g.V().flatMap(__.out().out()).path()"#],
assert: |res| {
assert_eq!(res.len(), 2);
for path in res {
let ElementProperty::Array(path) = path else { panic!("{res:?}") };
assert_eq!(path.len(), 2);
}
},
);
}
#[test]
fn reads_the_incoming_path() {
test_dual!(
graph: GraphSource::tinkerpop_modern(),
rust: |g| g.v(None).as_("a").flat_map(__::select("a")).count().to_list(),
rhai: [r#"g.V().as("a").flatMap(__.select("a")).count()"#],
assert: |res| assert_eq!(res[0].as_i64(), Some(6)),
);
}
#[test]
fn keeps_bulk() {
test_dual!(
graph: GraphSource::tinkerpop_modern(),
rust: |g| g.v(None).both(None).barrier().flat_map(__::out(None)).count().to_list(),
rhai: [r#"g.V().both().barrier().flatMap(__.out()).count()"#],
assert: |res| assert_eq!(res[0].as_i64(), Some(16)),
);
}
#[test]
fn profile() {
let graph = GraphSource::tinkerpop_modern();
let lock = graph.read();
let result = lock
.traversal()
.v(None)
.flat_map(__::out(None))
.profile()
.unwrap();
assert!(result.to_string().contains("flat_map("), "{result}");
}
}