// Copyright 2023 antkiller // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use std::{collections::HashSet, iter::repeat}; use tugraph::{ cursor::{EdgeCursor, VertexCursor}, field::{FieldData, FieldSpec, FieldType}, txn::{RwTxn, TxnRead, TxnWrite}, }; mod common; #[test] fn test_vertex_cursor() { let galaxy = common::open_galaxy_in_tmpdir().unwrap(); let graph = galaxy.open_graph("default", false).unwrap(); graph .add_vertex_label( "Node_0", &[FieldSpec { name: "id".into(), ty: FieldType::Int64, optional: false, }], "id", ) .unwrap(); graph .add_vertex_label( "Node_1", &[FieldSpec { name: "id".into(), ty: FieldType::Int64, optional: false, }], "id", ) .unwrap(); graph .add_vertex_label( "Node_2", &[FieldSpec { name: "id".into(), ty: FieldType::Int64, optional: false, }], "id", ) .unwrap(); const NUM_VERTEX: usize = 10; { let mut rw_txn = graph.create_rw_txn(false).unwrap(); (0..NUM_VERTEX).for_each(|i| { fn add_node(rw_txn: &mut RwTxn<'_>, label: &str, id: i64) { rw_txn .add_vertex(label, &["id"], &[FieldData::Int64(id)]) .unwrap(); } add_node(&mut rw_txn, "Node_0", i as i64); add_node(&mut rw_txn, "Node_1", i as i64); add_node(&mut rw_txn, "Node_2", i as i64); }); rw_txn.commit().unwrap(); } { let ro_txn = graph.create_ro_txn().unwrap(); assert_eq!( ro_txn.vertex_cur().unwrap().into_vertices().count(), NUM_VERTEX * 3 ); assert_eq!( ro_txn .vertex_cur() .unwrap() .into_vertex_labels() .collect::>(), HashSet::from_iter(["Node_0".into(), "Node_1".into(), "Node_2".into()]) ); assert_eq!( ro_txn .vertex_cur() .unwrap() .into_vertex_ids() .max() .unwrap(), (NUM_VERTEX * 3 - 1) as i64 ); assert_eq!( ro_txn .vertex_cur() .unwrap() .into_vertex_fields() .map(|fd| { assert_eq!(fd.len(), 1); match fd.first().unwrap() { FieldData::Int64(v) => *v, _ => panic!("fail to convert int64"), } }) .filter(|id| *id == (NUM_VERTEX - 1) as i64) .count(), 3 ); let lids: HashSet<_> = ["Node_0", "Node_1", "Node_2"] .map(|label| ro_txn.vertex_label_id(label).unwrap() as u16) .into(); assert_eq!( ro_txn .vertex_cur() .unwrap() .into_vertex_lids() .collect::>(), lids ); } } #[test] fn test_edge_cursor() { let galaxy = common::open_galaxy_in_tmpdir().unwrap(); let graph = galaxy.open_graph("default", false).unwrap(); graph .add_vertex_label( "Src", &[FieldSpec { name: "id".into(), ty: FieldType::Int64, optional: false, }], "id", ) .unwrap(); graph .add_vertex_label( "Dst", &[FieldSpec { name: "id".into(), ty: FieldType::Int64, optional: false, }], "id", ) .unwrap(); graph .add_edge_label( "IndexedEdge", &[FieldSpec { name: "index_field".into(), ty: FieldType::Int64, optional: false, }], "", [("Src", "Dst")], ) .unwrap(); graph .add_edge_label( "TemporalEdge", &[FieldSpec { name: "tid".into(), ty: FieldType::Int64, optional: false, }], "tid", [("Src", "Dst")], ) .unwrap(); graph .add_edge_index("IndexedEdge", "index_field", false) .unwrap(); const NUM_INDEXED_EDGE: usize = 5; const NUM_TEMPORAL_EDGE: usize = 10; let (src, dst) = { let mut rw_txn = graph.create_rw_txn(false).unwrap(); let src = rw_txn .add_vertex("Src", &["id"], &[FieldData::Int64(0)]) .unwrap(); let dst = rw_txn .add_vertex("Dst", &["id"], &[FieldData::Int64(0)]) .unwrap(); (0..NUM_INDEXED_EDGE).for_each(|i| { rw_txn .add_edge( src, dst, "IndexedEdge", &["index_field"], &[FieldData::Int64(i as i64)], ) .unwrap(); }); (0..NUM_TEMPORAL_EDGE).for_each(|i| { rw_txn .add_edge( src, dst, "TemporalEdge", &["tid"], &[FieldData::Int64(i as i64)], ) .unwrap(); }); rw_txn.commit().unwrap(); (src, dst) }; { let ro_txn = graph.create_ro_txn().unwrap(); let mut vcur = ro_txn.vertex_cur().unwrap(); assert_eq!( vcur.seek(src, true) .unwrap() .out_edge_cursor() .unwrap() .into_edges() .count(), vcur.num_out_edges(usize::MAX).unwrap().1 ); let mut vcur = ro_txn.vertex_cur().unwrap(); assert_eq!( vcur.seek(dst, true) .unwrap() .in_edge_cursor() .unwrap() .into_edges() .count(), vcur.num_in_edges(usize::MAX).unwrap().1 ); let mut vcur = ro_txn.vertex_cur().unwrap(); let src_cur = vcur.seek(src, true).unwrap(); assert_eq!( src_cur .out_edge_cursor() .unwrap() .into_edge_srcs() .collect::>(), HashSet::from_iter([src]) ); assert_eq!( src_cur .out_edge_cursor() .unwrap() .into_edge_dsts() .collect::>(), HashSet::from_iter([dst]) ); assert_eq!( src_cur .out_edge_cursor() .unwrap() .into_edge_labels() .collect::>(), HashSet::from_iter(["TemporalEdge".into(), "IndexedEdge".into()]) ); assert_eq!( src_cur .out_edge_cursor() .unwrap() .into_edge_tids() .collect::>(), HashSet::from_iter([0].into_iter().chain(0..NUM_TEMPORAL_EDGE as i64)) ); assert_eq!( src_cur .out_edge_cursor() .unwrap() .into_edge_eids() .collect::>(), (0..NUM_INDEXED_EDGE as i64) .chain(repeat(0).take(NUM_TEMPORAL_EDGE)) .collect::>() ); let index_ecur = ro_txn .edge_index_iter_values_from( "IndexedEdge", "index_field", &FieldData::Int64(1), &FieldData::Int64(i64::MAX), ) .unwrap(); assert_eq!( index_ecur.collect::>(), (1..NUM_INDEXED_EDGE as i64) .map(FieldData::Int64) .collect::>() ); } }