From 332f23d5b76581997e698736cb91ddbfe853720a Mon Sep 17 00:00:00 2001 From: Fabian Murariu <2404621+fabianmurariu@users.noreply.github.com> Date: Tue, 4 Jun 2024 00:04:44 +0100 Subject: [PATCH] Refactor pre release (#1631) * rename arrow to disk and raphtory-arrow to pometry-storage * transition to pometry-storage naming * rust fmt * rename more things to pometry-storage * get rid of the warning on Timestamps * Renaming * refactor bits * rename storage types to Disk* * Final changes * deactivate * deactivate 2 * use DiskGraph in python * More refactoring * fmt * fixed warnings * Fixed python tests * typos --------- Co-authored-by: miratepuffin --- .github/workflows/_release_rust.yml | 4 +- .github/workflows/benchmark.yml | 2 +- .github/workflows/test_python_workflow.yml | 8 +- .github/workflows/test_rust_workflow.yml | 18 +- .gitignore | 2 +- .gitmodules | 6 +- Cargo.lock | 15 +- Cargo.toml | 12 +- Makefile | 10 +- docs/source/conf.py | 20 +- pometry-storage-private | 1 + pometry-storage/Cargo.toml | 3 + .../src/lib.rs | 0 python/Cargo.toml | 2 +- python/python/raphtory/__init__.py | 2 +- python/src/lib.rs | 11 +- ...test_arrow_graph.py => test_disk_graph.py} | 4 +- .../{test_arrowgraph.py => test_diskgraph.py} | 10 +- python/tests/test_graphdb.py | 270 ++++++++++++------ raphtory-arrow-private | 1 - raphtory-arrow/Cargo.toml | 3 - raphtory-benchmark/Cargo.toml | 2 +- raphtory-benchmark/benches/arrow_algobench.rs | 10 +- raphtory-benchmark/bin/generate_graph.py | 2 +- raphtory-benchmark/bin/run_graphql.py | 2 +- raphtory-benchmark/python/kuzu_bench.py | 12 +- raphtory-benchmark/python/neo4j_bench.py | 16 +- raphtory-benchmark/python/raphtory_bench.py | 6 +- raphtory-cypher/Cargo.toml | 4 +- raphtory-cypher/examples/raphtory_cypher.rs | 20 +- .../src/executor/table_provider/edge.rs | 18 +- .../src/executor/table_provider/node.rs | 12 +- raphtory-cypher/src/hop/execution.rs | 25 +- raphtory-cypher/src/hop/operator.rs | 14 +- raphtory-cypher/src/hop/rule.rs | 16 +- raphtory-cypher/src/lib.rs | 68 ++--- raphtory-cypher/src/transpiler/mod.rs | 16 +- raphtory-graphql/Cargo.toml | 2 +- raphtory-graphql/src/data.rs | 46 ++- raphtory-graphql/src/lib.rs | 14 +- raphtory-graphql/src/model/mod.rs | 28 +- raphtory/Cargo.toml | 11 +- .../src/algorithms/centrality/betweenness.rs | 8 +- .../centrality/degree_centrality.rs | 8 +- raphtory/src/algorithms/centrality/hits.rs | 8 +- .../src/algorithms/centrality/pagerank.rs | 40 +-- .../community_detection/label_propagation.rs | 8 +- .../algorithms/community_detection/louvain.rs | 24 +- .../community_detection/modularity.rs | 16 +- .../components/connected_components.rs | 40 +-- .../algorithms/components/in_components.rs | 8 +- .../algorithms/components/out_components.rs | 8 +- raphtory/src/algorithms/components/scc.rs | 32 +-- raphtory/src/algorithms/cores/k_core.rs | 8 +- .../algorithms/dynamics/temporal/epidemics.rs | 8 +- raphtory/src/algorithms/metrics/balance.rs | 8 +- .../metrics/clustering_coefficient.rs | 8 +- raphtory/src/algorithms/metrics/degree.rs | 8 +- .../metrics/directed_graph_density.rs | 16 +- .../metrics/local_clustering_coefficient.rs | 8 +- .../src/algorithms/metrics/reciprocity.rs | 8 +- .../global_temporal_three_node_motifs.rs | 8 +- .../local_temporal_three_node_motifs.rs | 15 +- .../algorithms/motifs/local_triangle_count.rs | 8 +- .../src/algorithms/motifs/triangle_count.rs | 8 +- .../src/algorithms/motifs/triplet_count.rs | 8 +- raphtory/src/algorithms/pathing/dijkstra.rs | 48 ++-- .../pathing/single_source_shortest_path.rs | 8 +- .../pathing/temporal_reachability.rs | 32 +-- .../temporal_bipartite_projection.rs | 14 +- raphtory/src/arrow/storage_interface/nodes.rs | 26 -- raphtory/src/core/mod.rs | 4 +- raphtory/src/core/utils/hashing.rs | 6 +- .../src/db/api/storage/edges/edge_entry.rs | 12 +- .../db/api/storage/edges/edge_owned_entry.rs | 20 +- raphtory/src/db/api/storage/edges/edge_ref.rs | 24 +- .../db/api/storage/edges/edge_storage_ops.rs | 26 +- raphtory/src/db/api/storage/edges/edges.rs | 40 +-- .../src/db/api/storage/nodes/node_entry.rs | 24 +- .../db/api/storage/nodes/node_owned_entry.rs | 24 +- raphtory/src/db/api/storage/nodes/node_ref.rs | 28 +- raphtory/src/db/api/storage/nodes/nodes.rs | 16 +- .../src/db/api/storage/nodes/nodes_ref.rs | 20 +- raphtory/src/db/api/storage/storage_ops.rs | 96 +++---- .../src/db/api/storage/tprop_storage_ops.rs | 20 +- .../api/storage/variants/storage_variants.rs | 50 ++-- raphtory/src/db/api/view/edge.rs | 28 +- raphtory/src/db/api/view/graph.rs | 14 +- raphtory/src/db/api/view/internal/core_ops.rs | 20 +- .../src/db/api/view/internal/materialize.rs | 24 +- raphtory/src/db/api/view/time.rs | 80 +++--- raphtory/src/db/graph/edge.rs | 14 +- raphtory/src/db/graph/graph.rs | 232 ++++++++------- raphtory/src/db/graph/mod.rs | 1 - raphtory/src/db/graph/node.rs | 12 +- raphtory/src/db/graph/views/layer_graph.rs | 12 +- raphtory/src/db/graph/views/node_subgraph.rs | 20 +- raphtory/src/db/graph/views/window_graph.rs | 96 +++---- .../graph_impl/const_properties_ops.rs | 4 +- .../graph_impl/core_ops.rs | 47 ++- .../graph_impl/edge_filter_ops.rs | 4 +- .../graph_impl/edge_storage_ops.rs | 6 +- .../graph_impl/interop.rs | 4 +- .../graph_impl/layer_ops.rs | 6 +- .../graph_impl/list_ops.rs | 4 +- .../graph_impl/materialize.rs | 4 +- .../{arrow => disk_graph}/graph_impl/mod.rs | 204 ++++++------- .../graph_impl/node_filter_ops.rs | 4 +- .../graph_impl/prop_conversion.rs | 8 +- .../graph_impl/temporal_properties_ops.rs | 6 +- .../graph_impl/time_index_into_ops.rs | 10 +- .../graph_impl/time_semantics.rs | 50 ++-- .../graph_impl/tprops.rs | 52 ++-- raphtory/src/{arrow => disk_graph}/mod.rs | 20 +- .../src/{arrow => disk_graph}/query/ast.rs | 0 .../query/executors/mod.rs | 0 .../query/executors/rayon2.rs | 24 +- .../src/{arrow => disk_graph}/query/mod.rs | 22 +- .../src/{arrow => disk_graph}/query/state.rs | 2 +- .../storage_interface/edge.rs | 14 +- .../storage_interface/edges.rs | 14 +- .../storage_interface/edges_ref.rs | 14 +- .../storage_interface/mod.rs | 0 .../storage_interface/node.rs | 24 +- .../src/disk_graph/storage_interface/nodes.rs | 26 ++ .../storage_interface/nodes_ref.rs | 16 +- raphtory/src/lib.rs | 6 +- .../python/graph/{arrow.rs => disk_graph.rs} | 78 ++--- raphtory/src/python/graph/graph.rs | 2 +- raphtory/src/python/graph/mod.rs | 6 +- raphtory/src/python/graph/views/graph_view.rs | 4 +- raphtory/src/python/packages/algorithms.rs | 12 +- .../src/python/types/wrappers/document.rs | 1 + .../netflowsorted/nft_sorted/nft2.parquet | Bin 68981 -> 0 bytes resource/netflowsorted/v1_sorted/v1.parquet | Bin 76219 -> 0 bytes resource/netflowsorted/v2_sorted/v2.parquet | Bin 97326 -> 0 bytes ...e_arrow.py => activate_private_storage.py} | 12 +- ...arrow.py => deactivate_private_storage.py} | 12 +- 138 files changed, 1483 insertions(+), 1397 deletions(-) create mode 160000 pometry-storage-private create mode 100644 pometry-storage/Cargo.toml rename {raphtory-arrow => pometry-storage}/src/lib.rs (100%) rename python/tests/{test_arrow_graph.py => test_disk_graph.py} (96%) rename python/tests/{test_arrowgraph.py => test_diskgraph.py} (92%) delete mode 160000 raphtory-arrow-private delete mode 100644 raphtory-arrow/Cargo.toml delete mode 100644 raphtory/src/arrow/storage_interface/nodes.rs rename raphtory/src/{arrow => disk_graph}/graph_impl/const_properties_ops.rs (91%) rename raphtory/src/{arrow => disk_graph}/graph_impl/core_ops.rs (87%) rename raphtory/src/{arrow => disk_graph}/graph_impl/edge_filter_ops.rs (88%) rename raphtory/src/{arrow => disk_graph}/graph_impl/edge_storage_ops.rs (95%) rename raphtory/src/{arrow => disk_graph}/graph_impl/interop.rs (97%) rename raphtory/src/{arrow => disk_graph}/graph_impl/layer_ops.rs (95%) rename raphtory/src/{arrow => disk_graph}/graph_impl/list_ops.rs (87%) rename raphtory/src/{arrow => disk_graph}/graph_impl/materialize.rs (82%) rename raphtory/src/{arrow => disk_graph}/graph_impl/mod.rs (83%) rename raphtory/src/{arrow => disk_graph}/graph_impl/node_filter_ops.rs (82%) rename raphtory/src/{arrow => disk_graph}/graph_impl/prop_conversion.rs (98%) rename raphtory/src/{arrow => disk_graph}/graph_impl/temporal_properties_ops.rs (90%) rename raphtory/src/{arrow => disk_graph}/graph_impl/time_index_into_ops.rs (97%) rename raphtory/src/{arrow => disk_graph}/graph_impl/time_semantics.rs (90%) rename raphtory/src/{arrow => disk_graph}/graph_impl/tprops.rs (85%) rename raphtory/src/{arrow => disk_graph}/mod.rs (95%) rename raphtory/src/{arrow => disk_graph}/query/ast.rs (100%) rename raphtory/src/{arrow => disk_graph}/query/executors/mod.rs (100%) rename raphtory/src/{arrow => disk_graph}/query/executors/rayon2.rs (94%) rename raphtory/src/{arrow => disk_graph}/query/mod.rs (92%) rename raphtory/src/{arrow => disk_graph}/query/state.rs (97%) rename raphtory/src/{arrow => disk_graph}/storage_interface/edge.rs (86%) rename raphtory/src/{arrow => disk_graph}/storage_interface/edges.rs (88%) rename raphtory/src/{arrow => disk_graph}/storage_interface/edges_ref.rs (90%) rename raphtory/src/{arrow => disk_graph}/storage_interface/mod.rs (100%) rename raphtory/src/{arrow => disk_graph}/storage_interface/node.rs (97%) create mode 100644 raphtory/src/disk_graph/storage_interface/nodes.rs rename raphtory/src/{arrow => disk_graph}/storage_interface/nodes_ref.rs (59%) rename raphtory/src/python/graph/{arrow.rs => disk_graph.rs} (90%) delete mode 100644 resource/netflowsorted/nft_sorted/nft2.parquet delete mode 100644 resource/netflowsorted/v1_sorted/v1.parquet delete mode 100644 resource/netflowsorted/v2_sorted/v2.parquet rename scripts/{activate_private_arrow.py => activate_private_storage.py} (56%) rename scripts/{deactivate_private_arrow.py => deactivate_private_storage.py} (56%) diff --git a/.github/workflows/_release_rust.yml b/.github/workflows/_release_rust.yml index 48e37003c3..127bc89818 100644 --- a/.github/workflows/_release_rust.yml +++ b/.github/workflows/_release_rust.yml @@ -60,12 +60,12 @@ jobs: with: command: publish args: --token ${{ secrets.CRATES_TOKEN }} --package raphtory-api --allow-dirty - - name: "Publish raphtory-arrow to crates.io" + - name: "Publish pometry-storage to crates.io" if: ${{ !inputs.dry_run }} uses: actions-rs/cargo@v1 with: command: publish - args: --token ${{ secrets.CRATES_TOKEN }} --package raphtory-arrow --allow-dirty + args: --token ${{ secrets.CRATES_TOKEN }} --package pometry-storage --allow-dirty - name: "Publish raphtory to crates.io" if: ${{ !inputs.dry_run }} uses: actions-rs/cargo@v1 diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 3e68ac2074..9bf3cc992a 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -34,7 +34,7 @@ jobs: with: cache-all-crates: true - uses: webfactory/ssh-agent@v0.9.0 - name: Load raphtory-arrow key + name: Load raphtory-disk_graph key with: ssh-private-key: ${{ secrets.RA_SSH_PRIVATE_KEY }} - uses: actions-rs/toolchain@v1 diff --git a/.github/workflows/test_python_workflow.yml b/.github/workflows/test_python_workflow.yml index cad65b237f..b846e5753b 100644 --- a/.github/workflows/test_python_workflow.yml +++ b/.github/workflows/test_python_workflow.yml @@ -47,7 +47,7 @@ jobs: override: true components: rustfmt, clippy - uses: webfactory/ssh-agent@v0.7.0 - name: Load raphtory-arrow key + name: Load raphtory-disk_graph key with: ssh-private-key: ${{ secrets.RA_SSH_PRIVATE_KEY }} - uses: Swatinem/rust-cache@v2 @@ -59,15 +59,15 @@ jobs: with: python-version: ${{ matrix.python }} cache: 'pip' - - name: Activate raphtory-arrow in Cargo.toml - run: make pull-arrow + - name: Activate pometry-storage in Cargo.toml + run: make pull-storage - name: Run Maturin develop uses: PyO3/maturin-action@v1 with: working-directory: ./python command: build target: ${{ matrix.target }} - args: --release --features arrow + args: --release --features storage - name: Install Python dependencies (Unix) if: "contains(matrix.os, 'Ubuntu') || contains(matrix.os, 'macOS')" run: | diff --git a/.github/workflows/test_rust_workflow.yml b/.github/workflows/test_rust_workflow.yml index 577d6867ef..9bb036f278 100644 --- a/.github/workflows/test_rust_workflow.yml +++ b/.github/workflows/test_rust_workflow.yml @@ -43,7 +43,7 @@ jobs: sudo rm -rf /opt/ghc sudo rm -rf "$AGENT_TOOLSDIRECTORY" - uses: webfactory/ssh-agent@v0.7.0 - name: Load raphtory-arrow key + name: Load pometry-storage key with: ssh-private-key: ${{ secrets.RA_SSH_PRIVATE_KEY }} - name: Rust version @@ -56,20 +56,20 @@ jobs: uses: cargo-bins/cargo-binstall@main - name: Install nextest run: cargo binstall -y --force cargo-nextest - - name: Run all Tests (no arrow) + - name: Run all Tests (no disk_graph) env: RUSTFLAGS: -Awarnings TEMPDIR: ${{ runner.temp }} run: | cargo nextest run --all --no-default-features - - name: Activate raphtory-arrow in Cargo.toml - run: make pull-arrow - - name: Run all Tests (arrow) + - name: Activate pometry-storage in Cargo.toml + run: make pull-storage + - name: Run all Tests (disk_graph) env: RUSTFLAGS: -Awarnings TEMPDIR: ${{ runner.temp }} run: | - cargo nextest run --all --no-default-features --features "arrow" + cargo nextest run --all --no-default-features --features "storage" - name: Run Tests (features=io) env: RUSTFLAGS: -Awarnings @@ -90,11 +90,11 @@ jobs: RUSTFLAGS: -Awarnings run: | cargo check -p raphtory --no-default-features --features "vectors" - - name: Run Tests (features=arrow) + - name: Run Tests (features=storage) env: RUSTFLAGS: -Awarnings run: | - cargo check -p raphtory --no-default-features --features "arrow" + cargo check -p raphtory --no-default-features --features "storage" doc-test: if: ${{ !inputs.skip_tests }} name: "Doc tests" @@ -118,7 +118,7 @@ jobs: with: cache-all-crates: true - uses: webfactory/ssh-agent@v0.9.0 - name: Load raphtory-arrow key + name: Load raphtory-disk_graph key with: ssh-private-key: ${{ secrets.RA_SSH_PRIVATE_KEY }} - name: Set up Python diff --git a/.gitignore b/.gitignore index a7adf0e15d..abf1fc09ca 100644 --- a/.gitignore +++ b/.gitignore @@ -22,7 +22,7 @@ docs/logs/ docs/nx.html docs/graph.html .env -raphtory-arrow/ +pometry-arrow/ # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/.gitmodules b/.gitmodules index 12818e631f..0a34c6e8f0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ -[submodule "raphtory-arrow-private"] - path = raphtory-arrow-private - url = git@github.com:Pometry/raphtory-arrow.git +[submodule "pometry-storage-private"] + path = pometry-storage-private + url = git@github.com:Pometry/pometry-storage.git diff --git a/Cargo.lock b/Cargo.lock index 81c50de0ac..91ad9dbc48 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3539,7 +3539,6 @@ dependencies = [ "polars-error", "polars-utils", "ryu", - "serde", "simdutf8", "streaming-iterator", "strength_reduce", @@ -3611,6 +3610,10 @@ dependencies = [ "version_check", ] +[[package]] +name = "pometry-storage" +version = "0.8.1" + [[package]] name = "portable-atomic" version = "1.6.0" @@ -3982,6 +3985,7 @@ dependencies = [ "polars-arrow", "polars-parquet", "polars-utils", + "pometry-storage", "pretty_assertions", "proptest", "pyo3", @@ -3991,7 +3995,6 @@ dependencies = [ "rand 0.8.5", "rand_distr", "raphtory-api", - "raphtory-arrow", "rayon", "regex", "reqwest", @@ -4019,10 +4022,6 @@ dependencies = [ "serde", ] -[[package]] -name = "raphtory-arrow" -version = "0.8.1" - [[package]] name = "raphtory-benchmark" version = "0.8.1" @@ -4033,9 +4032,9 @@ dependencies = [ "csv", "flate2", "polars-arrow", + "pometry-storage", "rand 0.8.5", "raphtory", - "raphtory-arrow", "raphtory-graphql", "rayon", "sorted_vector_map", @@ -4060,11 +4059,11 @@ dependencies = [ "pest", "pest_derive", "polars-arrow", + "pometry-storage", "pretty_assertions", "proptest", "rand 0.8.5", "raphtory", - "raphtory-arrow", "rayon", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index e68ad6df4d..52857c70c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [ "raphtory", "raphtory-cypher", "raphtory-benchmark", - "raphtory-arrow", + "pometry-storage", "examples/rust", "examples/netflow", "python", @@ -30,10 +30,10 @@ inherits = "release" debug = true [workspace.dependencies] -#[public-arrow] -raphtory-arrow = { version = "0.8.1", path = "raphtory-arrow" } -#[private-arrow] -# raphtory-arrow = { path = "raphtory-arrow-private", package = "raphtory-arrow-private" } +#[public-storage] +pometry-storage = { version = "0.8.1", path = "pometry-storage" } +#[private-storage] +# pometry-storage = { path = "pometry-storage-private", package = "pometry-storage-private" } async-graphql = { version = "6.0.11", features = ["dynamic-schema"] } async-graphql-poem = "6.0.11" dynamic-graphql = "0.8.1" @@ -135,7 +135,7 @@ arrow-schema = { version = "50" } arrow-data = { version = "50" } arrow-array = { version = "50" } -# Make sure that transitive dependencies stick to arrow 50 +# Make sure that transitive dependencies stick to disk_graph 50 [patch.crates-io] arrow = { git = "https://github.com/apache/arrow-rs.git", tag = "50.0.0" } arrow-buffer = { git = "https://github.com/apache/arrow-rs.git", tag = "50.0.0" } diff --git a/Makefile b/Makefile index 6bd3acadbd..8476de7221 100644 --- a/Makefile +++ b/Makefile @@ -37,11 +37,11 @@ rust-test-all: cargo check -p raphtory --no-default-features --features "search" cargo check -p raphtory --no-default-features --features "vectors" -activate-arrow: - ./scripts/activate_private_arrow.py +activate-storage: + ./scripts/activate_private_storage.py -deactivate-arrow: - ./scripts/deactivate_private_arrow.py +deactivate-storage: + ./scripts/deactivate_private_storage.py -pull-arrow: activate-arrow +pull-storage: activate-storage git submodule update --init --recursive diff --git a/docs/source/conf.py b/docs/source/conf.py index dba69a4404..f733d0db1b 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -15,9 +15,9 @@ # -- Project information ----------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information -project = 'Raphtory' -copyright = '2023, Pometry' -author = 'Pometry' +project = "Raphtory" +copyright = "2023, Pometry" +author = "Pometry" release = __version__ git_ref = "master" @@ -62,8 +62,8 @@ # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output -html_theme = 'pydata_sphinx_theme' -html_static_path = ['_static'] +html_theme = "pydata_sphinx_theme" +html_static_path = ["_static"] html_css_files = [ "css/custom.css", ] @@ -74,12 +74,10 @@ static_assets_root = "https://raw.githubusercontent.com/Pometry/Raphtory/master" html_logo = "_static/logos/raphtory-logo-bright-medium.png" -html_context = { - "default_mode": "auto" -} +html_context = {"default_mode": "auto"} html_theme_options = { - 'nosidebar': True, + "nosidebar": True, "search_bar_text": "Search here...", "external_links": [ { @@ -118,7 +116,7 @@ "show_version_warning_banner": True, "navbar_end": ["theme-switcher", "navbar-icon-links"], "check_switcher": False, - "show_toc_level": 3 + "show_toc_level": 3, } # sphinx-favicon - Add support for custom favicons @@ -139,4 +137,4 @@ # sphinx view code viewcode_line_numbers = True -autodoc_typehints = 'both' +autodoc_typehints = "both" diff --git a/pometry-storage-private b/pometry-storage-private new file mode 160000 index 0000000000..9360b1c921 --- /dev/null +++ b/pometry-storage-private @@ -0,0 +1 @@ +Subproject commit 9360b1c921a448b6cff7701cb69038e6c8e50dac diff --git a/pometry-storage/Cargo.toml b/pometry-storage/Cargo.toml new file mode 100644 index 0000000000..a31f53e8ad --- /dev/null +++ b/pometry-storage/Cargo.toml @@ -0,0 +1,3 @@ +[package] +name = "pometry-storage" +version = "0.8.1" diff --git a/raphtory-arrow/src/lib.rs b/pometry-storage/src/lib.rs similarity index 100% rename from raphtory-arrow/src/lib.rs rename to pometry-storage/src/lib.rs diff --git a/python/Cargo.toml b/python/Cargo.toml index 0c628d5004..ccebf9972c 100644 --- a/python/Cargo.toml +++ b/python/Cargo.toml @@ -32,7 +32,7 @@ itertools = { workspace = true } [features] default = ["extension-module"] -arrow = ["raphtory_core/arrow"] +storage = ["raphtory_core/storage"] extension-module = ["pyo3/extension-module"] diff --git a/python/python/raphtory/__init__.py b/python/python/raphtory/__init__.py index 435a67c07b..b656e51b2d 100644 --- a/python/python/raphtory/__init__.py +++ b/python/python/raphtory/__init__.py @@ -8,7 +8,7 @@ sys.modules["raphtory.graphql"] = graphql try: - sys.modules["raphtory.arrow"] = arrow + sys.modules["raphtory.disk_graph"] = disk_graph except Exception as e: print(e) diff --git a/python/src/lib.rs b/python/src/lib.rs index eb23569098..eeac9de8f4 100644 --- a/python/src/lib.rs +++ b/python/src/lib.rs @@ -1,11 +1,12 @@ +#![allow(non_local_definitions)] mod graphql; extern crate core; use graphql::*; use pyo3::prelude::*; -#[cfg(feature = "arrow")] -use raphtory_core::python::graph::arrow::{PyArrowGraph, PyGraphQuery, PyState}; +#[cfg(feature = "storage")] +use raphtory_core::python::graph::disk_graph::{PyDiskGraph, PyGraphQuery, PyState}; use raphtory_core::python::{ graph::{ algorithm_result::AlgorithmResult, @@ -65,8 +66,8 @@ fn raphtory(py: Python<'_>, m: &PyModule) -> PyResult<()> { GraphIndex ); - #[cfg(feature = "arrow")] - add_classes!(m, PyArrowGraph, PyGraphQuery, PyState); + #[cfg(feature = "storage")] + add_classes!(m, PyDiskGraph, PyGraphQuery, PyState); //GRAPHQL let graphql_module = PyModule::new(py, "graphql")?; @@ -117,7 +118,7 @@ fn raphtory(py: Python<'_>, m: &PyModule) -> PyResult<()> { cohesive_fruchterman_reingold, ); - #[cfg(feature = "arrow")] + #[cfg(feature = "storage")] add_functions!(algorithm_module, connected_components,); m.add_submodule(algorithm_module)?; diff --git a/python/tests/test_arrow_graph.py b/python/tests/test_disk_graph.py similarity index 96% rename from python/tests/test_arrow_graph.py rename to python/tests/test_disk_graph.py index 9fca20ed53..6ee68dcb77 100644 --- a/python/tests/test_arrow_graph.py +++ b/python/tests/test_disk_graph.py @@ -1,4 +1,4 @@ -from raphtory import ArrowGraph, Query, State, PyDirection +from raphtory import DiskGraph, Query, State, PyDirection import pandas as pd import tempfile @@ -33,7 +33,7 @@ def create_graph(edges, dir): - return ArrowGraph.load_from_pandas(dir, edges, "src", "dst", "time") + return DiskGraph.load_from_pandas(dir, edges, "src", "dst", "time") # in every test use with to create a temporary directory that will be deleted automatically diff --git a/python/tests/test_arrowgraph.py b/python/tests/test_diskgraph.py similarity index 92% rename from python/tests/test_arrowgraph.py rename to python/tests/test_diskgraph.py index 3af535cb91..7319f95d0e 100644 --- a/python/tests/test_arrowgraph.py +++ b/python/tests/test_diskgraph.py @@ -1,13 +1,13 @@ -from raphtory import ArrowGraph +from raphtory import DiskGraph from raphtory import algorithms from utils import measure import tempfile import os -def test_arrow_graph(): +def test_disk_graph(): curr_dir = os.path.dirname(os.path.abspath(__file__)) - rsc_dir = os.path.join(curr_dir, "..", "..", "resource") + rsc_dir = os.path.join(curr_dir, "..","..", "pometry-storage-private", "resources") rsc_dir = os.path.normpath(rsc_dir) print("rsc_dir:", rsc_dir + "/netflowsorted/nft_sorted") @@ -44,7 +44,7 @@ def test_arrow_graph(): try: g = measure( "Graph load from dir", - ArrowGraph.load_from_dir, + DiskGraph.load_from_dir, graph_dir, print_result=False, ) @@ -57,7 +57,7 @@ def test_arrow_graph(): g = measure( "Graph load from parquets", - ArrowGraph.load_from_parquets, + DiskGraph.load_from_parquets, graph_dir.name, layer_parquet_cols, None, diff --git a/python/tests/test_graphdb.py b/python/tests/test_graphdb.py index bf60a7d15a..fe732bd179 100644 --- a/python/tests/test_graphdb.py +++ b/python/tests/test_graphdb.py @@ -984,7 +984,7 @@ def test_save_load_graph(): assert v.properties.temporal == {"type": [(1, "wallet")], "balance": [(1, 99.5)]} os.remove(graph_rel_path) - + tmpdirname = tempfile.TemporaryDirectory() graph_abs_path = tmpdirname.name + "/test_graph.bin" g.save_to_file(graph_abs_path) @@ -996,7 +996,7 @@ def test_save_load_graph(): view = g.window(0, 10) assert g.has_node(13) assert view.node(13).in_degree() == 1 - + tmpdirname.cleanup() @@ -1479,37 +1479,45 @@ def test_layer_name(): assert g.edge(0, 1).layer_names == ["_default"] assert g.edge(0, 2).layer_names == ["awesome layer"] - error_msg = ("The layer_name function is only available once an edge has been exploded via .explode_layers() or .explode(). " - "If you want to retrieve the layers for this edge you can use .layer_names") + error_msg = ( + "The layer_name function is only available once an edge has been exploded via .explode_layers() or .explode(). " + "If you want to retrieve the layers for this edge you can use .layer_names" + ) with pytest.raises(Exception) as e: g.edges.layer_name() assert str(e.value) == error_msg - assert list(g.edges.explode().layer_name) == ['_default', 'awesome layer'] - assert list(g.edges.explode_layers().layer_name) == ['_default', 'awesome layer'] + assert list(g.edges.explode().layer_name) == ["_default", "awesome layer"] + assert list(g.edges.explode_layers().layer_name) == ["_default", "awesome layer"] with pytest.raises(Exception) as e: g.edge(0, 2).layer_name() assert str(e.value) == error_msg - assert list(g.edge(0, 2).explode().layer_name) == ['awesome layer'] - assert list(g.edge(0, 2).explode_layers().layer_name) == ['awesome layer'] + assert list(g.edge(0, 2).explode().layer_name) == ["awesome layer"] + assert list(g.edge(0, 2).explode_layers().layer_name) == ["awesome layer"] with pytest.raises(Exception) as e: g.nodes.neighbours.edges.layer_name() assert str(e.value) == error_msg - assert [list(iterator) for iterator in g.nodes.neighbours.edges.explode().layer_name] == [ + assert [ + list(iterator) for iterator in g.nodes.neighbours.edges.explode().layer_name + ] == [ + ["_default", "awesome layer"], ["_default", "awesome layer"], ["_default", "awesome layer"], - ["_default", "awesome layer"] ] - assert [list(iterator) for iterator in g.nodes.neighbours.edges.explode_layers().layer_name] == [ + assert [ + list(iterator) + for iterator in g.nodes.neighbours.edges.explode_layers().layer_name + ] == [ + ["_default", "awesome layer"], ["_default", "awesome layer"], ["_default", "awesome layer"], - ["_default", "awesome layer"] ] + def test_time(): g = Graph() g.add_constant_properties({"name": "graph"}) @@ -1518,8 +1526,10 @@ def test_time(): g.add_edge(0, 0, 2) g.add_edge(1, 0, 2) - error_msg = ("The time function is only available once an edge has been exploded via .explode(). " - "You may want to retrieve the history for this edge via .history(), or the earliest/latest time via earliest_time or latest_time") + error_msg = ( + "The time function is only available once an edge has been exploded via .explode(). " + "You may want to retrieve the history for this edge via .history(), or the earliest/latest time via earliest_time or latest_time" + ) with pytest.raises(Exception) as e: g.edges.time() assert str(e.value) == error_msg @@ -1536,7 +1546,11 @@ def test_time(): g.nodes.neighbours.edges.time() assert str(e.value) == error_msg - assert [list(iterator) for iterator in g.nodes.neighbours.edges.explode().time] == [[0, 0, 1], [0, 0, 1], [0, 0, 1]] + assert [list(iterator) for iterator in g.nodes.neighbours.edges.explode().time] == [ + [0, 0, 1], + [0, 0, 1], + [0, 0, 1], + ] def test_window_size(): @@ -2066,8 +2080,8 @@ def test_type_filter(): g.add_node(1, 3, node_type="timer") g.add_node(1, 4, node_type="wallet") - assert [node.name for node in g.nodes.type_filter(["wallet"])] == ['1', '4'] - assert g.subgraph_node_types(["timer"]).nodes.name.collect() == ['2', '3'] + assert [node.name for node in g.nodes.type_filter(["wallet"])] == ["1", "4"] + assert g.subgraph_node_types(["timer"]).nodes.name.collect() == ["2", "3"] g = PersistentGraph() g.add_node(1, 1, node_type="wallet") @@ -2075,16 +2089,16 @@ def test_type_filter(): g.add_node(3, 3, node_type="timer") g.add_node(4, 4, node_type="wallet") - assert [node.name for node in g.nodes.type_filter(["wallet"])] == ['1', '4'] - assert g.subgraph_node_types(["timer"]).nodes.name.collect() == ['2', '3'] + assert [node.name for node in g.nodes.type_filter(["wallet"])] == ["1", "4"] + assert g.subgraph_node_types(["timer"]).nodes.name.collect() == ["2", "3"] subgraph = g.subgraph([1, 2, 3]) - assert [node.name for node in subgraph.nodes.type_filter(["wallet"])] == ['1'] - assert subgraph.subgraph_node_types(["timer"]).nodes.name.collect() == ['2', '3'] + assert [node.name for node in subgraph.nodes.type_filter(["wallet"])] == ["1"] + assert subgraph.subgraph_node_types(["timer"]).nodes.name.collect() == ["2", "3"] w = g.window(1, 3) - assert [node.name for node in w.nodes.type_filter(["wallet"])] == ['1'] - assert w.subgraph_node_types(["timer"]).nodes.name.collect() == ['2', '3'] + assert [node.name for node in w.nodes.type_filter(["wallet"])] == ["1"] + assert w.subgraph_node_types(["timer"]).nodes.name.collect() == ["2", "3"] g = Graph() g.add_node(1, 1, node_type="wallet") @@ -2095,8 +2109,8 @@ def test_type_filter(): g.add_edge(2, 2, 3, layer="layer1") g.add_edge(3, 2, 4, layer="layer2") layer = g.layers(["layer1"]) - assert [node.name for node in layer.nodes.type_filter(["wallet"])] == ['1'] - assert layer.subgraph_node_types(["timer"]).nodes.name.collect() == ['2', '3'] + assert [node.name for node in layer.nodes.type_filter(["wallet"])] == ["1"] + assert layer.subgraph_node_types(["timer"]).nodes.name.collect() == ["2", "3"] g = Graph() g.add_node(1, 1, node_type="a") @@ -2116,40 +2130,61 @@ def test_type_filter(): g.add_edge(2, 5, 6, layer="a") g.add_edge(2, 3, 6, layer="a") - assert g.nodes.type_filter([""]).name.collect() == ['7', '8', '9'] + assert g.nodes.type_filter([""]).name.collect() == ["7", "8", "9"] - assert g.nodes.type_filter(["a"]).name.collect() == ['1', '4'] - assert g.nodes.type_filter(["a", "c"]).name.collect() == ['1', '4', '5'] - assert g.nodes.type_filter(["a"]).neighbours.name.collect() == [['2'], ['2', '5']] + assert g.nodes.type_filter(["a"]).name.collect() == ["1", "4"] + assert g.nodes.type_filter(["a", "c"]).name.collect() == ["1", "4", "5"] + assert g.nodes.type_filter(["a"]).neighbours.name.collect() == [["2"], ["2", "5"]] assert g.nodes.degree().collect() == [1, 3, 2, 2, 2, 2, 0, 0, 0] - assert g.nodes.type_filter(['a']).degree().collect() == [1, 2] - assert g.nodes.type_filter(['d']).degree().collect() == [] + assert g.nodes.type_filter(["a"]).degree().collect() == [1, 2] + assert g.nodes.type_filter(["d"]).degree().collect() == [] assert g.nodes.type_filter([]).name.collect() == [] assert len(g.nodes) == 9 - assert len(g.nodes.type_filter(['b'])) == 2 - assert len(g.nodes.type_filter(['d'])) == 0 - - assert g.nodes.type_filter(['d']).neighbours.name.collect() == [] - assert g.nodes.type_filter(['a']).neighbours.name.collect() == [['2'], ['2', '5']] - assert g.nodes.type_filter(['a', 'c']).neighbours.name.collect() == [['2'], ['2', '5'], ['4', '6']] - - assert g.nodes.type_filter(['a']).neighbours.type_filter(['c']).name.collect() == [[], ['5']] - assert g.nodes.type_filter(['a']).neighbours.type_filter([]).name.collect() == [[], []] - assert g.nodes.type_filter(['a']).neighbours.type_filter(['b', 'c']).name.collect() == [['2'], ['2', '5']] - assert g.nodes.type_filter(['a']).neighbours.type_filter(['d']).name.collect() == [[], []] - assert g.nodes.type_filter(['a']).neighbours.neighbours.name.collect() == [['1', '3', '4'], ['1', '3', '4', '4', '6']] - assert g.nodes.type_filter(['a']).neighbours.type_filter(['c']).neighbours.name.collect() == [[], ['4', '6']] - assert g.nodes.type_filter(['a']).neighbours.type_filter(['d']).neighbours.name.collect() == [[], []] + assert len(g.nodes.type_filter(["b"])) == 2 + assert len(g.nodes.type_filter(["d"])) == 0 + + assert g.nodes.type_filter(["d"]).neighbours.name.collect() == [] + assert g.nodes.type_filter(["a"]).neighbours.name.collect() == [["2"], ["2", "5"]] + assert g.nodes.type_filter(["a", "c"]).neighbours.name.collect() == [ + ["2"], + ["2", "5"], + ["4", "6"], + ] - assert g.node('2').neighbours.type_filter(['b']).name.collect() == ['3'] - assert g.node('2').neighbours.type_filter(['d']).name.collect() == [] - assert g.node('2').neighbours.type_filter([]).name.collect() == [] - assert g.node('2').neighbours.type_filter(['c', 'a']).name.collect() == ['1', '4'] - assert g.node('2').neighbours.type_filter(['c']).neighbours.name.collect() == [] - assert g.node('2').neighbours.neighbours.name.collect() == ['2', '2', '6', '2', '5'] + assert g.nodes.type_filter(["a"]).neighbours.type_filter(["c"]).name.collect() == [ + [], + ["5"], + ] + assert g.nodes.type_filter(["a"]).neighbours.type_filter([]).name.collect() == [ + [], + [], + ] + assert g.nodes.type_filter(["a"]).neighbours.type_filter( + ["b", "c"] + ).name.collect() == [["2"], ["2", "5"]] + assert g.nodes.type_filter(["a"]).neighbours.type_filter(["d"]).name.collect() == [ + [], + [], + ] + assert g.nodes.type_filter(["a"]).neighbours.neighbours.name.collect() == [ + ["1", "3", "4"], + ["1", "3", "4", "4", "6"], + ] + assert g.nodes.type_filter(["a"]).neighbours.type_filter( + ["c"] + ).neighbours.name.collect() == [[], ["4", "6"]] + assert g.nodes.type_filter(["a"]).neighbours.type_filter( + ["d"] + ).neighbours.name.collect() == [[], []] + assert g.node("2").neighbours.type_filter(["b"]).name.collect() == ["3"] + assert g.node("2").neighbours.type_filter(["d"]).name.collect() == [] + assert g.node("2").neighbours.type_filter([]).name.collect() == [] + assert g.node("2").neighbours.type_filter(["c", "a"]).name.collect() == ["1", "4"] + assert g.node("2").neighbours.type_filter(["c"]).neighbours.name.collect() == [] + assert g.node("2").neighbours.neighbours.name.collect() == ["2", "2", "6", "2", "5"] def test_time_exploded_edges(): @@ -2273,23 +2308,45 @@ def test_unique_temporal_properties(): g.add_property(3, {"name": "tarzan2"}) g.add_property(2, {"salary": "1000"}) g.add_constant_properties({"type": "character"}) - g.add_edge(1,1,2,properties={"status":"open"}) - g.add_edge(2,1,2,properties={"status":"open"}) - g.add_edge(3,1,2,properties={"status":"review"}) - g.add_edge(4,1,2,properties={"status":"open"}) - g.add_edge(5,1,2,properties={"status":"in-progress"}) - g.add_edge(10,1,2,properties={"status":"in-progress"}) - g.add_edge(6,1,2) + g.add_edge(1, 1, 2, properties={"status": "open"}) + g.add_edge(2, 1, 2, properties={"status": "open"}) + g.add_edge(3, 1, 2, properties={"status": "review"}) + g.add_edge(4, 1, 2, properties={"status": "open"}) + g.add_edge(5, 1, 2, properties={"status": "in-progress"}) + g.add_edge(10, 1, 2, properties={"status": "in-progress"}) + g.add_edge(6, 1, 2) g.add_node(1, 3, {"name": "avatar1"}) g.add_node(2, 3, {"name": "avatar2"}) g.add_node(3, 3, {"name": "avatar2"}) - assert g.edge(1,2).properties.temporal.get('status').ordered_dedupe(True) == [(2, "open"), (3, "review"), (4, "open"), (10, "in-progress")] - assert g.edge(1,2).properties.temporal.get('status').ordered_dedupe(False) == [(1, "open"), (3, "review"), (4, "open"), (5, "in-progress")] - assert g.properties.temporal.get('name').ordered_dedupe(True) == [(1, "tarzan"), (3, "tarzan2")] - assert g.properties.temporal.get('name').ordered_dedupe(False) == [(1, "tarzan"), (2, "tarzan2")] - assert g.node(3).properties.temporal.get('name').ordered_dedupe(True) == [(1, "avatar1"), (3, "avatar2")] - assert g.node(3).properties.temporal.get('name').ordered_dedupe(False) == [(1, "avatar1"), (2, "avatar2")] + assert g.edge(1, 2).properties.temporal.get("status").ordered_dedupe(True) == [ + (2, "open"), + (3, "review"), + (4, "open"), + (10, "in-progress"), + ] + assert g.edge(1, 2).properties.temporal.get("status").ordered_dedupe(False) == [ + (1, "open"), + (3, "review"), + (4, "open"), + (5, "in-progress"), + ] + assert g.properties.temporal.get("name").ordered_dedupe(True) == [ + (1, "tarzan"), + (3, "tarzan2"), + ] + assert g.properties.temporal.get("name").ordered_dedupe(False) == [ + (1, "tarzan"), + (2, "tarzan2"), + ] + assert g.node(3).properties.temporal.get("name").ordered_dedupe(True) == [ + (1, "avatar1"), + (3, "avatar2"), + ] + assert g.node(3).properties.temporal.get("name").ordered_dedupe(False) == [ + (1, "avatar1"), + (2, "avatar2"), + ] g.add_node(4, 3, {"i64": 1}) g.add_node(5, 3, {"i64": 1}) @@ -2310,21 +2367,56 @@ def test_unique_temporal_properties(): g.add_node(18, 3, {"map": {"name": "bob", "value list": [1, 2, 3]}}) g.add_node(19, 3, {"map": {"name": "bob", "value list": [1, 2]}}) - assert list(g.edge(1,2).properties.temporal.get('status')) == [(1, 'open'), (2, 'open'), (3, 'review'), (4, 'open'), (5, 'in-progress'), (10, 'in-progress')] - assert sorted(g.edge(1,2).properties.temporal.get('status').unique()) == ['in-progress', 'open', 'review'] - assert list(g.properties.temporal.get('name')) == [(1, 'tarzan'), (2, 'tarzan2'), (3, 'tarzan2')] - assert sorted(g.properties.temporal.get('name').unique()) == ['tarzan', 'tarzan2'] - assert list(g.node(3).properties.temporal.get('name')) == [(1, 'avatar1'), (2, 'avatar2'), (3, 'avatar2')] - assert sorted(g.node(3).properties.temporal.get('name').unique()) == ['avatar1', 'avatar2'] - assert sorted(g.node(3).properties.temporal.get('i64').unique()) == [1, 5] - assert sorted(g.node(3).properties.temporal.get('f64').unique()) == [1.2, 1.3] - assert sorted(g.node(3).properties.temporal.get('bool').unique()) == [False, True] - assert sorted(g.node(3).properties.temporal.get('list').unique()) == [[1, 2, 3], [2, 3]] - assert sorted(g.node(3).properties.temporal.get('date').unique()) == [datetime_obj, datetime_obj2] - actual_list = g.node(3).properties.temporal.get('map').unique() - expected_list = [{"name": "bob", "value list": [1, 2]}, {"name": "bob", "value list": [1, 2, 3]}] - sorted_actual_list = sorted(actual_list, key=lambda d: (d["name"], tuple(d["value list"]))) - sorted_expected_list = sorted(expected_list, key=lambda d: (d["name"], tuple(d["value list"]))) + assert list(g.edge(1, 2).properties.temporal.get("status")) == [ + (1, "open"), + (2, "open"), + (3, "review"), + (4, "open"), + (5, "in-progress"), + (10, "in-progress"), + ] + assert sorted(g.edge(1, 2).properties.temporal.get("status").unique()) == [ + "in-progress", + "open", + "review", + ] + assert list(g.properties.temporal.get("name")) == [ + (1, "tarzan"), + (2, "tarzan2"), + (3, "tarzan2"), + ] + assert sorted(g.properties.temporal.get("name").unique()) == ["tarzan", "tarzan2"] + assert list(g.node(3).properties.temporal.get("name")) == [ + (1, "avatar1"), + (2, "avatar2"), + (3, "avatar2"), + ] + assert sorted(g.node(3).properties.temporal.get("name").unique()) == [ + "avatar1", + "avatar2", + ] + assert sorted(g.node(3).properties.temporal.get("i64").unique()) == [1, 5] + assert sorted(g.node(3).properties.temporal.get("f64").unique()) == [1.2, 1.3] + assert sorted(g.node(3).properties.temporal.get("bool").unique()) == [False, True] + assert sorted(g.node(3).properties.temporal.get("list").unique()) == [ + [1, 2, 3], + [2, 3], + ] + assert sorted(g.node(3).properties.temporal.get("date").unique()) == [ + datetime_obj, + datetime_obj2, + ] + actual_list = g.node(3).properties.temporal.get("map").unique() + expected_list = [ + {"name": "bob", "value list": [1, 2]}, + {"name": "bob", "value list": [1, 2, 3]}, + ] + sorted_actual_list = sorted( + actual_list, key=lambda d: (d["name"], tuple(d["value list"])) + ) + sorted_expected_list = sorted( + expected_list, key=lambda d: (d["name"], tuple(d["value list"])) + ) assert sorted_actual_list == sorted_expected_list g1 = Graph() g1.add_constant_properties({"type": "a"}) @@ -2339,14 +2431,24 @@ def test_unique_temporal_properties(): g3.add_node(1, "shivam") g.add_node(7, 3, {"graph": g3}) - actual_list = g.node(3).properties.temporal.get('graph').unique() + actual_list = g.node(3).properties.temporal.get("graph").unique() expected_list = [g1, g3] - sorted_actual_list = sorted(actual_list, key=lambda g: g.properties.constant.get('type')) - sorted_expected_list = sorted(expected_list, key=lambda g: g.properties.constant.get('type')) + sorted_actual_list = sorted( + actual_list, key=lambda g: g.properties.constant.get("type") + ) + sorted_expected_list = sorted( + expected_list, key=lambda g: g.properties.constant.get("type") + ) assert sorted_actual_list == sorted_expected_list - assert g.node(3).properties.temporal.get('i64').ordered_dedupe(True) == [(5, 1), (6, 5)] - assert g.node(3).properties.temporal.get('i64').ordered_dedupe(False) == [(4, 1), (6, 5)] + assert g.node(3).properties.temporal.get("i64").ordered_dedupe(True) == [ + (5, 1), + (6, 5), + ] + assert g.node(3).properties.temporal.get("i64").ordered_dedupe(False) == [ + (4, 1), + (6, 5), + ] def test_fuzzy_search(): diff --git a/raphtory-arrow-private b/raphtory-arrow-private deleted file mode 160000 index 78f231a104..0000000000 --- a/raphtory-arrow-private +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 78f231a10456a514bd56ed5912a18f431f245b87 diff --git a/raphtory-arrow/Cargo.toml b/raphtory-arrow/Cargo.toml deleted file mode 100644 index 3d9a59067a..0000000000 --- a/raphtory-arrow/Cargo.toml +++ /dev/null @@ -1,3 +0,0 @@ -[package] -name = "raphtory-arrow" -version = "0.8.1" \ No newline at end of file diff --git a/raphtory-benchmark/Cargo.toml b/raphtory-benchmark/Cargo.toml index 131ce09534..09758a8ee7 100644 --- a/raphtory-benchmark/Cargo.toml +++ b/raphtory-benchmark/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" criterion = { workspace = true } raphtory = { path = "../raphtory", features = ["io"] } raphtory-graphql = { path = "../raphtory-graphql", version = "0.8.1" } -raphtory-arrow.workspace = true +pometry-storage.workspace = true sorted_vector_map = { workspace = true } rand = { workspace = true } rayon = { workspace = true } diff --git a/raphtory-benchmark/benches/arrow_algobench.rs b/raphtory-benchmark/benches/arrow_algobench.rs index a2c1e2a0c0..3f9b7439c9 100644 --- a/raphtory-benchmark/benches/arrow_algobench.rs +++ b/raphtory-benchmark/benches/arrow_algobench.rs @@ -39,7 +39,7 @@ pub fn local_triangle_count_analysis(c: &mut Criterion) { bench(&mut group, "local_triangle_count", None, |b| { let g = raphtory::graph_loader::example::lotr_graph::lotr_graph(); let test_dir = TempDir::new().unwrap(); - let g = g.persist_as_arrow(test_dir.path()).unwrap(); + let g = g.persist_as_disk_graph(test_dir.path()).unwrap(); let windowed_graph = g.window(i64::MIN, i64::MAX); b.iter(|| { @@ -91,7 +91,7 @@ pub fn local_clustering_coefficient_analysis(c: &mut Criterion) { } let test_dir = TempDir::new().unwrap(); - let g = g.persist_as_arrow(test_dir.path()).unwrap(); + let g = g.persist_as_disk_graph(test_dir.path()).unwrap(); let windowed_graph = g.window(0, 5); b.iter(|| local_clustering_coefficient(&windowed_graph, 1)) @@ -108,7 +108,7 @@ pub fn graphgen_large_clustering_coeff(c: &mut Criterion) { random_attachment(&graph, 500000, 4, Some(seed)); let test_dir = TempDir::new().unwrap(); - let graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + let graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); group.sampling_mode(SamplingMode::Flat); group.measurement_time(std::time::Duration::from_secs(60)); @@ -134,7 +134,7 @@ pub fn graphgen_large_pagerank(c: &mut Criterion) { random_attachment(&graph, 500000, 4, Some(seed)); let test_dir = TempDir::new().unwrap(); - let graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + let graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); group.sampling_mode(SamplingMode::Flat); group.measurement_time(std::time::Duration::from_secs(20)); group.sample_size(10); @@ -158,7 +158,7 @@ pub fn graphgen_large_concomp(c: &mut Criterion) { let seed: [u8; 32] = [1; 32]; random_attachment(&graph, 500000, 4, Some(seed)); let test_dir = TempDir::new().unwrap(); - let graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + let graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); group.sampling_mode(SamplingMode::Flat); group.measurement_time(std::time::Duration::from_secs(60)); diff --git a/raphtory-benchmark/bin/generate_graph.py b/raphtory-benchmark/bin/generate_graph.py index ba0b479c41..ce7e379410 100644 --- a/raphtory-benchmark/bin/generate_graph.py +++ b/raphtory-benchmark/bin/generate_graph.py @@ -14,4 +14,4 @@ logging.info("Saving to ./reddit_graph.bincode") g.save_to_file("reddit_graph.bincode") -logging.info("Complete") \ No newline at end of file +logging.info("Complete") diff --git a/raphtory-benchmark/bin/run_graphql.py b/raphtory-benchmark/bin/run_graphql.py index dc48bf532d..c22f7acad0 100644 --- a/raphtory-benchmark/bin/run_graphql.py +++ b/raphtory-benchmark/bin/run_graphql.py @@ -9,4 +9,4 @@ def load_gql_server(): RaphtoryServer(graph_dir="./graphs/").run() -load_gql_server() \ No newline at end of file +load_gql_server() diff --git a/raphtory-benchmark/python/kuzu_bench.py b/raphtory-benchmark/python/kuzu_bench.py index db54d98d7c..1304173be2 100755 --- a/raphtory-benchmark/python/kuzu_bench.py +++ b/raphtory-benchmark/python/kuzu_bench.py @@ -48,9 +48,9 @@ def degree(self): def out_neighbours(self): self.conn.set_query_timeout(600000) # 600 seconds - #query = "MATCH (u:User) RETURN MAX(u.id) AS max_user_id" - #res = self.run_query(query) - max_user_id = 1632803 # res.get_as_df().iloc[0]['max_user_id'] + # query = "MATCH (u:User) RETURN MAX(u.id) AS max_user_id" + # res = self.run_query(query) + max_user_id = 1632803 # res.get_as_df().iloc[0]['max_user_id'] batch_size = 100000 for i in range(0, max_user_id, batch_size): start_id = i + 1 @@ -58,9 +58,9 @@ def out_neighbours(self): query = f"MATCH (u:User) WHERE u.id >= {start_id} AND u.id < {end_id} WITH u MATCH (u)-[:Follows]->(n:User) RETURN u.id, COLLECT(n.id) AS out_neighbours" res = self.run_query(query) df = res.get_as_df() - #query = "MATCH (u:User)-[:Follows]->(n:User) RETURN u.id, COUNT(n.id) AS out_neighbours" - #res = self.run_query(query) - #df = res.get_as_df() + # query = "MATCH (u:User)-[:Follows]->(n:User) RETURN u.id, COUNT(n.id) AS out_neighbours" + # res = self.run_query(query) + # df = res.get_as_df() def page_rank(self): print("NOT IMPLEMENTED IN KUZU") diff --git a/raphtory-benchmark/python/neo4j_bench.py b/raphtory-benchmark/python/neo4j_bench.py index e2c55d4c40..b4f68b2a80 100755 --- a/raphtory-benchmark/python/neo4j_bench.py +++ b/raphtory-benchmark/python/neo4j_bench.py @@ -47,7 +47,9 @@ def get_out_neighbors(tx): def run_pagerank(tx): - result = tx.run("""CALL gds.pageRank.stream("social") YIELD nodeId, score RETURN nodeId, score""") + result = tx.run( + """CALL gds.pageRank.stream("social") YIELD nodeId, score RETURN nodeId, score""" + ) return list(result) @@ -68,7 +70,9 @@ def execute_bash_command(command, background=False, timeout=60): ) return try: - child = pexpect.spawn("/bin/bash", ["-c", command + " 2>&1"], timeout=timeout) # Redirect stderr to stdout + child = pexpect.spawn( + "/bin/bash", ["-c", command + " 2>&1"], timeout=timeout + ) # Redirect stderr to stdout child.expect(pexpect.EOF) output = child.before.decode("utf-8") except pexpect.TIMEOUT: @@ -98,7 +102,7 @@ def modify_data(): write_array_to_csv( [["node:START_ID", "node:END_ID", ":TYPE"]], file_dir + "simple-relationships-headers-neo4j.csv", - ) + ) print("Generating node data") df = pd.read_csv(file_dir + "simple-profiles.csv", sep="\t", header=None) @@ -115,7 +119,7 @@ def modify_data(): sep="\t", index=None, header=None, - ) + ) print("Done") @@ -154,7 +158,7 @@ def start_docker(self, **kwargs): # '/bin/bash -c "neo4j start"', # '/bin/bash -c "sleep 15"', #'/bin/bash -c "cd /var/lib/neo4j/import/data2/; python3 benchmark_driver.py --no-docker --bench neo"', - 'tail -f /dev/null', + "tail -f /dev/null", ] # image_path = 'DockerFiles/pyneo' image_path ports code, contents = super().start_docker( @@ -223,4 +227,4 @@ def page_rank(self): return self.execute_read(run_pagerank) def connected_components(self): - return self.execute_read(run_connected_components) \ No newline at end of file + return self.execute_read(run_connected_components) diff --git a/raphtory-benchmark/python/raphtory_bench.py b/raphtory-benchmark/python/raphtory_bench.py index 95d33a7fc8..825c792d1d 100755 --- a/raphtory-benchmark/python/raphtory_bench.py +++ b/raphtory-benchmark/python/raphtory_bench.py @@ -42,8 +42,10 @@ def __init__(self): def setup(self): # Load edges - df = pd.read_csv(simple_relationship_file, delimiter="\t", header=None, names=["src", "dst"]) - df['time'] = 1 + df = pd.read_csv( + simple_relationship_file, delimiter="\t", header=None, names=["src", "dst"] + ) + df["time"] = 1 self.graph = raphtory.Graph.load_from_pandas(df, "src", "dst", "time") def degree(self): diff --git a/raphtory-cypher/Cargo.toml b/raphtory-cypher/Cargo.toml index e142fa9719..7eec1c729f 100644 --- a/raphtory-cypher/Cargo.toml +++ b/raphtory-cypher/Cargo.toml @@ -15,7 +15,7 @@ edition.workspace = true [dependencies] raphtory = { path = "../raphtory" } -raphtory-arrow = { workspace = true, optional = true } +pometry-storage = { workspace = true, optional = true } arrow.workspace = true arrow-buffer.workspace = true arrow-schema.workspace = true @@ -47,4 +47,4 @@ tokio.workspace = true clap.workspace = true [features] -arrow = ["raphtory/arrow", "raphtory-arrow"] +storage = ["raphtory/storage", "pometry-storage"] diff --git a/raphtory-cypher/examples/raphtory_cypher.rs b/raphtory-cypher/examples/raphtory_cypher.rs index 0af6f5d5be..e16c304d21 100644 --- a/raphtory-cypher/examples/raphtory_cypher.rs +++ b/raphtory-cypher/examples/raphtory_cypher.rs @@ -1,23 +1,23 @@ -#[cfg(feature = "arrow")] -pub use cyper_arrow::*; +#[cfg(feature = "storage")] +pub use cypher::*; -#[cfg(not(feature = "arrow"))] +#[cfg(not(feature = "storage"))] fn main() {} -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] #[tokio::main] async fn main() { - cyper_arrow::main().await; + cypher::main().await; } -#[cfg(feature = "arrow")] -mod cyper_arrow { +#[cfg(feature = "storage")] +mod cypher { use std::{error::Error, str::FromStr}; use arrow::util::pretty::print_batches; use clap::Parser; use futures::{stream, StreamExt}; - use raphtory::arrow::graph_impl::{ArrowGraph, ParquetLayerCols}; + use raphtory::disk_graph::graph_impl::{DiskGraph, ParquetLayerCols}; use raphtory_cypher::{run_cypher, run_cypher_to_streams, run_sql}; use serde::{de::DeserializeOwned, Deserialize}; @@ -134,7 +134,7 @@ mod cyper_arrow { match args { Args::Query(args) => { let graph = - ArrowGraph::load_from_dir(&args.graph_dir).expect("Failed to load graph"); + DiskGraph::load_from_dir(&args.graph_dir).expect("Failed to load graph"); let now = std::time::Instant::now(); @@ -183,7 +183,7 @@ mod cyper_arrow { } }) .collect(); - ArrowGraph::load_from_parquets( + DiskGraph::load_from_parquets( args.graph_dir.as_str(), layer_parquet_cols, args.node_props.as_deref(), diff --git a/raphtory-cypher/src/executor/table_provider/edge.rs b/raphtory-cypher/src/executor/table_provider/edge.rs index b6bf6eadf9..fe54dfe23e 100644 --- a/raphtory-cypher/src/executor/table_provider/edge.rs +++ b/raphtory-cypher/src/executor/table_provider/edge.rs @@ -27,8 +27,8 @@ use datafusion::{ physical_planner::create_physical_sort_expr, }; use futures::Stream; -use raphtory::arrow::graph_impl::ArrowGraph; -use raphtory_arrow::prelude::*; +use pometry_storage::prelude::*; +use raphtory::disk_graph::graph_impl::DiskGraph; use crate::executor::{arrow2_to_arrow_buf, ExecError}; @@ -37,7 +37,7 @@ use crate::executor::{arrow2_to_arrow_buf, ExecError}; pub struct EdgeListTableProvider { layer_id: usize, layer_name: String, - graph: ArrowGraph, + graph: DiskGraph, nested_schema: SchemaRef, num_partitions: usize, row_count: usize, @@ -45,7 +45,7 @@ pub struct EdgeListTableProvider { } impl EdgeListTableProvider { - pub fn new(layer_name: &str, g: ArrowGraph) -> Result { + pub fn new(layer_name: &str, g: DiskGraph) -> Result { let graph = g.as_ref(); let layer_id = graph .find_layer_id(layer_name) @@ -97,7 +97,7 @@ impl EdgeListTableProvider { } } -fn lift_nested_arrow_schema(graph: &ArrowGraph, layer_id: usize) -> Result, ExecError> { +fn lift_nested_arrow_schema(graph: &DiskGraph, layer_id: usize) -> Result, ExecError> { let arrow2_fields = graph.as_ref().layer(layer_id).edges_data_type(); let a2_dt = crate::arrow2::datatypes::ArrowDataType::Struct(arrow2_fields.clone()); let a_dt: DataType = a2_dt.into(); @@ -165,7 +165,7 @@ impl TableProvider for EdgeListTableProvider { struct EdgeListExecPlan { layer_id: usize, layer_name: String, - graph: ArrowGraph, + graph: DiskGraph, schema: SchemaRef, num_partitions: usize, row_count: usize, @@ -175,7 +175,7 @@ struct EdgeListExecPlan { } fn produce_record_batch( - graph: ArrowGraph, + graph: DiskGraph, schema: SchemaRef, layer_id: usize, start_offset: usize, @@ -223,7 +223,7 @@ fn produce_record_batch( let mut layer_id_builder = Vec::with_capacity(time_values_chunks.len()); // take every chunk here and surface the primitive arrays - // convert from arrow2 to arrow-rs then to polars + // convert from arrow2 to disk_graph-rs then to polars for (i, ((src, dst), layer_id)) in srcs .iter_chunks() .zip(dsts.iter_chunks()) @@ -444,7 +444,7 @@ mod test { (3, 4, 7, 6.), (4, 5, 9, 7.), ]; - let graph = ArrowGraph::make_simple_graph(graph_dir, &edges, 10, 10); + let graph = DiskGraph::make_simple_graph(graph_dir, &edges, 10, 10); ctx.register_table( "graph", Arc::new(EdgeListTableProvider::new("_default", graph).unwrap()), diff --git a/raphtory-cypher/src/executor/table_provider/node.rs b/raphtory-cypher/src/executor/table_provider/node.rs index e0d9deefb4..fbca415c88 100644 --- a/raphtory-cypher/src/executor/table_provider/node.rs +++ b/raphtory-cypher/src/executor/table_provider/node.rs @@ -19,11 +19,11 @@ use datafusion::{ }, }; use futures::Stream; -use raphtory_arrow::properties::Properties; +use pometry_storage::properties::Properties; use raphtory::{ - arrow::{graph_impl::ArrowGraph, prelude::*}, core::entities::VID, + disk_graph::{graph_impl::DiskGraph, prelude::*}, }; use crate::{ @@ -32,14 +32,14 @@ use crate::{ }; pub struct NodeTableProvider { - graph: ArrowGraph, + graph: DiskGraph, schema: SchemaRef, num_partitions: usize, chunk_size: usize, } impl NodeTableProvider { - pub fn new(g: ArrowGraph) -> Result { + pub fn new(g: DiskGraph) -> Result { let graph = g.as_ref(); let (num_partitions, chunk_size) = graph .node_properties() @@ -132,7 +132,7 @@ impl TableProvider for NodeTableProvider { } async fn produce_record_batch( - g: ArrowGraph, + g: DiskGraph, schema: SchemaRef, chunk_id: usize, chunk_size: usize, @@ -182,7 +182,7 @@ async fn produce_record_batch( } struct NodeScanExecPlan { - graph: ArrowGraph, + graph: DiskGraph, schema: SchemaRef, num_partitions: usize, chunk_size: usize, diff --git a/raphtory-cypher/src/hop/execution.rs b/raphtory-cypher/src/hop/execution.rs index 46c921a7e1..2a49112ddf 100644 --- a/raphtory-cypher/src/hop/execution.rs +++ b/raphtory-cypher/src/hop/execution.rs @@ -9,7 +9,7 @@ use std::{ }; use crate::arrow2::{offset::Offset, types::NativeType}; -// use arrow::compute::take_record_batch; +// use disk_graph::compute::take_record_batch; use arrow_array::{ builder::{ make_builder, ArrayBuilder, Float32Builder, Float64Builder, GenericStringBuilder, @@ -38,14 +38,14 @@ use datafusion::{ use datafusion::physical_expr::Partitioning; use futures::{Stream, StreamExt}; +use pometry_storage::graph_fragment::TempColGraphFragment; use raphtory::{ - arrow::{ - graph_impl::ArrowGraph, + core::{entities::VID, Direction}, + disk_graph::{ + graph_impl::DiskGraph, prelude::{ArrayOps, BaseArrayOps, PrimitiveCol}, }, - core::{entities::VID, Direction}, }; -use raphtory_arrow::graph_fragment::TempColGraphFragment; use crate::take_record_batch; @@ -53,7 +53,7 @@ use super::operator::HopPlan; #[derive(Debug)] pub struct HopExec { - graph: ArrowGraph, + graph: DiskGraph, dir: Direction, input_col: usize, input: Arc, @@ -176,7 +176,7 @@ impl ExecutionPlan for HopExec { } pub(crate) struct HopStream { input: SendableRecordBatchStream, - graph: ArrowGraph, + graph: DiskGraph, dir: Direction, input_col: usize, batch_size: usize, @@ -190,7 +190,7 @@ pub(crate) struct HopStream { impl HopStream { fn new( input: SendableRecordBatchStream, - graph: ArrowGraph, + graph: DiskGraph, dir: Direction, input_col: usize, batch_size: usize, @@ -342,7 +342,7 @@ fn produce_next_record( prev_node: &mut Option, input_col: usize, - graph: &ArrowGraph, + graph: &DiskGraph, layers: Vec, output_schema: SchemaRef, right_schema: DFSchemaRef, @@ -663,7 +663,7 @@ mod test { edges: &[(u64, u64, i64, f64)], ) { let graph_dir = tempdir().unwrap(); - let graph = ArrowGraph::make_simple_graph(graph_dir, edges, chunk_size, t_props_chunk_size); + let graph = DiskGraph::make_simple_graph(graph_dir, edges, chunk_size, t_props_chunk_size); let query = "MATCH (a)-[e1]->(b)-[e2]->(c) RETURN count(*)"; let df = run_cypher(query, &graph, false).await.unwrap(); @@ -689,8 +689,7 @@ mod test { output_range: Range, ) { let graph_dir = tempdir().unwrap(); - let graph = - ArrowGraph::make_simple_graph(graph_dir, &EDGES, chunk_size, t_props_chunk_size); + let graph = DiskGraph::make_simple_graph(graph_dir, &EDGES, chunk_size, t_props_chunk_size); let schema = make_input_schema(); let table_schema: DFSchema = schema.clone().to_dfschema().unwrap(); @@ -792,7 +791,7 @@ mod test { fn make_hop_stream( batch_size: usize, - graph: ArrowGraph, + graph: DiskGraph, table_schema: DFSchema, output_schema: Arc, input: RecordBatchStreamAdapter< diff --git a/raphtory-cypher/src/hop/operator.rs b/raphtory-cypher/src/hop/operator.rs index 8aa5134b63..c274cf56e9 100644 --- a/raphtory-cypher/src/hop/operator.rs +++ b/raphtory-cypher/src/hop/operator.rs @@ -5,7 +5,7 @@ use datafusion::{ logical_expr::{Expr, LogicalPlan, TableScan, UserDefinedLogicalNodeCore}, }; -use raphtory::{arrow::graph_impl::ArrowGraph, core::Direction}; +use raphtory::{core::Direction, disk_graph::graph_impl::DiskGraph}; #[derive(Debug, PartialEq, Hash, Eq)] pub struct HopPlan { @@ -22,17 +22,17 @@ pub struct HopPlan { #[derive(Clone)] struct GraphHolder { - pub graph: ArrowGraph, + pub graph: DiskGraph, } impl GraphHolder { - pub fn new(graph: ArrowGraph) -> Self { + pub fn new(graph: DiskGraph) -> Self { GraphHolder { graph } } } -impl AsRef for GraphHolder { - fn as_ref(&self) -> &ArrowGraph { +impl AsRef for GraphHolder { + fn as_ref(&self) -> &DiskGraph { &self.graph } } @@ -59,12 +59,12 @@ impl std::fmt::Debug for GraphHolder { } impl HopPlan { - pub fn graph(&self) -> ArrowGraph { + pub fn graph(&self) -> DiskGraph { self.graph.as_ref().clone() } pub fn from_table_scans( - graph: ArrowGraph, + graph: DiskGraph, dir: Direction, schema: DFSchemaRef, left: &LogicalPlan, diff --git a/raphtory-cypher/src/hop/rule.rs b/raphtory-cypher/src/hop/rule.rs index 17d8ac7acb..60efb7a94a 100644 --- a/raphtory-cypher/src/hop/rule.rs +++ b/raphtory-cypher/src/hop/rule.rs @@ -10,18 +10,18 @@ use datafusion::{ physical_plan::ExecutionPlan, physical_planner::{DefaultPhysicalPlanner, ExtensionPlanner, PhysicalPlanner}, }; -use raphtory::{arrow::graph_impl::ArrowGraph, core::Direction}; +use raphtory::{core::Direction, disk_graph::graph_impl::DiskGraph}; use crate::hop::operator::HopPlan; use super::execution::HopExec; pub struct HopRule { - pub graph: ArrowGraph, + pub graph: DiskGraph, } impl HopRule { - pub fn new(graph: ArrowGraph) -> Self { + pub fn new(graph: DiskGraph) -> Self { Self { graph } } } @@ -141,7 +141,7 @@ impl ExtensionPlanner for HopPlanner { #[cfg(test)] mod test { use arrow::util::pretty::print_batches; - use raphtory::arrow::graph_impl::ArrowGraph; + use raphtory::disk_graph::graph_impl::DiskGraph; use tempfile::tempdir; use crate::prepare_plan; @@ -150,7 +150,7 @@ mod test { async fn double_hop_edge_to_edge() { let graph_dir = tempdir().unwrap(); let edges = vec![(0u64, 1u64, 0i64, 2.)]; - let g = ArrowGraph::make_simple_graph(graph_dir, &edges, 10, 10); + let g = DiskGraph::make_simple_graph(graph_dir, &edges, 10, 10); let (_, plan) = prepare_plan("MATCH ()-[e1]->()-[e2]->() RETURN *", &g, true) .await .unwrap(); @@ -162,7 +162,7 @@ mod test { async fn double_hop_edge_to_edge_with_pushdown_filter_e2() { let graph_dir = tempdir().unwrap(); let edges = vec![(0u64, 1u64, 0i64, 2.)]; - let g = ArrowGraph::make_simple_graph(graph_dir, &edges, 10, 10); + let g = DiskGraph::make_simple_graph(graph_dir, &edges, 10, 10); let (_, plan) = prepare_plan( "MATCH ()-[e1]->()-[e2]->() WHERE e2.weight > 5 RETURN *", &g, @@ -178,7 +178,7 @@ mod test { async fn double_hop_edge_to_edge_with_pushdown_filter_e1() { let graph_dir = tempdir().unwrap(); let edges = vec![(0u64, 1u64, 0i64, 2.)]; - let g = ArrowGraph::make_simple_graph(graph_dir, &edges, 10, 10); + let g = DiskGraph::make_simple_graph(graph_dir, &edges, 10, 10); let (_, plan) = prepare_plan( "MATCH ()-[e1]->()-[e2]->() WHERE e1.weight > 5 RETURN *", &g, @@ -200,7 +200,7 @@ mod test { // +----+----------+-----+-----+----------+--------+----+----------+-----+-----+----------+--------+ let graph_dir = tempdir().unwrap(); let edges = vec![(0u64, 1u64, 0i64, 2.), (1, 2, 1, 3.), (2, 3, 2, 4.)]; - let g = ArrowGraph::make_simple_graph(graph_dir, &edges, 10, 10); + let g = DiskGraph::make_simple_graph(graph_dir, &edges, 10, 10); // let (ctx, plan) = prepare_plan("MATCH ()-[e1]->() RETURN *", &g) let (ctx, plan) = prepare_plan("MATCH ()-[e1]->()-[e2]->() RETURN *", &g, true) diff --git a/raphtory-cypher/src/lib.rs b/raphtory-cypher/src/lib.rs index e9d76c028c..99d0dcb72b 100644 --- a/raphtory-cypher/src/lib.rs +++ b/raphtory-cypher/src/lib.rs @@ -1,17 +1,17 @@ -#[cfg(feature = "arrow")] -pub use cypher_arrow::*; +#[cfg(feature = "storage")] +pub use cypher::*; -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] pub mod executor; -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] pub mod hop; -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] pub mod parser; -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] pub mod transpiler; -#[cfg(feature = "arrow")] -mod cypher_arrow { +#[cfg(feature = "storage")] +mod cypher { use arrow::compute::take; use std::sync::Arc; @@ -34,7 +34,7 @@ mod cypher_arrow { parser::ast::*, *, }; - use raphtory::arrow::graph_impl::ArrowGraph; + use raphtory::disk_graph::graph_impl::DiskGraph; use crate::{ executor::table_provider::node::NodeTableProvider, @@ -45,7 +45,7 @@ mod cypher_arrow { pub async fn run_cypher( query: &str, - g: &ArrowGraph, + g: &DiskGraph, enable_hop_optim: bool, ) -> Result { let (ctx, plan) = prepare_plan(query, g, enable_hop_optim).await?; @@ -55,7 +55,7 @@ mod cypher_arrow { pub async fn prepare_plan( query: &str, - g: &ArrowGraph, + g: &DiskGraph, enable_hop_optim: bool, ) -> Result<(SessionContext, LogicalPlan), ExecError> { // println!("Running query: {:?}", query); @@ -135,14 +135,14 @@ mod cypher_arrow { pub async fn run_cypher_to_streams( query: &str, - graph: &ArrowGraph, + graph: &DiskGraph, ) -> Result, ExecError> { let df = run_cypher(query, graph, true).await?; let stream = df.execute_stream_partitioned().await?; Ok(stream) } - pub async fn run_sql(query: &str, graph: &ArrowGraph) -> Result { + pub async fn run_sql(query: &str, graph: &DiskGraph) -> Result { let ctx = SessionContext::new(); for layer in graph.as_ref().layer_names() { @@ -185,7 +185,7 @@ mod cypher_arrow { use arrow_array::RecordBatch; use tempfile::tempdir; - use raphtory::{arrow::graph_impl::ArrowGraph, prelude::*}; + use raphtory::{disk_graph::graph_impl::DiskGraph, prelude::*}; use crate::run_cypher; @@ -245,7 +245,7 @@ mod cypher_arrow { #[tokio::test] async fn select_table() { let graph_dir = tempdir().unwrap(); - let graph = ArrowGraph::make_simple_graph(graph_dir, &EDGES, 3, 2); + let graph = DiskGraph::make_simple_graph(graph_dir, &EDGES, 3, 2); let df = run_cypher("match ()-[e]->() RETURN *", &graph, true) .await @@ -259,7 +259,7 @@ mod cypher_arrow { #[tokio::test] async fn select_table_order_by() { let graph_dir = tempdir().unwrap(); - let graph = ArrowGraph::make_simple_graph(graph_dir, &EDGES, 3, 2); + let graph = DiskGraph::make_simple_graph(graph_dir, &EDGES, 3, 2); let df = run_cypher("match ()-[e]->() RETURN * ORDER by e.weight", &graph, true) .await @@ -280,7 +280,7 @@ mod cypher_arrow { use arrow::util::pretty::print_batches; use tempfile::tempdir; - use raphtory::arrow::graph_impl::{ArrowGraph, ParquetLayerCols}; + use raphtory::disk_graph::graph_impl::{DiskGraph, ParquetLayerCols}; use crate::run_cypher; @@ -313,7 +313,7 @@ mod cypher_arrow { let edge_lists = vec![chunk]; let graph = - ArrowGraph::load_from_edge_lists(&edge_lists, 20, 20, graph_dir, 0, 1, 2) + DiskGraph::load_from_edge_lists(&edge_lists, 20, 20, graph_dir, 0, 1, 2) .unwrap(); let df = run_cypher("match ()-[e]->() RETURN *", &graph, true) @@ -356,7 +356,7 @@ mod cypher_arrow { }, ]; - let graph = ArrowGraph::load_from_parquets( + let graph = DiskGraph::load_from_parquets( graph_dir, layer_parquet_cols, None, @@ -386,7 +386,7 @@ mod cypher_arrow { load_nodes(&graph); load_star_edges(&graph); - let graph = ArrowGraph::from_graph(&graph, graph_dir).unwrap(); + let graph = DiskGraph::from_graph(&graph, graph_dir).unwrap(); let df = run_cypher("match ()-[e1]->(b)-[e2]->(), (b)-[e3]->() RETURN e1.src, e1.id, b.id, e2.id, e2.dst, e3.id, e3.dst", &graph, true) .await @@ -404,7 +404,7 @@ mod cypher_arrow { #[tokio::test] async fn select_table_filter_weight() { let graph_dir = tempdir().unwrap(); - let graph = ArrowGraph::make_simple_graph(graph_dir, &EDGES, 10, 10); + let graph = DiskGraph::make_simple_graph(graph_dir, &EDGES, 10, 10); let df = run_cypher("match ()-[e {src: 0}]->() RETURN *", &graph, true) .await @@ -429,7 +429,7 @@ mod cypher_arrow { #[tokio::test] async fn two_hops() { let graph_dir = tempdir().unwrap(); - let graph = ArrowGraph::make_simple_graph(graph_dir, &EDGES, 100, 100); + let graph = DiskGraph::make_simple_graph(graph_dir, &EDGES, 100, 100); let query = "match ()-[e1]->()-[e2]->() return e1.src as start, e1.dst as mid, e2.dst as end ORDER BY start, mid, end"; @@ -439,7 +439,7 @@ mod cypher_arrow { assert_eq!(rb_hop, rb_join); } - async fn run_to_rb(graph: &ArrowGraph, query: &str, enable_hop_optim: bool) -> RecordBatch { + async fn run_to_rb(graph: &DiskGraph, query: &str, enable_hop_optim: bool) -> RecordBatch { let df = run_cypher(query, &graph, enable_hop_optim).await.unwrap(); let data = df.collect().await.unwrap(); print_batches(&data).unwrap(); @@ -451,7 +451,7 @@ mod cypher_arrow { #[ignore] // Hop optimization is not yet fully implemented async fn three_hops() { let graph_dir = tempdir().unwrap(); - let graph = ArrowGraph::make_simple_graph(graph_dir, &EDGES, 100, 100); + let graph = DiskGraph::make_simple_graph(graph_dir, &EDGES, 100, 100); let query = "match ()-[e1]->()-[e2]->()-[e3]->() return * ORDER BY e1.src, e1.dst, e2.src, e2.dst, e3.src, e3.dst"; let hop_rb = run_to_rb(&graph, query, true).await; @@ -464,7 +464,7 @@ mod cypher_arrow { #[tokio::test] async fn three_hops_with_condition() { let graph_dir = tempdir().unwrap(); - let graph = ArrowGraph::make_simple_graph(graph_dir, &EDGES, 100, 100); + let graph = DiskGraph::make_simple_graph(graph_dir, &EDGES, 100, 100); let df = run_cypher( "match ()-[e1]->()-[e2]->()<-[e3]-() where e2.weight > 5 return *", @@ -481,7 +481,7 @@ mod cypher_arrow { #[tokio::test] async fn five_hops() { let graph_dir = tempdir().unwrap(); - let graph = ArrowGraph::make_simple_graph(graph_dir, &EDGES, 100, 100); + let graph = DiskGraph::make_simple_graph(graph_dir, &EDGES, 100, 100); let df = run_cypher( "match ()-[e1]->()-[e2]->()-[e3]->()-[e4]->()-[e5]->() return *", @@ -495,21 +495,21 @@ mod cypher_arrow { print_batches(&data).unwrap(); } - fn make_graph_with_str_col(graph_dir: impl AsRef) -> ArrowGraph { + fn make_graph_with_str_col(graph_dir: impl AsRef) -> DiskGraph { let graph = Graph::new(); load_edges_with_str_props(&graph, None); - ArrowGraph::from_graph(&graph, graph_dir).unwrap() + DiskGraph::from_graph(&graph, graph_dir).unwrap() } - fn make_graph_with_node_props(graph_dir: impl AsRef) -> ArrowGraph { + fn make_graph_with_node_props(graph_dir: impl AsRef) -> DiskGraph { let graph = Graph::new(); load_nodes(&graph); load_edges_with_str_props(&graph, None); - ArrowGraph::from_graph(&graph, graph_dir).unwrap() + DiskGraph::from_graph(&graph, graph_dir).unwrap() } fn load_nodes(graph: &Graph) { @@ -659,7 +659,7 @@ mod cypher_arrow { load_edges_1(&g, Some("LAYER1")); load_edges_with_str_props(&g, Some("LAYER2")); - let graph = ArrowGraph::from_graph(&g, graph_dir).unwrap(); + let graph = DiskGraph::from_graph(&g, graph_dir).unwrap(); let df = run_cypher( "match ()-[e:_default|LAYER1|LAYER2]->() where (e.weight > 3 and e.weight < 5) or e.name starts with 'xb' return e", @@ -678,7 +678,7 @@ mod cypher_arrow { load_edges_1(&g, Some("LAYER1")); load_edges_with_str_props(&g, Some("LAYER2")); - let graph = ArrowGraph::from_graph(&g, graph_dir).unwrap(); + let graph = DiskGraph::from_graph(&g, graph_dir).unwrap(); let df = run_cypher("match ()-[e2:LAYER2]->() RETURN *", &graph, true) .await @@ -706,7 +706,7 @@ mod cypher_arrow { load_edges_1(&g, Some("LAYER1")); load_edges_with_str_props(&g, Some("LAYER2")); - let graph = ArrowGraph::from_graph(&g, graph_dir).unwrap(); + let graph = DiskGraph::from_graph(&g, graph_dir).unwrap(); let df = run_cypher("match ()-[e]->() RETURN *", &graph, true) .await @@ -725,7 +725,7 @@ mod cypher_arrow { load_edges_1(&g, Some("LAYER1")); load_edges_with_str_props(&g, Some("LAYER2")); - let graph = ArrowGraph::from_graph(&g, graph_dir).unwrap(); + let graph = DiskGraph::from_graph(&g, graph_dir).unwrap(); let df = run_cypher("match ()-[e]->() return type(e), e", &graph, true) .await .unwrap(); diff --git a/raphtory-cypher/src/transpiler/mod.rs b/raphtory-cypher/src/transpiler/mod.rs index a5495bbdff..a83d1f5f70 100644 --- a/raphtory-cypher/src/transpiler/mod.rs +++ b/raphtory-cypher/src/transpiler/mod.rs @@ -9,12 +9,12 @@ use arrow_schema::{Fields, Schema}; use itertools::Itertools; use raphtory::{ - arrow::graph_impl::ArrowGraph, core::{ entities::{edges::edge_ref::Dir, VID}, Direction, }, db::{api::properties::internal::ConstPropertiesOps, graph::node::NodeView}, + disk_graph::graph_impl::DiskGraph, prelude::*, }; use sqlparser::ast::{ @@ -23,7 +23,7 @@ use sqlparser::ast::{ mod exprs; -pub fn to_sql(query: Query, graph: &ArrowGraph) -> sql_ast::Statement { +pub fn to_sql(query: Query, graph: &DiskGraph) -> sql_ast::Statement { let query = bind_unbound_pattern_filters(query); let query = unbind_unused_binds(query); @@ -204,7 +204,7 @@ fn parse_order_by(query: &Query, rel_binds: &[String], node_binds: &[String]) -> fn scan_edges_as_sql_cte( layer_names: &[impl AsRef], name: &impl AsRef, - graph: &ArrowGraph, + graph: &DiskGraph, ) -> sql_ast::Cte { // fetch and merge the schemas @@ -245,7 +245,7 @@ fn scan_edges_as_sql_cte( } // TODO: this needs to match the schema from EdgeListTableProvider -fn full_layer_fields(graph: &ArrowGraph, layer_id: usize) -> Option { +fn full_layer_fields(graph: &DiskGraph, layer_id: usize) -> Option { let dt = graph.as_ref().layer(layer_id).edges_props_data_type(); let arr_dt: arrow_schema::DataType = dt.clone().into(); match arr_dt { @@ -301,7 +301,7 @@ fn query_union(q1: Box, q2: Box) -> Box, ) -> (usize, Box) { let layer_id = graph @@ -411,7 +411,7 @@ fn select_query_with_projection( }) } -fn parse_rels_to_ctes(query: &Query, graph: &ArrowGraph) -> With { +fn parse_rels_to_ctes(query: &Query, graph: &DiskGraph) -> With { // each rel can become a CTE // inside the cte // if the pattern has no layers -[e]- and the graph has one layer then we just select * from the layer @@ -479,7 +479,7 @@ fn node_scan_cte(node: &NodePattern) -> sql_ast::Cte { fn parse_select_body( query: &Query, - _graph: &ArrowGraph, + _graph: &DiskGraph, rel_binds: &[String], node_binds: &[String], ) -> Box { @@ -1646,7 +1646,7 @@ mod test { g.add_edge(0, 0, 0, NO_PROPS, Some(layer.as_ref())) .expect("failed to add edge"); } - let graph = ArrowGraph::from_graph(&g, graph_dir).unwrap(); + let graph = DiskGraph::from_graph(&g, graph_dir).unwrap(); let sql = transpiler::to_sql(query, &graph); assert_eq!(sql.to_string(), expected.to_string()); } diff --git a/raphtory-graphql/Cargo.toml b/raphtory-graphql/Cargo.toml index 8c47b69b59..b2a2c3442a 100644 --- a/raphtory-graphql/Cargo.toml +++ b/raphtory-graphql/Cargo.toml @@ -47,4 +47,4 @@ toml = { workspace = true } tempfile = { workspace = true } [features] -arrow = ["raphtory/arrow"] +storage = ["raphtory/storage"] diff --git a/raphtory-graphql/src/data.rs b/raphtory-graphql/src/data.rs index 8b76f1150d..d3fde782b2 100644 --- a/raphtory-graphql/src/data.rs +++ b/raphtory-graphql/src/data.rs @@ -1,6 +1,6 @@ use parking_lot::RwLock; -#[cfg(feature = "arrow")] -use raphtory::arrow::graph_impl::ArrowGraph; +#[cfg(feature = "storage")] +use raphtory::disk_graph::graph_impl::DiskGraph; use raphtory::{ core::Prop, db::api::view::MaterializedGraph, @@ -96,18 +96,18 @@ impl Data { .unwrap_or_else(|| path.file_name().unwrap().to_str().unwrap().to_owned()) } - fn is_arrow_graph_dir(path: &Path) -> bool { - // Check if the directory contains files specific to arrow graphs + fn is_disk_graph_dir(path: &Path) -> bool { + // Check if the directory contains files specific to disk_graph graphs let files = fs::read_dir(path).unwrap(); - let mut has_arrow_files = false; + let mut has_disk_graph_files = false; for file in files { let file_name = file.unwrap().file_name().into_string().unwrap(); if file_name.ends_with(".ipc") { - has_arrow_files = true; + has_disk_graph_files = true; break; } } - has_arrow_files + has_disk_graph_files } fn load_bincode_graph(path: &Path) -> (String, MaterializedGraph) { @@ -122,19 +122,19 @@ impl Data { (graph_name, graph) } - #[cfg(feature = "arrow")] - fn load_arrow_graph(path: &Path) -> (String, MaterializedGraph) { - let arrow_graph = - ArrowGraph::load_from_dir(path).expect("Unable to load from arrow graph"); - let graph: MaterializedGraph = arrow_graph.into(); + #[cfg(feature = "storage")] + fn load_disk_graph(path: &Path) -> (String, MaterializedGraph) { + let disk_graph = + DiskGraph::load_from_dir(path).expect("Unable to load from disk_graph graph"); + let graph: MaterializedGraph = disk_graph.into(); let graph_name = get_graph_name(path, &graph); (graph_name, graph) } - - #[cfg(not(feature = "arrow"))] - fn load_arrow_graph(path: &Path) -> (String, MaterializedGraph) { - unimplemented!("Arrow feature not enabled, cannot load from arrow graph") + #[allow(unused_variables)] + #[cfg(not(feature = "storage"))] + fn load_disk_graph(path: &Path) -> (String, MaterializedGraph) { + unimplemented!("Storage feature not enabled, cannot load from disk graph") } fn add_to_graphs( @@ -161,17 +161,15 @@ impl Data { let entry = entry.unwrap(); let path = entry.path(); if path.is_dir() { - println!("Arrow Graph loaded = {}", path.display()); - if is_arrow_graph_dir(&path) { - if let (graph_name, graph) = load_arrow_graph(&path) { - add_to_graphs(&mut graphs, &graph_name, &graph); - } + println!("Disk Graph loaded = {}", path.display()); + if is_disk_graph_dir(&path) { + let (graph_name, graph) = load_disk_graph(&path); + add_to_graphs(&mut graphs, &graph_name, &graph); } } else { println!("Graph loaded = {}", path.display()); - if let (graph_name, graph) = load_bincode_graph(&path) { - add_to_graphs(&mut graphs, &graph_name, &graph); - } + let (graph_name, graph) = load_bincode_graph(&path); + add_to_graphs(&mut graphs, &graph_name, &graph); } } diff --git a/raphtory-graphql/src/lib.rs b/raphtory-graphql/src/lib.rs index 67333ab10e..b056f1ef06 100644 --- a/raphtory-graphql/src/lib.rs +++ b/raphtory-graphql/src/lib.rs @@ -40,8 +40,8 @@ mod graphql_test { use crate::{data::Data, model::App}; use async_graphql::UploadValue; use dynamic_graphql::{Request, Variables}; - #[cfg(feature = "arrow")] - use raphtory::arrow::graph_impl::ArrowGraph; + #[cfg(feature = "storage")] + use raphtory::disk_graph::graph_impl::DiskGraph; use raphtory::{ db::{api::view::IntoDynamic, graph::views::deletion_graph::PersistentGraph}, prelude::*, @@ -1094,9 +1094,9 @@ mod graphql_test { ); } - #[cfg(feature = "arrow")] + #[cfg(feature = "storage")] #[tokio::test] - async fn test_arrow_graph() { + async fn test_disk_graph() { let graph = Graph::new(); graph.add_constant_properties([("name", "graph")]).unwrap(); graph.add_node(1, 1, NO_PROPS, Some("a")).unwrap(); @@ -1114,8 +1114,8 @@ mod graphql_test { graph.add_edge(22, 3, 6, NO_PROPS, Some("a")).unwrap(); let test_dir = TempDir::new().unwrap(); - let arrow_graph = ArrowGraph::from_graph(&graph, test_dir.path()).unwrap(); - let graph: MaterializedGraph = arrow_graph.into(); + let disk_graph = DiskGraph::from_graph(&graph, test_dir.path()).unwrap(); + let graph: MaterializedGraph = disk_graph.into(); let graphs = HashMap::from([("graph".to_string(), graph)]); let data = Data::from_map(graphs); @@ -1177,7 +1177,7 @@ mod graphql_test { let res = schema.execute(req).await; let data = res.errors; let error_message = &data[0].message; - let expected_error_message = "Arrow Graph is immutable"; + let expected_error_message = "Disk Graph is immutable"; assert_eq!(error_message, expected_error_message); } } diff --git a/raphtory-graphql/src/model/mod.rs b/raphtory-graphql/src/model/mod.rs index 403d7d6088..c6904efa90 100644 --- a/raphtory-graphql/src/model/mod.rs +++ b/raphtory-graphql/src/model/mod.rs @@ -46,8 +46,8 @@ impl Error for MissingGraph {} #[derive(thiserror::Error, Debug)] pub enum GqlGraphError { - #[error("Arrow Graph is immutable")] - ImmutableArrowGraph, + #[error("Disk Graph is immutable")] + ImmutableDiskGraph, } #[derive(ResolvedObject)] @@ -142,9 +142,9 @@ impl Mut { let subgraph = data.get(&graph_name).ok_or("Graph not found")?; - #[cfg(feature = "arrow")] - if subgraph.clone().graph.into_arrow().is_some() { - return Err(GqlGraphError::ImmutableArrowGraph.into()); + #[cfg(feature = "storage")] + if subgraph.clone().graph.into_disk_graph().is_some() { + return Err(GqlGraphError::ImmutableDiskGraph.into()); } if new_graph_name.ne(&graph_name) && parent_graph_name.ne(&graph_name) { @@ -193,9 +193,9 @@ impl Mut { let subgraph = data.get(&graph_name).ok_or("Graph not found")?; - #[cfg(feature = "arrow")] - if subgraph.clone().graph.into_arrow().is_some() { - return Err(GqlGraphError::ImmutableArrowGraph.into()); + #[cfg(feature = "storage")] + if subgraph.clone().graph.into_disk_graph().is_some() { + return Err(GqlGraphError::ImmutableDiskGraph.into()); } let dt = Utc::now(); @@ -229,9 +229,9 @@ impl Mut { let parent_graph = data.get(&parent_graph_name).ok_or("Graph not found")?; let subgraph = data.get(&graph_name).ok_or("Graph not found")?; - #[cfg(feature = "arrow")] - if subgraph.clone().graph.into_arrow().is_some() { - return Err(GqlGraphError::ImmutableArrowGraph.into()); + #[cfg(feature = "storage")] + if subgraph.clone().graph.into_disk_graph().is_some() { + return Err(GqlGraphError::ImmutableDiskGraph.into()); } let path = match data.get(&new_graph_name) { @@ -382,9 +382,9 @@ impl Mut { let data = ctx.data_unchecked::().graphs.write(); let subgraph = data.get(&graph_name).ok_or("Graph not found")?; - #[cfg(feature = "arrow")] - if subgraph.clone().graph.into_arrow().is_some() { - return Err(GqlGraphError::ImmutableArrowGraph.into()); + #[cfg(feature = "storage")] + if subgraph.clone().graph.into_disk_graph().is_some() { + return Err(GqlGraphError::ImmutableDiskGraph.into()); } subgraph.update_constant_properties([("isArchive", Prop::U8(is_archive))])?; diff --git a/raphtory/Cargo.toml b/raphtory/Cargo.toml index 84fb057b8b..160c7b3441 100644 --- a/raphtory/Cargo.toml +++ b/raphtory/Cargo.toml @@ -68,14 +68,14 @@ polars-parquet = { workspace = true, optional = true } polars-utils = { workspace = true, optional = true } kdam = { workspace = true, optional = true } -# arrow storage optional dependencies +# disk storage optional dependencies memmap2 = { workspace = true, optional = true } ahash = { workspace = true, optional = true } tempfile = { workspace = true, optional = true } bytemuck = { workspace = true, optional = true } rpds = { workspace = true, optional = true } thread_local = { workspace = true, optional = true } -raphtory-arrow = { workspace = true, optional = true } +pometry-storage = { workspace = true, optional = true } [dev-dependencies] csv = { workspace = true } @@ -104,7 +104,6 @@ io = [ # Enables generating the pyo3 python bindings python = [ "io", - # "arrow", "dep:pyo3", "dep:num", "dep:display-error-chain", @@ -117,9 +116,9 @@ python = [ search = ["dep:tantivy"] # vectors vectors = ["dep:futures-util", "dep:async-trait", "dep:async-openai"] -# arrow -arrow = [ - "raphtory-arrow", +# storage +storage = [ + "pometry-storage", "dep:polars-arrow", "dep:polars-utils", "dep:polars-parquet", diff --git a/raphtory/src/algorithms/centrality/betweenness.rs b/raphtory/src/algorithms/centrality/betweenness.rs index ba6e90a752..453461983e 100644 --- a/raphtory/src/algorithms/centrality/betweenness.rs +++ b/raphtory/src/algorithms/centrality/betweenness.rs @@ -140,8 +140,8 @@ mod betweenness_centrality_test { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let mut expected: HashMap = HashMap::new(); @@ -166,7 +166,7 @@ mod betweenness_centrality_test { assert_eq!(res.get_all_with_names(), expected); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } } diff --git a/raphtory/src/algorithms/centrality/degree_centrality.rs b/raphtory/src/algorithms/centrality/degree_centrality.rs index 7c2bcf6010..8a90238f47 100644 --- a/raphtory/src/algorithms/centrality/degree_centrality.rs +++ b/raphtory/src/algorithms/centrality/degree_centrality.rs @@ -77,8 +77,8 @@ mod degree_centrality_test { graph.add_edge(0, *src, *dst, NO_PROPS, None).unwrap(); } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let mut hash_map_result: HashMap = HashMap::new(); @@ -92,7 +92,7 @@ mod degree_centrality_test { assert_eq!(res, hash_map_result); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } } diff --git a/raphtory/src/algorithms/centrality/hits.rs b/raphtory/src/algorithms/centrality/hits.rs index d089d3e395..f1498dfad3 100644 --- a/raphtory/src/algorithms/centrality/hits.rs +++ b/raphtory/src/algorithms/centrality/hits.rs @@ -215,8 +215,8 @@ mod hits_tests { (8, 1), ]); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let results = hits(graph, 20, None).get_all_with_names(); @@ -236,7 +236,7 @@ mod hits_tests { ); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } } diff --git a/raphtory/src/algorithms/centrality/pagerank.rs b/raphtory/src/algorithms/centrality/pagerank.rs index 4bb5f548c9..97440bb740 100644 --- a/raphtory/src/algorithms/centrality/pagerank.rs +++ b/raphtory/src/algorithms/centrality/pagerank.rs @@ -213,8 +213,8 @@ mod page_rank_tests { let graph = load_graph(); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let results = unweighted_page_rank(graph, Some(1000), Some(1), None, true, None); @@ -226,8 +226,8 @@ mod page_rank_tests { } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -265,8 +265,8 @@ mod page_rank_tests { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let results = unweighted_page_rank(graph, Some(1000), Some(4), None, true, None); @@ -284,8 +284,8 @@ mod page_rank_tests { assert_eq_f64(results.get("5"), Some(&0.19658), 5); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -299,8 +299,8 @@ mod page_rank_tests { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let results = unweighted_page_rank(graph, Some(1000), Some(4), None, false, None); @@ -309,8 +309,8 @@ mod page_rank_tests { assert_eq_f64(results.get("2"), Some(&0.5), 3); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -324,8 +324,8 @@ mod page_rank_tests { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let results = unweighted_page_rank(graph, Some(10), Some(4), None, false, None); @@ -335,8 +335,8 @@ mod page_rank_tests { assert_eq_f64(results.get("3"), Some(&0.303), 3); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -369,8 +369,8 @@ mod page_rank_tests { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let results = unweighted_page_rank(graph, Some(1000), Some(4), None, true, None); @@ -388,8 +388,8 @@ mod page_rank_tests { assert_eq_f64(results.get("11"), Some(&0.122), 3); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } fn assert_eq_f64 + PartialEq + std::fmt::Debug>( diff --git a/raphtory/src/algorithms/community_detection/label_propagation.rs b/raphtory/src/algorithms/community_detection/label_propagation.rs index fc65add0a7..e69ee0cc7e 100644 --- a/raphtory/src/algorithms/community_detection/label_propagation.rs +++ b/raphtory/src/algorithms/community_detection/label_propagation.rs @@ -99,8 +99,8 @@ mod lpa_tests { graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let seed = Some([5; 32]); @@ -125,7 +125,7 @@ mod lpa_tests { } } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } } diff --git a/raphtory/src/algorithms/community_detection/louvain.rs b/raphtory/src/algorithms/community_detection/louvain.rs index c62231c4ab..121155d9ba 100644 --- a/raphtory/src/algorithms/community_detection/louvain.rs +++ b/raphtory/src/algorithms/community_detection/louvain.rs @@ -100,16 +100,16 @@ mod test { .unwrap(); } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let result = louvain::(graph, 1.0, Some("weight"), None); assert!(graph.nodes().iter().all(|n| result.get(n).is_some())); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } fn test_all_nodes_assigned_inner_unweighted(edges: Vec<(u64, u64)>) { @@ -120,16 +120,16 @@ mod test { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let result = louvain::(graph, 1.0, None, None); assert!(graph.nodes().iter().all(|n| result.get(n).is_some())); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } proptest! { @@ -169,15 +169,15 @@ mod test { .unwrap(); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let result = louvain::(graph, 1.0, None, None); println!("{result:?}") } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } } diff --git a/raphtory/src/algorithms/community_detection/modularity.rs b/raphtory/src/algorithms/community_detection/modularity.rs index cbd77a45ed..b1a64a1f97 100644 --- a/raphtory/src/algorithms/community_detection/modularity.rs +++ b/raphtory/src/algorithms/community_detection/modularity.rs @@ -436,8 +436,8 @@ mod test { graph.add_edge(0, 2, 1, NO_PROPS, None).unwrap(); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let mut m = ModularityUnDir::new( @@ -455,8 +455,8 @@ mod test { assert_eq!(m.value(), old_value + delta) } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -470,8 +470,8 @@ mod test { graph.add_edge(0, 3, 0, NO_PROPS, None).unwrap(); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let partition = Partition::from_iter([0usize, 0, 1, 1]); @@ -488,7 +488,7 @@ mod test { assert!((value_merged - (value_after + delta)).abs() < 1e-8); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } } diff --git a/raphtory/src/algorithms/components/connected_components.rs b/raphtory/src/algorithms/components/connected_components.rs index 9d80b179e4..91cc182ddb 100644 --- a/raphtory/src/algorithms/components/connected_components.rs +++ b/raphtory/src/algorithms/components/connected_components.rs @@ -118,8 +118,8 @@ mod cc_test { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let results = weakly_connected_components(graph, usize::MAX, None).get_all_with_names(); @@ -140,8 +140,8 @@ mod cc_test { ); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -179,8 +179,8 @@ mod cc_test { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let results = weakly_connected_components(graph, usize::MAX, None).get_all_with_names(); @@ -205,8 +205,8 @@ mod cc_test { ); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } // connected community_detection on a graph with 1 node and a self loop @@ -221,8 +221,8 @@ mod cc_test { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let results = weakly_connected_components(graph, usize::MAX, None).get_all_with_names(); @@ -235,8 +235,8 @@ mod cc_test { ); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -248,8 +248,8 @@ mod cc_test { graph.add_edge(9, 4, 3, NO_PROPS, None).expect("add edge"); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let results = weakly_connected_components(graph, usize::MAX, None).get_all_with_names(); @@ -275,8 +275,8 @@ mod cc_test { assert_eq!(results, expected); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[quickcheck] @@ -322,8 +322,8 @@ mod cc_test { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G, smallest: &u64, edges: &[(u64, u64)]) { // now we do connected community_detection over window 0..1 @@ -341,8 +341,8 @@ mod cc_test { assert_eq!(actual, (*smallest, edges.len())); } test(&graph, smallest, &edges); - #[cfg(feature = "arrow")] - test(&arrow_graph, smallest, &edges); + #[cfg(feature = "storage")] + test(&disk_graph, smallest, &edges); } } } diff --git a/raphtory/src/algorithms/components/in_components.rs b/raphtory/src/algorithms/components/in_components.rs index 1328cefbc3..b4fbc4f0a3 100644 --- a/raphtory/src/algorithms/components/in_components.rs +++ b/raphtory/src/algorithms/components/in_components.rs @@ -107,8 +107,8 @@ mod components_test { graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let results = in_components(graph, None).get_all_with_names(); @@ -131,7 +131,7 @@ mod components_test { assert_eq!(map, correct); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } } diff --git a/raphtory/src/algorithms/components/out_components.rs b/raphtory/src/algorithms/components/out_components.rs index 8d4430e7e8..2cfbe72d3a 100644 --- a/raphtory/src/algorithms/components/out_components.rs +++ b/raphtory/src/algorithms/components/out_components.rs @@ -111,8 +111,8 @@ mod components_test { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let results = out_components(graph, None).get_all_with_names(); @@ -135,7 +135,7 @@ mod components_test { assert_eq!(map, correct); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } } diff --git a/raphtory/src/algorithms/components/scc.rs b/raphtory/src/algorithms/components/scc.rs index 61bec62185..29eadfa6ff 100644 --- a/raphtory/src/algorithms/components/scc.rs +++ b/raphtory/src/algorithms/components/scc.rs @@ -200,8 +200,8 @@ mod strongly_connected_components_tests { graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let scc_nodes: HashSet<_> = strongly_connected_components(graph, None) @@ -225,8 +225,8 @@ mod strongly_connected_components_tests { assert_eq!(scc_nodes, expected); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -251,8 +251,8 @@ mod strongly_connected_components_tests { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let scc_nodes: HashSet<_> = strongly_connected_components(graph, None) @@ -272,8 +272,8 @@ mod strongly_connected_components_tests { assert_eq!(scc_nodes, expected); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -285,8 +285,8 @@ mod strongly_connected_components_tests { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let scc_nodes: HashSet<_> = strongly_connected_components(graph, None) @@ -305,8 +305,8 @@ mod strongly_connected_components_tests { assert_eq!(scc_nodes, expected); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -327,8 +327,8 @@ mod strongly_connected_components_tests { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let scc_nodes: HashSet<_> = strongly_connected_components(graph, None) @@ -355,7 +355,7 @@ mod strongly_connected_components_tests { assert_eq!(scc_nodes, expected); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } } diff --git a/raphtory/src/algorithms/cores/k_core.rs b/raphtory/src/algorithms/cores/k_core.rs index 42345980ec..fdc8dc3733 100644 --- a/raphtory/src/algorithms/cores/k_core.rs +++ b/raphtory/src/algorithms/cores/k_core.rs @@ -144,8 +144,8 @@ mod k_core_test { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let result = k_core_set(graph, 2, usize::MAX, None); @@ -158,7 +158,7 @@ mod k_core_test { assert_eq!(actual, subgraph.nodes().name().collect::>()); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } } diff --git a/raphtory/src/algorithms/dynamics/temporal/epidemics.rs b/raphtory/src/algorithms/dynamics/temporal/epidemics.rs index bfdc41b3d2..8e462bb783 100644 --- a/raphtory/src/algorithms/dynamics/temporal/epidemics.rs +++ b/raphtory/src/algorithms/dynamics/temporal/epidemics.rs @@ -371,9 +371,9 @@ mod test { inner_test(event_rate, recovery_rate, p); } - #[cfg(feature = "arrow")] + #[cfg(feature = "storage")] #[test] - fn compare_arrow_with_in_mem() { + fn compare_disk_with_in_mem() { let event_rate = 0.00000001; let recovery_rate = 0.000000001; let p = 0.3; @@ -381,10 +381,10 @@ mod test { let mut rng = SmallRng::seed_from_u64(0); let g = generate_graph(1000, event_rate, &mut rng); let test_dir = TempDir::new().unwrap(); - let arrow_graph = g.persist_as_arrow(test_dir.path()).unwrap(); + let disk_graph = g.persist_as_disk_graph(test_dir.path()).unwrap(); let mut rng = SmallRng::seed_from_u64(0); let res_arrow = temporal_SEIR( - &arrow_graph, + &disk_graph, Some(recovery_rate), None, p, diff --git a/raphtory/src/algorithms/metrics/balance.rs b/raphtory/src/algorithms/metrics/balance.rs index fc627bcb34..ae115498d4 100644 --- a/raphtory/src/algorithms/metrics/balance.rs +++ b/raphtory/src/algorithms/metrics/balance.rs @@ -169,8 +169,8 @@ mod sum_weight_test { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let res = balance(graph, "value_dec".to_string(), Direction::BOTH, None); @@ -209,7 +209,7 @@ mod sum_weight_test { assert_eq!(res.get_all(), expected); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } } diff --git a/raphtory/src/algorithms/metrics/clustering_coefficient.rs b/raphtory/src/algorithms/metrics/clustering_coefficient.rs index 9fd5d7b11c..1e723e27a6 100644 --- a/raphtory/src/algorithms/metrics/clustering_coefficient.rs +++ b/raphtory/src/algorithms/metrics/clustering_coefficient.rs @@ -90,15 +90,15 @@ mod cc_test { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let results = clustering_coefficient(graph); assert_eq!(results, 0.3); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } } diff --git a/raphtory/src/algorithms/metrics/degree.rs b/raphtory/src/algorithms/metrics/degree.rs index de16f3d844..9bf9271a63 100644 --- a/raphtory/src/algorithms/metrics/degree.rs +++ b/raphtory/src/algorithms/metrics/degree.rs @@ -120,8 +120,8 @@ mod degree_test { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let expected_max_out_degree = 3; @@ -154,7 +154,7 @@ mod degree_test { assert_eq!(expected_min_degree, actual_min_degree); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } } diff --git a/raphtory/src/algorithms/metrics/directed_graph_density.rs b/raphtory/src/algorithms/metrics/directed_graph_density.rs index fb3ee96318..8b1f9a461f 100644 --- a/raphtory/src/algorithms/metrics/directed_graph_density.rs +++ b/raphtory/src/algorithms/metrics/directed_graph_density.rs @@ -65,8 +65,8 @@ mod directed_graph_density_tests { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let windowed_graph = graph.window(0, 7); @@ -76,8 +76,8 @@ mod directed_graph_density_tests { assert_eq!(actual, expected); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -91,8 +91,8 @@ mod directed_graph_density_tests { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let windowed_graph = graph.window(0, 3); @@ -102,7 +102,7 @@ mod directed_graph_density_tests { assert_eq!(actual, expected); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } } diff --git a/raphtory/src/algorithms/metrics/local_clustering_coefficient.rs b/raphtory/src/algorithms/metrics/local_clustering_coefficient.rs index 284d97250d..49b9aea21f 100644 --- a/raphtory/src/algorithms/metrics/local_clustering_coefficient.rs +++ b/raphtory/src/algorithms/metrics/local_clustering_coefficient.rs @@ -105,8 +105,8 @@ mod clustering_coefficient_tests { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let expected = vec![0.33333334, 1.0, 1.0, 0.0, 0.0]; @@ -118,7 +118,7 @@ mod clustering_coefficient_tests { assert_eq!(actual, expected); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } } diff --git a/raphtory/src/algorithms/metrics/reciprocity.rs b/raphtory/src/algorithms/metrics/reciprocity.rs index b2895147bd..350ff75f9c 100644 --- a/raphtory/src/algorithms/metrics/reciprocity.rs +++ b/raphtory/src/algorithms/metrics/reciprocity.rs @@ -194,8 +194,8 @@ mod reciprocity_test { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let actual = global_reciprocity(graph, None); @@ -212,7 +212,7 @@ mod reciprocity_test { assert_eq!(res.get("1"), hash_map_result.get("1")); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } } diff --git a/raphtory/src/algorithms/motifs/global_temporal_three_node_motifs.rs b/raphtory/src/algorithms/motifs/global_temporal_three_node_motifs.rs index 0d64c52df7..576b054a69 100644 --- a/raphtory/src/algorithms/motifs/global_temporal_three_node_motifs.rs +++ b/raphtory/src/algorithms/motifs/global_temporal_three_node_motifs.rs @@ -351,8 +351,8 @@ mod motifs_test { ]); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let global_motifs = &temporal_three_node_motif_multi(graph, vec![10], None); @@ -369,7 +369,7 @@ mod motifs_test { assert_eq!(global_motifs[0], expected); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } } diff --git a/raphtory/src/algorithms/motifs/local_temporal_three_node_motifs.rs b/raphtory/src/algorithms/motifs/local_temporal_three_node_motifs.rs index 4281cc7373..d62bc9c76d 100644 --- a/raphtory/src/algorithms/motifs/local_temporal_three_node_motifs.rs +++ b/raphtory/src/algorithms/motifs/local_temporal_three_node_motifs.rs @@ -1,17 +1,10 @@ // Imports /////////////////////////////////////////// use crate::{ algorithms::{cores::k_core::k_core_set, motifs::three_node_motifs::*}, - core::state::{ - accumulator_id::{ - accumulators::{self, val}, - AccId, - }, - agg::ValDef, - compute_state::ComputeStateVec, - }, + core::state::{accumulator_id::accumulators, compute_state::ComputeStateVec}, db::{ api::view::{NodeViewOps, *}, - graph::{edge::EdgeView, views::node_subgraph::NodeSubgraph}, + graph::views::node_subgraph::NodeSubgraph, task::{ context::Context, node::eval_node::EvalNodeView, @@ -25,7 +18,7 @@ use itertools::{enumerate, Itertools}; use num_traits::Zero; use raphtory_api::core::entities::VID; use rustc_hash::FxHashSet; -use std::{cmp::Ordering, collections::HashMap, mem, ops::Add, slice::Iter}; +use std::{collections::HashMap, mem, ops::Add, slice::Iter}; /////////////////////////////////////////////////////// // State objects for three node motifs @@ -325,7 +318,7 @@ where G: StaticGraphViewOps, { let delta_len = deltas.len(); - let mut ctx: Context = g.into(); + let ctx: Context = g.into(); println!("Running triangle step"); let triadic_motifs = triangle_motifs(g, deltas.clone(), threads); diff --git a/raphtory/src/algorithms/motifs/local_triangle_count.rs b/raphtory/src/algorithms/motifs/local_triangle_count.rs index c59dc92f91..fff93fecc1 100644 --- a/raphtory/src/algorithms/motifs/local_triangle_count.rs +++ b/raphtory/src/algorithms/motifs/local_triangle_count.rs @@ -87,8 +87,8 @@ mod triangle_count_tests { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let windowed_graph = graph.window(0, 5); @@ -101,7 +101,7 @@ mod triangle_count_tests { assert_eq!(actual, expected); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } } diff --git a/raphtory/src/algorithms/motifs/triangle_count.rs b/raphtory/src/algorithms/motifs/triangle_count.rs index 6c95d0973c..20bd670f3e 100644 --- a/raphtory/src/algorithms/motifs/triangle_count.rs +++ b/raphtory/src/algorithms/motifs/triangle_count.rs @@ -161,8 +161,8 @@ mod triangle_count_tests { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let actual_tri_count = triangle_count(graph, Some(2)); @@ -170,8 +170,8 @@ mod triangle_count_tests { assert_eq!(actual_tri_count, 4) } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] diff --git a/raphtory/src/algorithms/motifs/triplet_count.rs b/raphtory/src/algorithms/motifs/triplet_count.rs index c1d3c6233c..184d87cc37 100644 --- a/raphtory/src/algorithms/motifs/triplet_count.rs +++ b/raphtory/src/algorithms/motifs/triplet_count.rs @@ -155,8 +155,8 @@ mod triplet_test { graph.add_edge(0, src, dst, NO_PROPS, None).unwrap(); } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let exp_triplet_count = 20; @@ -165,7 +165,7 @@ mod triplet_test { assert_eq!(results, exp_triplet_count); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } } diff --git a/raphtory/src/algorithms/pathing/dijkstra.rs b/raphtory/src/algorithms/pathing/dijkstra.rs index d1aca62a67..81f1840d71 100644 --- a/raphtory/src/algorithms/pathing/dijkstra.rs +++ b/raphtory/src/algorithms/pathing/dijkstra.rs @@ -247,8 +247,8 @@ mod dijkstra_tests { fn test_dijkstra_multiple_targets() { let graph = basic_graph(); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let targets: Vec<&str> = vec!["D", "F"]; @@ -285,16 +285,16 @@ mod dijkstra_tests { assert_eq!(results.get("F").unwrap().1, vec!["B", "C", "E", "F"]); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] fn test_dijkstra_no_weight() { let graph = basic_graph(); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let targets: Vec<&str> = vec!["C", "E", "F"]; @@ -306,8 +306,8 @@ mod dijkstra_tests { assert_eq!(results.get("F").unwrap().1, vec!["A", "C", "F"]); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -329,8 +329,8 @@ mod dijkstra_tests { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let targets = vec![4, 6]; @@ -365,8 +365,8 @@ mod dijkstra_tests { assert_eq!(results.get("6").unwrap().1, vec!["2", "3", "5", "6"]); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -389,8 +389,8 @@ mod dijkstra_tests { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let targets: Vec<&str> = vec!["D", "F"]; @@ -425,8 +425,8 @@ mod dijkstra_tests { assert_eq!(results.get("F").unwrap().1, vec!["B", "C", "E", "F"]); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -444,8 +444,8 @@ mod dijkstra_tests { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let targets: Vec<&str> = vec!["D"]; @@ -462,8 +462,8 @@ mod dijkstra_tests { assert_eq!(results.get("D").unwrap().1, vec!["A", "C", "D"]); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -481,8 +481,8 @@ mod dijkstra_tests { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let targets: Vec<&str> = vec!["D"]; @@ -492,7 +492,7 @@ mod dijkstra_tests { assert_eq!(results.get("D").unwrap().1, vec!["A", "C", "D"]); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } } diff --git a/raphtory/src/algorithms/pathing/single_source_shortest_path.rs b/raphtory/src/algorithms/pathing/single_source_shortest_path.rs index 4dd927022e..5b3b6e7590 100644 --- a/raphtory/src/algorithms/pathing/single_source_shortest_path.rs +++ b/raphtory/src/algorithms/pathing/single_source_shortest_path.rs @@ -108,8 +108,8 @@ mod sssp_tests { ]); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let binding = single_source_shortest_path(graph, 1, Some(4)); @@ -138,7 +138,7 @@ mod sssp_tests { println!("{:?}", binding.get_all_with_names()); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } } diff --git a/raphtory/src/algorithms/pathing/temporal_reachability.rs b/raphtory/src/algorithms/pathing/temporal_reachability.rs index 2305114342..c023f8ee6d 100644 --- a/raphtory/src/algorithms/pathing/temporal_reachability.rs +++ b/raphtory/src/algorithms/pathing/temporal_reachability.rs @@ -261,8 +261,8 @@ mod generic_taint_tests { ]); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let results = sort_inner_by_string(test_generic_taint(graph, 20, 11, vec![2], None)); @@ -285,8 +285,8 @@ mod generic_taint_tests { assert_eq!(results, expected); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -305,8 +305,8 @@ mod generic_taint_tests { ]); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let results = sort_inner_by_string(test_generic_taint(graph, 20, 11, vec![1, 2], None)); @@ -332,8 +332,8 @@ mod generic_taint_tests { assert_eq!(results, expected); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -352,8 +352,8 @@ mod generic_taint_tests { ]); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let results = sort_inner_by_string(test_generic_taint( @@ -379,8 +379,8 @@ mod generic_taint_tests { assert_eq!(results, expected); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -401,8 +401,8 @@ mod generic_taint_tests { ]); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let results = sort_inner_by_string(test_generic_taint( @@ -432,7 +432,7 @@ mod generic_taint_tests { assert_eq!(results, expected); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } } diff --git a/raphtory/src/algorithms/projections/temporal_bipartite_projection.rs b/raphtory/src/algorithms/projections/temporal_bipartite_projection.rs index c9f9f4d53a..f973a0e757 100644 --- a/raphtory/src/algorithms/projections/temporal_bipartite_projection.rs +++ b/raphtory/src/algorithms/projections/temporal_bipartite_projection.rs @@ -1,17 +1,11 @@ -use std::any::Any; - use itertools::Itertools; use num_integer::average_floor; -// use num::integer::average_floor; extern crate num_integer; use crate::{ - core::entities::nodes::node_ref::{AsNodeRef, NodeRef}, + core::entities::nodes::node_ref::AsNodeRef, db::{ - api::{ - mutation::AdditionOps, - view::{internal, *}, - }, + api::{mutation::AdditionOps, view::*}, graph::graph::Graph, }, prelude::*, @@ -75,15 +69,13 @@ fn populate_edges(g: &G, new_graph: &Graph, #[cfg(test)] mod bipartite_graph_tests { - use itertools::Itertools; - use super::temporal_bipartite_projection; use crate::{ db::{ api::{mutation::AdditionOps, view::*}, graph::graph::Graph, }, - prelude::{Prop, NO_PROPS}, + prelude::NO_PROPS, }; #[test] diff --git a/raphtory/src/arrow/storage_interface/nodes.rs b/raphtory/src/arrow/storage_interface/nodes.rs deleted file mode 100644 index 899347cbea..0000000000 --- a/raphtory/src/arrow/storage_interface/nodes.rs +++ /dev/null @@ -1,26 +0,0 @@ -use crate::{ - arrow::storage_interface::{node::ArrowNode, nodes_ref::ArrowNodesRef}, - core::entities::VID, -}; - -use raphtory_arrow::graph::TemporalGraph; -use std::sync::Arc; - -#[derive(Clone, Debug)] -pub struct ArrowNodesOwned { - graph: Arc, -} - -impl ArrowNodesOwned { - pub(crate) fn new(graph: Arc) -> Self { - Self { graph } - } - - pub fn node(&self, vid: VID) -> ArrowNode { - ArrowNode::new(&self.graph, vid) - } - - pub fn as_ref(&self) -> ArrowNodesRef { - ArrowNodesRef::new(&self.graph) - } -} diff --git a/raphtory/src/core/mod.rs b/raphtory/src/core/mod.rs index 30e8bb8a3c..2f84af6a20 100644 --- a/raphtory/src/core/mod.rs +++ b/raphtory/src/core/mod.rs @@ -42,7 +42,7 @@ use std::{ sync::Arc, }; -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] use crate::arrow2::datatypes::ArrowDataType as DataType; #[cfg(test)] @@ -221,7 +221,7 @@ impl PropType { } } -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] impl From<&DataType> for PropType { fn from(value: &DataType) -> Self { match value { diff --git a/raphtory/src/core/utils/hashing.rs b/raphtory/src/core/utils/hashing.rs index 0c1bc43e61..006b10b429 100644 --- a/raphtory/src/core/utils/hashing.rs +++ b/raphtory/src/core/utils/hashing.rs @@ -5,8 +5,8 @@ use std::hash::{Hash, Hasher}; use twox_hash::XxHash64; -#[cfg(feature = "arrow")] -use raphtory_arrow::GID; +#[cfg(feature = "storage")] +use pometry_storage::GID; pub fn calculate_hash(t: &T) -> u64 { let mut s = XxHash64::default(); @@ -14,7 +14,7 @@ pub fn calculate_hash(t: &T) -> u64 { s.finish() } -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] pub fn calculate_hash_spark(gid: &GID) -> i64 { let mut s = XxHash64::with_seed(42); match gid { diff --git a/raphtory/src/db/api/storage/edges/edge_entry.rs b/raphtory/src/db/api/storage/edges/edge_entry.rs index dbcd431741..ef8b730552 100644 --- a/raphtory/src/db/api/storage/edges/edge_entry.rs +++ b/raphtory/src/db/api/storage/edges/edge_entry.rs @@ -12,8 +12,8 @@ use crate::{ }, }; -#[cfg(feature = "arrow")] -use crate::arrow::storage_interface::edge::ArrowEdge; +#[cfg(feature = "storage")] +use crate::disk_graph::storage_interface::edge::DiskEdge; use crate::db::api::storage::tprop_storage_ops::TPropOps; use rayon::prelude::*; @@ -22,16 +22,16 @@ use std::ops::Range; #[derive(Debug)] pub enum EdgeStorageEntry<'a> { Mem(Entry<'a, EdgeStore>), - #[cfg(feature = "arrow")] - Arrow(ArrowEdge<'a>), + #[cfg(feature = "storage")] + Disk(DiskEdge<'a>), } impl<'a> EdgeStorageEntry<'a> { pub fn as_ref(&self) -> EdgeStorageRef { match self { EdgeStorageEntry::Mem(edge) => EdgeStorageRef::Mem(edge), - #[cfg(feature = "arrow")] - EdgeStorageEntry::Arrow(edge) => EdgeStorageRef::Arrow(*edge), + #[cfg(feature = "storage")] + EdgeStorageEntry::Disk(edge) => EdgeStorageRef::Disk(*edge), } } } diff --git a/raphtory/src/db/api/storage/edges/edge_owned_entry.rs b/raphtory/src/db/api/storage/edges/edge_owned_entry.rs index fa482d8704..3b4536da19 100644 --- a/raphtory/src/db/api/storage/edges/edge_owned_entry.rs +++ b/raphtory/src/db/api/storage/edges/edge_owned_entry.rs @@ -1,7 +1,7 @@ -#[cfg(feature = "arrow")] -use crate::arrow::storage_interface::edge::ArrowOwnedEdge; -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] use crate::db::api::storage::variants::storage_variants::StorageVariants; +#[cfg(feature = "storage")] +use crate::disk_graph::storage_interface::edge::DiskOwnedEdge; use crate::{ core::{ entities::{ @@ -25,21 +25,21 @@ use std::ops::Range; #[derive(Debug, Clone)] pub enum EdgeOwnedEntry { Mem(ArcEntry), - #[cfg(feature = "arrow")] - Arrow(ArrowOwnedEdge), + #[cfg(feature = "storage")] + Disk(DiskOwnedEdge), } -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] macro_rules! for_all_variants { ($value:expr, $pattern:pat => $result:expr) => { match $value { EdgeOwnedEntry::Mem($pattern) => StorageVariants::Mem($result), - EdgeOwnedEntry::Arrow($pattern) => StorageVariants::Arrow($result), + EdgeOwnedEntry::Disk($pattern) => StorageVariants::Disk($result), } }; } -#[cfg(not(feature = "arrow"))] +#[cfg(not(feature = "storage"))] macro_rules! for_all_variants { ($value:expr, $pattern:pat => $result:expr) => { match $value { @@ -52,8 +52,8 @@ impl EdgeOwnedEntry { pub fn as_ref(&self) -> EdgeStorageRef { match self { EdgeOwnedEntry::Mem(entry) => EdgeStorageRef::Mem(entry), - #[cfg(feature = "arrow")] - EdgeOwnedEntry::Arrow(entry) => EdgeStorageRef::Arrow(entry.as_ref()), + #[cfg(feature = "storage")] + EdgeOwnedEntry::Disk(entry) => EdgeStorageRef::Disk(entry.as_ref()), } } } diff --git a/raphtory/src/db/api/storage/edges/edge_ref.rs b/raphtory/src/db/api/storage/edges/edge_ref.rs index fed46c0f62..cf2fa4db53 100644 --- a/raphtory/src/db/api/storage/edges/edge_ref.rs +++ b/raphtory/src/db/api/storage/edges/edge_ref.rs @@ -1,7 +1,7 @@ -#[cfg(feature = "arrow")] -use crate::arrow::storage_interface::edge::ArrowEdge; -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] use crate::db::api::storage::variants::storage_variants::StorageVariants; +#[cfg(feature = "storage")] +use crate::disk_graph::storage_interface::edge::DiskEdge; use crate::{ core::entities::{ edges::{edge_ref::EdgeRef, edge_store::EdgeStore}, @@ -19,23 +19,23 @@ macro_rules! for_all { ($value:expr, $pattern:pat => $result:expr) => { match $value { EdgeStorageRef::Mem($pattern) => $result, - #[cfg(feature = "arrow")] - EdgeStorageRef::Arrow($pattern) => $result, + #[cfg(feature = "storage")] + EdgeStorageRef::Disk($pattern) => $result, } }; } -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] macro_rules! for_all_iter { ($value:expr, $pattern:pat => $result:expr) => { match $value { EdgeStorageRef::Mem($pattern) => StorageVariants::Mem($result), - EdgeStorageRef::Arrow($pattern) => StorageVariants::Arrow($result), + EdgeStorageRef::Disk($pattern) => StorageVariants::Disk($result), } }; } -#[cfg(not(feature = "arrow"))] +#[cfg(not(feature = "storage"))] macro_rules! for_all_iter { ($value:expr, $pattern:pat => $result:expr) => { match $value { @@ -47,8 +47,8 @@ macro_rules! for_all_iter { #[derive(Copy, Clone, Debug)] pub enum EdgeStorageRef<'a> { Mem(&'a EdgeStore), - #[cfg(feature = "arrow")] - Arrow(ArrowEdge<'a>), + #[cfg(feature = "storage")] + Disk(DiskEdge<'a>), } impl<'a> EdgeStorageRef<'a> { @@ -56,8 +56,8 @@ impl<'a> EdgeStorageRef<'a> { pub fn eid(&self) -> EID { match self { EdgeStorageRef::Mem(e) => e.eid, - #[cfg(feature = "arrow")] - EdgeStorageRef::Arrow(e) => e.eid(), + #[cfg(feature = "storage")] + EdgeStorageRef::Disk(e) => e.eid(), } } } diff --git a/raphtory/src/db/api/storage/edges/edge_storage_ops.rs b/raphtory/src/db/api/storage/edges/edge_storage_ops.rs index 76faaaf27b..d862d1cd87 100644 --- a/raphtory/src/db/api/storage/edges/edge_storage_ops.rs +++ b/raphtory/src/db/api/storage/edges/edge_storage_ops.rs @@ -9,8 +9,8 @@ use crate::{ db::api::view::IntoDynBoxed, }; -#[cfg(feature = "arrow")] -use raphtory_arrow::timestamps::TimeStamps; +#[cfg(feature = "storage")] +use pometry_storage::timestamps::TimeStamps; use crate::{ core::entities::properties::tprop::TProp, @@ -23,7 +23,7 @@ use std::ops::Range; pub enum TimeIndexRef<'a> { Ref(&'a TimeIndex), Range(TimeIndexWindow<'a, TimeIndexEntry>), - #[cfg(feature = "arrow")] + #[cfg(feature = "storage")] External(TimeStamps<'a, TimeIndexEntry>), } @@ -32,7 +32,7 @@ impl<'a> TimeIndexRef<'a> { match self { TimeIndexRef::Ref(ts) => ts.len(), TimeIndexRef::Range(ts) => ts.len(), - #[cfg(feature = "arrow")] + #[cfg(feature = "storage")] TimeIndexRef::External(ts) => ts.len(), } } @@ -46,7 +46,7 @@ impl<'a> TimeIndexOps for TimeIndexRef<'a> { match self { TimeIndexRef::Ref(t) => t.active(w), TimeIndexRef::Range(ref t) => t.active(w), - #[cfg(feature = "arrow")] + #[cfg(feature = "storage")] TimeIndexRef::External(ref t) => t.active(w), } } @@ -55,7 +55,7 @@ impl<'a> TimeIndexOps for TimeIndexRef<'a> { match self { TimeIndexRef::Ref(t) => TimeIndexRef::Range(t.range(w)), TimeIndexRef::Range(ref t) => TimeIndexRef::Range(t.range(w)), - #[cfg(feature = "arrow")] + #[cfg(feature = "storage")] TimeIndexRef::External(ref t) => TimeIndexRef::External(t.range(w)), } } @@ -64,7 +64,7 @@ impl<'a> TimeIndexOps for TimeIndexRef<'a> { match self { TimeIndexRef::Ref(t) => t.first(), TimeIndexRef::Range(ref t) => t.first(), - #[cfg(feature = "arrow")] + #[cfg(feature = "storage")] TimeIndexRef::External(ref t) => t.first(), } } @@ -73,7 +73,7 @@ impl<'a> TimeIndexOps for TimeIndexRef<'a> { match self { TimeIndexRef::Ref(t) => t.last(), TimeIndexRef::Range(ref t) => t.last(), - #[cfg(feature = "arrow")] + #[cfg(feature = "storage")] TimeIndexRef::External(ref t) => t.last(), } } @@ -82,7 +82,7 @@ impl<'a> TimeIndexOps for TimeIndexRef<'a> { match self { TimeIndexRef::Ref(t) => t.iter(), TimeIndexRef::Range(t) => t.iter(), - #[cfg(feature = "arrow")] + #[cfg(feature = "storage")] TimeIndexRef::External(ref t) => t.iter(), } } @@ -91,7 +91,7 @@ impl<'a> TimeIndexOps for TimeIndexRef<'a> { match self { TimeIndexRef::Ref(ts) => ts.len(), TimeIndexRef::Range(ts) => ts.len(), - #[cfg(feature = "arrow")] + #[cfg(feature = "storage")] TimeIndexRef::External(ref t) => t.len(), } } @@ -106,15 +106,15 @@ impl<'a> TimeIndexIntoOps for TimeIndexRef<'a> { match self { TimeIndexRef::Ref(t) => TimeIndexRef::Range(t.range_inner(w)), TimeIndexRef::Range(t) => TimeIndexRef::Range(t.into_range(w)), - #[cfg(feature = "arrow")] + #[cfg(feature = "storage")] TimeIndexRef::External(t) => TimeIndexRef::External(t.into_range(w)), } } - fn into_iter(self) -> impl Iterator + Send + 'a { + fn into_iter(self) -> impl Iterator + Send { match self { TimeIndexRef::Ref(t) => t.iter(), TimeIndexRef::Range(t) => t.into_iter().into_dyn_boxed(), - #[cfg(feature = "arrow")] + #[cfg(feature = "storage")] TimeIndexRef::External(t) => t.into_iter().into_dyn_boxed(), } } diff --git a/raphtory/src/db/api/storage/edges/edges.rs b/raphtory/src/db/api/storage/edges/edges.rs index 559c26f7b3..2f3cd6ffe2 100644 --- a/raphtory/src/db/api/storage/edges/edges.rs +++ b/raphtory/src/db/api/storage/edges/edges.rs @@ -6,20 +6,20 @@ use crate::{ db::api::storage::edges::edge_ref::EdgeStorageRef, }; -#[cfg(feature = "arrow")] -use crate::arrow::storage_interface::edges_ref::ArrowEdgesRef; +#[cfg(feature = "storage")] +use crate::disk_graph::storage_interface::edges_ref::DiskEdgesRef; -#[cfg(feature = "arrow")] -use crate::arrow::storage_interface::edges::ArrowEdges; use crate::db::api::storage::edges::edge_storage_ops::EdgeStorageOps; +#[cfg(feature = "storage")] +use crate::disk_graph::storage_interface::edges::DiskEdges; use either::Either; use rayon::iter::ParallelIterator; use std::sync::Arc; pub enum EdgesStorage { Mem(Arc>), - #[cfg(feature = "arrow")] - Arrow(ArrowEdges), + #[cfg(feature = "storage")] + Disk(DiskEdges), } impl EdgesStorage { @@ -27,8 +27,8 @@ impl EdgesStorage { pub fn as_ref(&self) -> EdgesStorageRef { match self { EdgesStorage::Mem(storage) => EdgesStorageRef::Mem(storage), - #[cfg(feature = "arrow")] - EdgesStorage::Arrow(storage) => EdgesStorageRef::Arrow(storage.as_ref()), + #[cfg(feature = "storage")] + EdgesStorage::Disk(storage) => EdgesStorageRef::Disk(storage.as_ref()), } } } @@ -36,12 +36,12 @@ impl EdgesStorage { #[derive(Copy, Clone, Debug)] pub enum EdgesStorageRef<'a> { Mem(&'a ReadLockedStorage), - #[cfg(feature = "arrow")] - Arrow(ArrowEdgesRef<'a>), + #[cfg(feature = "storage")] + Disk(DiskEdgesRef<'a>), } impl<'a> EdgesStorageRef<'a> { - #[cfg(feature = "arrow")] + #[cfg(feature = "storage")] pub fn iter(self, layers: LayerIds) -> impl Iterator> { match self { EdgesStorageRef::Mem(storage) => Either::Left( @@ -50,13 +50,13 @@ impl<'a> EdgesStorageRef<'a> { .filter(move |e| e.has_layer(&layers)) .map(EdgeStorageRef::Mem), ), - EdgesStorageRef::Arrow(storage) => { - Either::Right(storage.iter(layers).map(EdgeStorageRef::Arrow)) + EdgesStorageRef::Disk(storage) => { + Either::Right(storage.iter(layers).map(EdgeStorageRef::Disk)) } } } - #[cfg(not(feature = "arrow"))] + #[cfg(not(feature = "storage"))] pub fn iter(self, layers: LayerIds) -> impl Iterator> { match self { EdgesStorageRef::Mem(storage) => { @@ -70,7 +70,7 @@ impl<'a> EdgesStorageRef<'a> { } } - #[cfg(feature = "arrow")] + #[cfg(feature = "storage")] pub fn par_iter(self, layers: LayerIds) -> impl ParallelIterator> { match self { EdgesStorageRef::Mem(storage) => Either::Left( @@ -79,13 +79,13 @@ impl<'a> EdgesStorageRef<'a> { .filter(move |e| e.has_layer(&layers)) .map(EdgeStorageRef::Mem), ), - EdgesStorageRef::Arrow(storage) => { - Either::Right(storage.par_iter(layers).map(EdgeStorageRef::Arrow)) + EdgesStorageRef::Disk(storage) => { + Either::Right(storage.par_iter(layers).map(EdgeStorageRef::Disk)) } } } - #[cfg(not(feature = "arrow"))] + #[cfg(not(feature = "storage"))] pub fn par_iter(self, layers: LayerIds) -> impl ParallelIterator> { match self { EdgesStorageRef::Mem(storage) => { @@ -107,8 +107,8 @@ impl<'a> EdgesStorageRef<'a> { LayerIds::All => storage.len(), _ => storage.par_iter().filter(|e| e.has_layer(layers)).count(), }, - #[cfg(feature = "arrow")] - EdgesStorageRef::Arrow(storage) => storage.count(layers), + #[cfg(feature = "storage")] + EdgesStorageRef::Disk(storage) => storage.count(layers), } } } diff --git a/raphtory/src/db/api/storage/nodes/node_entry.rs b/raphtory/src/db/api/storage/nodes/node_entry.rs index d699b2b59e..1ff55de43d 100644 --- a/raphtory/src/db/api/storage/nodes/node_entry.rs +++ b/raphtory/src/db/api/storage/nodes/node_entry.rs @@ -1,7 +1,7 @@ -#[cfg(feature = "arrow")] -use crate::arrow::storage_interface::node::ArrowNode; -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] use crate::db::api::storage::variants::storage_variants::StorageVariants; +#[cfg(feature = "storage")] +use crate::disk_graph::storage_interface::node::DiskNode; use crate::{ core::{ entities::{edges::edge_ref::EdgeRef, nodes::node_store::NodeStore, LayerIds, VID}, @@ -19,31 +19,31 @@ use crate::{ pub enum NodeStorageEntry<'a> { Mem(Entry<'a, NodeStore>), - #[cfg(feature = "arrow")] - Arrow(ArrowNode<'a>), + #[cfg(feature = "storage")] + Disk(DiskNode<'a>), } macro_rules! for_all { ($value:expr, $pattern:pat => $result:expr) => { match $value { NodeStorageEntry::Mem($pattern) => $result, - #[cfg(feature = "arrow")] - NodeStorageEntry::Arrow($pattern) => $result, + #[cfg(feature = "storage")] + NodeStorageEntry::Disk($pattern) => $result, } }; } -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] macro_rules! for_all_iter { ($value:expr, $pattern:pat => $result:expr) => {{ match $value { NodeStorageEntry::Mem($pattern) => StorageVariants::Mem($result), - NodeStorageEntry::Arrow($pattern) => StorageVariants::Arrow($result), + NodeStorageEntry::Disk($pattern) => StorageVariants::Disk($result), } }}; } -#[cfg(not(feature = "arrow"))] +#[cfg(not(feature = "storage"))] macro_rules! for_all_iter { ($value:expr, $pattern:pat => $result:expr) => {{ match $value { @@ -56,8 +56,8 @@ impl<'a> NodeStorageEntry<'a> { pub fn as_ref(&self) -> NodeStorageRef { match self { NodeStorageEntry::Mem(entry) => NodeStorageRef::Mem(entry), - #[cfg(feature = "arrow")] - NodeStorageEntry::Arrow(node) => NodeStorageRef::Arrow(*node), + #[cfg(feature = "storage")] + NodeStorageEntry::Disk(node) => NodeStorageRef::Disk(*node), } } } diff --git a/raphtory/src/db/api/storage/nodes/node_owned_entry.rs b/raphtory/src/db/api/storage/nodes/node_owned_entry.rs index 8d16aa3297..64a8bdd6b5 100644 --- a/raphtory/src/db/api/storage/nodes/node_owned_entry.rs +++ b/raphtory/src/db/api/storage/nodes/node_owned_entry.rs @@ -1,7 +1,7 @@ -#[cfg(feature = "arrow")] -use crate::arrow::storage_interface::node::ArrowOwnedNode; -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] use crate::db::api::storage::variants::storage_variants::StorageVariants; +#[cfg(feature = "storage")] +use crate::disk_graph::storage_interface::node::DiskOwnedNode; use crate::{ core::{ entities::{edges::edge_ref::EdgeRef, nodes::node_store::NodeStore, LayerIds, VID}, @@ -22,16 +22,16 @@ use crate::{ pub enum NodeOwnedEntry { Mem(ArcEntry), - #[cfg(feature = "arrow")] - Arrow(ArrowOwnedNode), + #[cfg(feature = "storage")] + Disk(DiskOwnedNode), } impl NodeOwnedEntry { pub fn as_ref(&self) -> NodeStorageRef { match self { NodeOwnedEntry::Mem(entry) => NodeStorageRef::Mem(entry), - #[cfg(feature = "arrow")] - NodeOwnedEntry::Arrow(entry) => NodeStorageRef::Arrow(entry.as_ref()), + #[cfg(feature = "storage")] + NodeOwnedEntry::Disk(entry) => NodeStorageRef::Disk(entry.as_ref()), } } } @@ -40,23 +40,23 @@ macro_rules! for_all { ($value:expr, $pattern:pat => $result:expr) => { match $value { NodeOwnedEntry::Mem($pattern) => $result, - #[cfg(feature = "arrow")] - NodeOwnedEntry::Arrow($pattern) => $result, + #[cfg(feature = "storage")] + NodeOwnedEntry::Disk($pattern) => $result, } }; } -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] macro_rules! for_all_iter { ($value:expr, $pattern:pat => $result:expr) => {{ match $value { NodeOwnedEntry::Mem($pattern) => StorageVariants::Mem($result), - NodeOwnedEntry::Arrow($pattern) => StorageVariants::Arrow($result), + NodeOwnedEntry::Disk($pattern) => StorageVariants::Disk($result), } }}; } -#[cfg(not(feature = "arrow"))] +#[cfg(not(feature = "storage"))] macro_rules! for_all_iter { ($value:expr, $pattern:pat => $result:expr) => {{ match $value { diff --git a/raphtory/src/db/api/storage/nodes/node_ref.rs b/raphtory/src/db/api/storage/nodes/node_ref.rs index 2fa626818e..410cc9028f 100644 --- a/raphtory/src/db/api/storage/nodes/node_ref.rs +++ b/raphtory/src/db/api/storage/nodes/node_ref.rs @@ -1,7 +1,7 @@ -#[cfg(feature = "arrow")] -use crate::arrow::storage_interface::node::ArrowNode; -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] use crate::db::api::storage::variants::storage_variants::StorageVariants; +#[cfg(feature = "storage")] +use crate::disk_graph::storage_interface::node::DiskNode; use crate::{ core::{ entities::{edges::edge_ref::EdgeRef, nodes::node_store::NodeStore, LayerIds, VID}, @@ -16,8 +16,8 @@ use crate::{ #[derive(Copy, Clone, Debug)] pub enum NodeStorageRef<'a> { Mem(&'a NodeStore), - #[cfg(feature = "arrow")] - Arrow(ArrowNode<'a>), + #[cfg(feature = "storage")] + Disk(DiskNode<'a>), } impl<'a> From<&'a NodeStore> for NodeStorageRef<'a> { @@ -26,10 +26,10 @@ impl<'a> From<&'a NodeStore> for NodeStorageRef<'a> { } } -#[cfg(feature = "arrow")] -impl<'a> From> for NodeStorageRef<'a> { - fn from(value: ArrowNode<'a>) -> Self { - NodeStorageRef::Arrow(value) +#[cfg(feature = "storage")] +impl<'a> From> for NodeStorageRef<'a> { + fn from(value: DiskNode<'a>) -> Self { + NodeStorageRef::Disk(value) } } @@ -37,23 +37,23 @@ macro_rules! for_all { ($value:expr, $pattern:pat => $result:expr) => { match $value { NodeStorageRef::Mem($pattern) => $result, - #[cfg(feature = "arrow")] - NodeStorageRef::Arrow($pattern) => $result, + #[cfg(feature = "storage")] + NodeStorageRef::Disk($pattern) => $result, } }; } -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] macro_rules! for_all_iter { ($value:expr, $pattern:pat => $result:expr) => {{ match $value { NodeStorageRef::Mem($pattern) => StorageVariants::Mem($result), - NodeStorageRef::Arrow($pattern) => StorageVariants::Arrow($result), + NodeStorageRef::Disk($pattern) => StorageVariants::Disk($result), } }}; } -#[cfg(not(feature = "arrow"))] +#[cfg(not(feature = "storage"))] macro_rules! for_all_iter { ($value:expr, $pattern:pat => $result:expr) => {{ match $value { diff --git a/raphtory/src/db/api/storage/nodes/nodes.rs b/raphtory/src/db/api/storage/nodes/nodes.rs index 3349092180..dacf9d8e1e 100644 --- a/raphtory/src/db/api/storage/nodes/nodes.rs +++ b/raphtory/src/db/api/storage/nodes/nodes.rs @@ -1,5 +1,5 @@ -#[cfg(feature = "arrow")] -use crate::arrow::storage_interface::nodes::ArrowNodesOwned; +#[cfg(feature = "storage")] +use crate::disk_graph::storage_interface::nodes::DiskNodesOwned; use crate::{ core::{ entities::{nodes::node_store::NodeStore, VID}, @@ -11,24 +11,24 @@ use std::sync::Arc; pub enum NodesStorage { Mem(Arc>), - #[cfg(feature = "arrow")] - Arrow(ArrowNodesOwned), + #[cfg(feature = "storage")] + Disk(DiskNodesOwned), } impl NodesStorage { pub fn as_ref(&self) -> NodesStorageRef { match self { NodesStorage::Mem(storage) => NodesStorageRef::Mem(storage), - #[cfg(feature = "arrow")] - NodesStorage::Arrow(storage) => NodesStorageRef::Arrow(storage.as_ref()), + #[cfg(feature = "storage")] + NodesStorage::Disk(storage) => NodesStorageRef::Disk(storage.as_ref()), } } pub fn node_ref(&self, vid: VID) -> NodeStorageRef { match self { NodesStorage::Mem(storage) => NodeStorageRef::Mem(storage.get(vid)), - #[cfg(feature = "arrow")] - NodesStorage::Arrow(storage) => NodeStorageRef::Arrow(storage.node(vid)), + #[cfg(feature = "storage")] + NodesStorage::Disk(storage) => NodeStorageRef::Disk(storage.node(vid)), } } } diff --git a/raphtory/src/db/api/storage/nodes/nodes_ref.rs b/raphtory/src/db/api/storage/nodes/nodes_ref.rs index e2814e4162..208357533b 100644 --- a/raphtory/src/db/api/storage/nodes/nodes_ref.rs +++ b/raphtory/src/db/api/storage/nodes/nodes_ref.rs @@ -1,7 +1,7 @@ -#[cfg(feature = "arrow")] -use crate::arrow::storage_interface::nodes_ref::ArrowNodesRef; -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] use crate::db::api::storage::variants::storage_variants::StorageVariants; +#[cfg(feature = "storage")] +use crate::disk_graph::storage_interface::nodes_ref::DiskNodesRef; use crate::{ core::{ entities::{nodes::node_store::NodeStore, VID}, @@ -14,21 +14,21 @@ use rayon::iter::ParallelIterator; #[derive(Copy, Clone, Debug)] pub enum NodesStorageRef<'a> { Mem(&'a ReadLockedStorage), - #[cfg(feature = "arrow")] - Arrow(ArrowNodesRef<'a>), + #[cfg(feature = "storage")] + Disk(DiskNodesRef<'a>), } -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] macro_rules! for_all_variants { ($value:expr, $pattern:pat => $result:expr) => { match $value { NodesStorageRef::Mem($pattern) => StorageVariants::Mem($result), - NodesStorageRef::Arrow($pattern) => StorageVariants::Arrow($result), + NodesStorageRef::Disk($pattern) => StorageVariants::Disk($result), } }; } -#[cfg(not(feature = "arrow"))] +#[cfg(not(feature = "storage"))] macro_rules! for_all_variants { ($value:expr, $pattern:pat => $result:expr) => { match $value { @@ -41,8 +41,8 @@ impl<'a> NodesStorageRef<'a> { pub fn node(self, vid: VID) -> NodeStorageRef<'a> { match self { NodesStorageRef::Mem(store) => NodeStorageRef::Mem(store.get(vid)), - #[cfg(feature = "arrow")] - NodesStorageRef::Arrow(store) => NodeStorageRef::Arrow(store.node(vid)), + #[cfg(feature = "storage")] + NodesStorageRef::Disk(store) => NodeStorageRef::Disk(store.node(vid)), } } diff --git a/raphtory/src/db/api/storage/storage_ops.rs b/raphtory/src/db/api/storage/storage_ops.rs index e6e387d2e8..a5612d3c70 100644 --- a/raphtory/src/db/api/storage/storage_ops.rs +++ b/raphtory/src/db/api/storage/storage_ops.rs @@ -31,62 +31,60 @@ use itertools::Itertools; use rayon::prelude::*; use std::iter; -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] use crate::{ - arrow::storage_interface::{ - edges::ArrowEdges, - edges_ref::ArrowEdgesRef, - node::{ArrowNode, ArrowOwnedNode}, - nodes::ArrowNodesOwned, - nodes_ref::ArrowNodesRef, - }, db::api::storage::variants::storage_variants::StorageVariants, + disk_graph::storage_interface::{ + edges::DiskEdges, + edges_ref::DiskEdgesRef, + node::{DiskNode, DiskOwnedNode}, + nodes::DiskNodesOwned, + nodes_ref::DiskNodesRef, + }, }; -#[cfg(feature = "arrow")] -use raphtory_arrow::graph::TemporalGraph; -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] +use pometry_storage::graph::TemporalGraph; +#[cfg(feature = "storage")] use std::sync::Arc; #[derive(Debug, Clone)] pub enum GraphStorage { Mem(LockedGraph), - #[cfg(feature = "arrow")] - Arrow(Arc), + #[cfg(feature = "storage")] + Disk(Arc), } impl GraphStorage { pub fn nodes(&self) -> NodesStorageRef { match self { GraphStorage::Mem(storage) => NodesStorageRef::Mem(&storage.nodes), - #[cfg(feature = "arrow")] - GraphStorage::Arrow(storage) => NodesStorageRef::Arrow(ArrowNodesRef::new(storage)), + #[cfg(feature = "storage")] + GraphStorage::Disk(storage) => NodesStorageRef::Disk(DiskNodesRef::new(storage)), } } pub fn owned_nodes(&self) -> NodesStorage { match self { GraphStorage::Mem(storage) => NodesStorage::Mem(storage.nodes.clone()), - #[cfg(feature = "arrow")] - GraphStorage::Arrow(storage) => { - NodesStorage::Arrow(ArrowNodesOwned::new(storage.clone())) - } + #[cfg(feature = "storage")] + GraphStorage::Disk(storage) => NodesStorage::Disk(DiskNodesOwned::new(storage.clone())), } } pub fn node(&self, vid: VID) -> NodeStorageRef { match self { GraphStorage::Mem(storage) => NodeStorageRef::Mem(storage.nodes.get(vid)), - #[cfg(feature = "arrow")] - GraphStorage::Arrow(storage) => NodeStorageRef::Arrow(ArrowNode::new(storage, vid)), + #[cfg(feature = "storage")] + GraphStorage::Disk(storage) => NodeStorageRef::Disk(DiskNode::new(storage, vid)), } } pub fn owned_node(&self, vid: VID) -> NodeOwnedEntry { match self { GraphStorage::Mem(storage) => NodeOwnedEntry::Mem(storage.nodes.arc_entry(vid)), - #[cfg(feature = "arrow")] - GraphStorage::Arrow(storage) => { - NodeOwnedEntry::Arrow(ArrowOwnedNode::new(storage.clone(), vid)) + #[cfg(feature = "storage")] + GraphStorage::Disk(storage) => { + NodeOwnedEntry::Disk(DiskOwnedNode::new(storage.clone(), vid)) } } } @@ -94,28 +92,28 @@ impl GraphStorage { pub fn edges(&self) -> EdgesStorageRef { match self { GraphStorage::Mem(storage) => EdgesStorageRef::Mem(&storage.edges), - #[cfg(feature = "arrow")] - GraphStorage::Arrow(storage) => EdgesStorageRef::Arrow(ArrowEdgesRef::new(storage)), + #[cfg(feature = "storage")] + GraphStorage::Disk(storage) => EdgesStorageRef::Disk(DiskEdgesRef::new(storage)), } } pub fn owned_edges(&self) -> EdgesStorage { match self { GraphStorage::Mem(storage) => EdgesStorage::Mem(storage.edges.clone()), - #[cfg(feature = "arrow")] - GraphStorage::Arrow(storage) => EdgesStorage::Arrow(ArrowEdges::new(storage)), + #[cfg(feature = "storage")] + GraphStorage::Disk(storage) => EdgesStorage::Disk(DiskEdges::new(storage)), } } pub fn edge(&self, eid: EdgeRef) -> EdgeStorageRef { match self { GraphStorage::Mem(storage) => EdgeStorageRef::Mem(storage.edges.get(eid.pid())), - #[cfg(feature = "arrow")] - GraphStorage::Arrow(storage) => { + #[cfg(feature = "storage")] + GraphStorage::Disk(storage) => { let layer = eid .layer() - .expect("arrow EdgeRefs should always have layer set"); - EdgeStorageRef::Arrow(storage.layers()[*layer].edge(eid.pid())) + .expect("disk_graph EdgeRefs should always have layer set"); + EdgeStorageRef::Disk(storage.layers()[*layer].edge(eid.pid())) } } } @@ -278,17 +276,17 @@ impl GraphStorage { })) } }; - #[cfg(feature = "arrow")] + #[cfg(feature = "storage")] { StorageVariants::Mem(filtered) } - #[cfg(not(feature = "arrow"))] + #[cfg(not(feature = "storage"))] { filtered } } - #[cfg(feature = "arrow")] - EdgesStorage::Arrow(edges) => { + #[cfg(feature = "storage")] + EdgesStorage::Disk(edges) => { let edges_clone = edges.clone(); let iter = edges_clone.into_iter_refs(view.layer_ids().clone()); let filtered = match view.filter_state() { @@ -297,7 +295,7 @@ impl GraphStorage { ), FilterState::Both => { FilterVariants::Both(iter.filter_map(move |(eid, layer_id)| { - let e = EdgeStorageRef::Arrow(edges.get(eid, layer_id)); + let e = EdgeStorageRef::Disk(edges.get(eid, layer_id)); if !view.filter_edge(e, view.layer_ids()) { return None; } @@ -314,7 +312,7 @@ impl GraphStorage { } FilterState::Nodes => { FilterVariants::Nodes(iter.filter_map(move |(eid, layer_id)| { - let e = EdgeStorageRef::Arrow(edges.get(eid, layer_id)); + let e = EdgeStorageRef::Disk(edges.get(eid, layer_id)); let src = nodes.node_ref(e.src()); if !view.filter_node(src, view.layer_ids()) { return None; @@ -328,7 +326,7 @@ impl GraphStorage { } FilterState::Edges | FilterState::BothIndependent => { FilterVariants::Edges(iter.filter_map(move |(eid, layer_id)| { - let e = EdgeStorageRef::Arrow(edges.get(eid, layer_id)); + let e = EdgeStorageRef::Disk(edges.get(eid, layer_id)); if !view.filter_edge(e, view.layer_ids()) { return None; } @@ -336,7 +334,7 @@ impl GraphStorage { })) } }; - StorageVariants::Arrow(filtered) + StorageVariants::Disk(filtered) } } } @@ -404,28 +402,28 @@ impl GraphStorage { })) } }; - #[cfg(feature = "arrow")] + #[cfg(feature = "storage")] { StorageVariants::Mem(filtered) } - #[cfg(not(feature = "arrow"))] + #[cfg(not(feature = "storage"))] { filtered } } - #[cfg(feature = "arrow")] - EdgesStorage::Arrow(edges) => { + #[cfg(feature = "storage")] + EdgesStorage::Disk(edges) => { let edges_clone = edges.clone(); let iter = edges_clone.into_par_iter_refs(view.layer_ids().clone()); let filtered = match view.filter_state() { FilterState::Neither => { FilterVariants::Neither(iter.map(move |(eid, layer_id)| { - EdgeStorageRef::Arrow(edges.get(eid, layer_id)).out_ref() + EdgeStorageRef::Disk(edges.get(eid, layer_id)).out_ref() })) } FilterState::Both => { FilterVariants::Both(iter.filter_map(move |(eid, layer_id)| { - let e = EdgeStorageRef::Arrow(edges.get(eid, layer_id)); + let e = EdgeStorageRef::Disk(edges.get(eid, layer_id)); if !view.filter_edge(e, view.layer_ids()) { return None; } @@ -442,7 +440,7 @@ impl GraphStorage { } FilterState::Nodes => { FilterVariants::Nodes(iter.filter_map(move |(eid, layer_id)| { - let e = EdgeStorageRef::Arrow(edges.get(eid, layer_id)); + let e = EdgeStorageRef::Disk(edges.get(eid, layer_id)); let src = nodes.node_ref(e.src()); if !view.filter_node(src, view.layer_ids()) { return None; @@ -456,7 +454,7 @@ impl GraphStorage { } FilterState::Edges | FilterState::BothIndependent => { FilterVariants::Edges(iter.filter_map(move |(eid, layer_id)| { - let e = EdgeStorageRef::Arrow(edges.get(eid, layer_id)); + let e = EdgeStorageRef::Disk(edges.get(eid, layer_id)); if !view.filter_edge(e, view.layer_ids()) { return None; } @@ -464,7 +462,7 @@ impl GraphStorage { })) } }; - StorageVariants::Arrow(filtered) + StorageVariants::Disk(filtered) } } } diff --git a/raphtory/src/db/api/storage/tprop_storage_ops.rs b/raphtory/src/db/api/storage/tprop_storage_ops.rs index 9563e72414..e29def68b6 100644 --- a/raphtory/src/db/api/storage/tprop_storage_ops.rs +++ b/raphtory/src/db/api/storage/tprop_storage_ops.rs @@ -1,39 +1,39 @@ use crate::core::{entities::properties::tprop::TProp, storage::timeindex::AsTime, Prop}; -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] use crate::db::api::storage::variants::storage_variants::StorageVariants; +#[cfg(feature = "storage")] +use pometry_storage::tprops::DiskTProp; use raphtory_api::core::storage::timeindex::TimeIndexEntry; -#[cfg(feature = "arrow")] -use raphtory_arrow::tprops::ArrowTProp; use std::ops::Range; #[derive(Copy, Clone, Debug)] pub enum TPropRef<'a> { Mem(&'a TProp), - #[cfg(feature = "arrow")] - Arrow(ArrowTProp<'a, TimeIndexEntry>), + #[cfg(feature = "storage")] + Disk(DiskTProp<'a, TimeIndexEntry>), } macro_rules! for_all { ($value:expr, $pattern:pat => $result:expr) => { match $value { TPropRef::Mem($pattern) => $result, - #[cfg(feature = "arrow")] - TPropRef::Arrow($pattern) => $result, + #[cfg(feature = "storage")] + TPropRef::Disk($pattern) => $result, } }; } -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] macro_rules! for_all_variants { ($value:expr, $pattern:pat => $result:expr) => { match $value { TPropRef::Mem($pattern) => StorageVariants::Mem($result), - TPropRef::Arrow($pattern) => StorageVariants::Arrow($result), + TPropRef::Disk($pattern) => StorageVariants::Disk($result), } }; } -#[cfg(not(feature = "arrow"))] +#[cfg(not(feature = "storage"))] macro_rules! for_all_variants { ($value:expr, $pattern:pat => $result:expr) => { match $value { diff --git a/raphtory/src/db/api/storage/variants/storage_variants.rs b/raphtory/src/db/api/storage/variants/storage_variants.rs index ef761b7412..4d5edb4461 100644 --- a/raphtory/src/db/api/storage/variants/storage_variants.rs +++ b/raphtory/src/db/api/storage/variants/storage_variants.rs @@ -7,22 +7,22 @@ use rayon::iter::{ use std::{cmp::Ordering, ops::Range}; #[derive(Copy, Clone, Debug)] -pub enum StorageVariants { +pub enum StorageVariants { Mem(Mem), - #[cfg(feature = "arrow")] - Arrow(Arrow), + #[cfg(feature = "storage")] + Disk(Disk), } -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] macro_rules! SelfType { - ($Mem:ident, $Arrow:ident) => { - StorageVariants<$Mem, $Arrow> + ($Mem:ident, $Disk:ident) => { + StorageVariants<$Mem, $Disk> }; } -#[cfg(not(feature = "arrow"))] +#[cfg(not(feature = "storage"))] macro_rules! SelfType { - ($Mem:ident, $Arrow:ident) => { + ($Mem:ident, $Disk:ident) => { StorageVariants<$Mem> }; } @@ -31,23 +31,23 @@ macro_rules! for_all { ($value:expr, $pattern:pat => $result:expr) => { match $value { StorageVariants::Mem($pattern) => $result, - #[cfg(feature = "arrow")] - StorageVariants::Arrow($pattern) => $result, + #[cfg(feature = "storage")] + StorageVariants::Disk($pattern) => $result, } }; } -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] macro_rules! for_all_iter { ($value:expr, $pattern:pat => $result:expr) => { match $value { StorageVariants::Mem($pattern) => StorageVariants::Mem($result), - StorageVariants::Arrow($pattern) => StorageVariants::Arrow($result), + StorageVariants::Disk($pattern) => StorageVariants::Disk($result), } }; } -#[cfg(not(feature = "arrow"))] +#[cfg(not(feature = "storage"))] macro_rules! for_all_iter { ($value:expr, $pattern:pat => $result:expr) => { match $value { @@ -56,8 +56,8 @@ macro_rules! for_all_iter { }; } -impl, #[cfg(feature = "arrow")] Arrow: Iterator> Iterator - for SelfType!(Mem, Arrow) +impl, #[cfg(feature = "storage")] Disk: Iterator> Iterator + for SelfType!(Mem, Disk) { type Item = V; @@ -178,8 +178,8 @@ impl, #[cfg(feature = "arrow")] Arrow: Iterator, - #[cfg(feature = "arrow")] Arrow: DoubleEndedIterator, - > DoubleEndedIterator for SelfType!(Mem, Arrow) + #[cfg(feature = "storage")] Disk: DoubleEndedIterator, + > DoubleEndedIterator for SelfType!(Mem, Disk) { fn next_back(&mut self) -> Option { for_all!(self, iter => iter.next_back()) @@ -209,8 +209,8 @@ impl< impl< V, Mem: ExactSizeIterator, - #[cfg(feature = "arrow")] Arrow: ExactSizeIterator, - > ExactSizeIterator for SelfType!(Mem, Arrow) + #[cfg(feature = "storage")] Disk: ExactSizeIterator, + > ExactSizeIterator for SelfType!(Mem, Disk) { fn len(&self) -> usize { for_all!(self, iter => iter.len()) @@ -220,8 +220,8 @@ impl< impl< V: Send, Mem: ParallelIterator, - #[cfg(feature = "arrow")] Arrow: ParallelIterator, - > ParallelIterator for SelfType!(Mem, Arrow) + #[cfg(feature = "storage")] Disk: ParallelIterator, + > ParallelIterator for SelfType!(Mem, Disk) { type Item = V; @@ -240,8 +240,8 @@ impl< impl< V: Send, Mem: IndexedParallelIterator, - #[cfg(feature = "arrow")] Arrow: IndexedParallelIterator, - > IndexedParallelIterator for SelfType!(Mem, Arrow) + #[cfg(feature = "storage")] Disk: IndexedParallelIterator, + > IndexedParallelIterator for SelfType!(Mem, Disk) { fn len(&self) -> usize { for_all!(self, iter => iter.len()) @@ -256,8 +256,8 @@ impl< } } -impl<'a, Mem: TPropOps<'a> + 'a, #[cfg(feature = "arrow")] Arrow: TPropOps<'a> + 'a> TPropOps<'a> - for SelfType!(Mem, Arrow) +impl<'a, Mem: TPropOps<'a> + 'a, #[cfg(feature = "storage")] Disk: TPropOps<'a> + 'a> TPropOps<'a> + for SelfType!(Mem, Disk) { fn last_before(self, t: i64) -> Option<(TimeIndexEntry, Prop)> { for_all!(self, props => props.last_before(t)) diff --git a/raphtory/src/db/api/view/edge.rs b/raphtory/src/db/api/view/edge.rs index 2da1b63438..966f74c68e 100644 --- a/raphtory/src/db/api/view/edge.rs +++ b/raphtory/src/db/api/view/edge.rs @@ -307,8 +307,8 @@ mod test_edge_view { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G, actual_prop_values: &[i32]) { let prop_values: Vec<_> = graph @@ -321,8 +321,8 @@ mod test_edge_view { assert_eq!(prop_values, actual_prop_values) } test(&graph, &actual_prop_values); - #[cfg(feature = "arrow")] - test(&arrow_graph, &actual_prop_values); + #[cfg(feature = "storage")] + test(&disk_graph, &actual_prop_values); } #[test] @@ -338,8 +338,8 @@ mod test_edge_view { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test( graph: &G, @@ -366,8 +366,8 @@ mod test_edge_view { assert_eq!(prop_values, actual_prop_values_1) } test(&graph, &actual_prop_values_0, &actual_prop_values_1); - #[cfg(feature = "arrow")] - test(&arrow_graph, &actual_prop_values_0, &actual_prop_values_1); + #[cfg(feature = "storage")] + test(&disk_graph, &actual_prop_values_0, &actual_prop_values_1); } #[test] @@ -381,8 +381,8 @@ mod test_edge_view { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G, expected_prop_values: &[i32]) { let prop_values: Vec<_> = graph @@ -446,7 +446,7 @@ mod test_edge_view { } test(&graph, &expected_prop_values); // FIXME: Needs multilayer support (Issue #47) - // test(&arrow_graph, &expected_prop_values); + // test(&disk_graph, &expected_prop_values); } #[test] @@ -458,8 +458,8 @@ mod test_edge_view { graph.add_edge(0, 2, 3, [("second", true)], None).unwrap(); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let mut exploded_edges: Vec<_> = graph.edges().explode().iter().collect(); @@ -487,6 +487,6 @@ mod test_edge_view { } test(&graph); // FIXME: boolean properties not supported yet (Issue #48) - // test(&arrow_graph); + // test(&disk_graph); } } diff --git a/raphtory/src/db/api/view/graph.rs b/raphtory/src/db/api/view/graph.rs index 84cf1d2a6d..705cb22c9d 100644 --- a/raphtory/src/db/api/view/graph.rs +++ b/raphtory/src/db/api/view/graph.rs @@ -477,15 +477,15 @@ mod test_exploded_edges { graph.add_edge(3, 0, 1, NO_PROPS, None).unwrap(); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { assert_eq!(graph.count_temporal_edges(), 4) } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } } @@ -572,8 +572,8 @@ mod test_materialize { graph.add_node(0, "A", NO_PROPS, None).unwrap(); graph.add_node(1, "B", NO_PROPS, Some("H")).unwrap(); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let node_a = graph.node("A").unwrap(); let node_b = graph.node("B").unwrap(); @@ -585,7 +585,7 @@ mod test_materialize { } test(&graph); // FIXME: Node types not yet supported (Issue #51) - // test(&arrow_graph); + // test(&disk_graph); // Nodes with No type can be overwritten let node_a = graph.add_node(1, "A", NO_PROPS, Some("TYPEA")).unwrap(); diff --git a/raphtory/src/db/api/view/internal/core_ops.rs b/raphtory/src/db/api/view/internal/core_ops.rs index 10bad849e4..e595d94eb3 100644 --- a/raphtory/src/db/api/view/internal/core_ops.rs +++ b/raphtory/src/db/api/view/internal/core_ops.rs @@ -28,9 +28,9 @@ use crate::{ use enum_dispatch::enum_dispatch; use std::ops::Range; -#[cfg(feature = "arrow")] -use raphtory_arrow::timestamps::TimeStamps; -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] +use pometry_storage::timestamps::TimeStamps; +#[cfg(feature = "storage")] use rayon::prelude::*; /// Core functions that should (almost-)always be implemented by pointing at the underlying graph. @@ -372,7 +372,7 @@ impl CoreGraphOps for G { pub enum NodeAdditions<'a> { Mem(&'a TimeIndex), Range(TimeIndexWindow<'a, i64>), - #[cfg(feature = "arrow")] + #[cfg(feature = "storage")] Col(Vec>), } @@ -383,7 +383,7 @@ impl<'b> TimeIndexOps for NodeAdditions<'b> { fn active(&self, w: Range) -> bool { match self { NodeAdditions::Mem(index) => index.active_t(w), - #[cfg(feature = "arrow")] + #[cfg(feature = "storage")] NodeAdditions::Col(index) => index.par_iter().any(|index| index.active_t(w.clone())), NodeAdditions::Range(index) => index.active_t(w), } @@ -392,7 +392,7 @@ impl<'b> TimeIndexOps for NodeAdditions<'b> { fn range(&self, w: Range) -> Self::RangeType<'_> { match self { NodeAdditions::Mem(index) => NodeAdditions::Range(index.range(w)), - #[cfg(feature = "arrow")] + #[cfg(feature = "storage")] NodeAdditions::Col(index) => { let mut ranges = Vec::with_capacity(index.len()); index @@ -408,7 +408,7 @@ impl<'b> TimeIndexOps for NodeAdditions<'b> { fn first(&self) -> Option { match self { NodeAdditions::Mem(index) => index.first(), - #[cfg(feature = "arrow")] + #[cfg(feature = "storage")] NodeAdditions::Col(index) => index.par_iter().flat_map(|index| index.first()).min(), NodeAdditions::Range(index) => index.first(), } @@ -417,7 +417,7 @@ impl<'b> TimeIndexOps for NodeAdditions<'b> { fn last(&self) -> Option { match self { NodeAdditions::Mem(index) => index.last(), - #[cfg(feature = "arrow")] + #[cfg(feature = "storage")] NodeAdditions::Col(index) => index.par_iter().flat_map(|index| index.last()).max(), NodeAdditions::Range(index) => index.last(), } @@ -426,7 +426,7 @@ impl<'b> TimeIndexOps for NodeAdditions<'b> { fn iter(&self) -> Box + Send + '_> { match self { NodeAdditions::Mem(index) => index.iter(), - #[cfg(feature = "arrow")] + #[cfg(feature = "storage")] NodeAdditions::Col(index) => Box::new(index.iter().flat_map(|index| index.iter())), NodeAdditions::Range(index) => index.iter(), } @@ -436,7 +436,7 @@ impl<'b> TimeIndexOps for NodeAdditions<'b> { match self { NodeAdditions::Mem(index) => index.len(), NodeAdditions::Range(range) => range.len(), - #[cfg(feature = "arrow")] + #[cfg(feature = "storage")] NodeAdditions::Col(col) => col.len(), } } diff --git a/raphtory/src/db/api/view/internal/materialize.rs b/raphtory/src/db/api/view/internal/materialize.rs index 4eaa7ad9c3..d36e3686f8 100644 --- a/raphtory/src/db/api/view/internal/materialize.rs +++ b/raphtory/src/db/api/view/internal/materialize.rs @@ -40,8 +40,8 @@ use enum_dispatch::enum_dispatch; use serde::{de::Error, Deserialize, Deserializer, Serialize}; use std::path::Path; -#[cfg(feature = "arrow")] -use crate::arrow::graph_impl::ArrowGraph; +#[cfg(feature = "storage")] +use crate::disk_graph::graph_impl::DiskGraph; #[enum_dispatch(CoreGraphOps)] #[enum_dispatch(InternalLayerOps)] @@ -59,8 +59,8 @@ use crate::arrow::graph_impl::ArrowGraph; pub enum MaterializedGraph { EventGraph(Graph), PersistentGraph(PersistentGraph), - #[cfg(feature = "arrow")] - ArrowEventGraph(ArrowGraph), + #[cfg(feature = "storage")] + DiskEventGraph(DiskGraph), } fn version_deserialize<'de, D>(deserializer: D) -> Result @@ -91,26 +91,26 @@ impl MaterializedGraph { match self { MaterializedGraph::EventGraph(g) => Some(g), MaterializedGraph::PersistentGraph(_) => None, - #[cfg(feature = "arrow")] - MaterializedGraph::ArrowEventGraph(_) => None, + #[cfg(feature = "storage")] + MaterializedGraph::DiskEventGraph(_) => None, } } pub fn into_persistent(self) -> Option { match self { MaterializedGraph::EventGraph(_) => None, MaterializedGraph::PersistentGraph(g) => Some(g), - #[cfg(feature = "arrow")] - MaterializedGraph::ArrowEventGraph(_) => None, + #[cfg(feature = "storage")] + MaterializedGraph::DiskEventGraph(_) => None, } } - #[cfg(feature = "arrow")] - pub fn into_arrow(self) -> Option { + #[cfg(feature = "storage")] + pub fn into_disk_graph(self) -> Option { match self { MaterializedGraph::EventGraph(_) => None, MaterializedGraph::PersistentGraph(_) => None, - #[cfg(feature = "arrow")] - MaterializedGraph::ArrowEventGraph(g) => Some(g), + #[cfg(feature = "storage")] + MaterializedGraph::DiskEventGraph(g) => Some(g), } } diff --git a/raphtory/src/db/api/view/time.rs b/raphtory/src/db/api/view/time.rs index 5412053b9f..dd110672c2 100644 --- a/raphtory/src/db/api/view/time.rs +++ b/raphtory/src/db/api/view/time.rs @@ -335,8 +335,8 @@ mod time_tests { fn rolling() { let graph = graph_with_timeline(1, 7); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test1(graph: &G) { let windows = graph.rolling(2, None).unwrap(); @@ -344,13 +344,13 @@ mod time_tests { assert_bounds(windows, expected); } test1(&graph); - #[cfg(feature = "arrow")] - test1(&arrow_graph); + #[cfg(feature = "storage")] + test1(&disk_graph); let graph = graph_with_timeline(1, 6); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test2(graph: &G) { let windows = graph.rolling(3, Some(2)).unwrap(); @@ -358,13 +358,13 @@ mod time_tests { assert_bounds(windows, expected.clone()); } test2(&graph); - #[cfg(feature = "arrow")] - test2(&arrow_graph); + #[cfg(feature = "storage")] + test2(&disk_graph); let graph = graph_with_timeline(0, 9); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test3(graph: &G) { let windows = graph.window(1, 6).rolling(3, Some(2)).unwrap(); assert_bounds( @@ -373,16 +373,16 @@ mod time_tests { ); } test3(&graph); - #[cfg(feature = "arrow")] - test3(&arrow_graph); + #[cfg(feature = "storage")] + test3(&disk_graph); } #[test] fn expanding() { let graph = graph_with_timeline(1, 7); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test1(graph: &G) { let windows = graph.expanding(2).unwrap(); @@ -390,13 +390,13 @@ mod time_tests { assert_bounds(windows, expected); } test1(&graph); - #[cfg(feature = "arrow")] - test1(&arrow_graph); + #[cfg(feature = "storage")] + test1(&disk_graph); let graph = graph_with_timeline(1, 6); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test2(graph: &G) { let windows = graph.expanding(2).unwrap(); @@ -404,13 +404,13 @@ mod time_tests { assert_bounds(windows, expected.clone()); } test2(&graph); - #[cfg(feature = "arrow")] - test2(&arrow_graph); + #[cfg(feature = "storage")] + test2(&disk_graph); let graph = graph_with_timeline(0, 9); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test3(graph: &G) { let windows = graph.window(1, 6).expanding(2).unwrap(); @@ -420,8 +420,8 @@ mod time_tests { ); } test3(&graph); - #[cfg(feature = "arrow")] - test3(&arrow_graph); + #[cfg(feature = "storage")] + test3(&disk_graph); } #[test] @@ -430,8 +430,8 @@ mod time_tests { let end = "2020-06-07 23:59:59.999".try_into_time().unwrap(); let graph = graph_with_timeline(start, end); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test1(graph: &G) { let windows = graph.rolling("1 day", None).unwrap(); @@ -448,15 +448,15 @@ mod time_tests { assert_bounds(windows, expected); } test1(&graph); - #[cfg(feature = "arrow")] - test1(&arrow_graph); + #[cfg(feature = "storage")] + test1(&disk_graph); let start = "2020-06-06 00:00:00".try_into_time().unwrap(); let end = "2020-06-08 00:00:00".try_into_time().unwrap(); let graph = graph_with_timeline(start, end); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test2(graph: &G) { let windows = graph.rolling("1 day", None).unwrap(); @@ -473,8 +473,8 @@ mod time_tests { assert_bounds(windows, expected); } test2(&graph); - #[cfg(feature = "arrow")] - test2(&arrow_graph); + #[cfg(feature = "storage")] + test2(&disk_graph); // TODO: turn this back on if we bring bach epoch alignment for unwindowed graphs // let start = "2020-06-05 23:59:59.999".into_time().unwrap(); @@ -500,8 +500,8 @@ mod time_tests { let end = "2020-06-07 23:59:59.999".try_into_time().unwrap(); let graph = graph_with_timeline(start, end); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test1(graph: &G) { let windows = graph.expanding("1 day").unwrap(); @@ -512,15 +512,15 @@ mod time_tests { assert_bounds(windows, expected); } test1(&graph); - #[cfg(feature = "arrow")] - test1(&arrow_graph); + #[cfg(feature = "storage")] + test1(&disk_graph); let start = "2020-06-06 00:00:00".try_into_time().unwrap(); let end = "2020-06-08 00:00:00".try_into_time().unwrap(); let graph = graph_with_timeline(start, end); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test2(graph: &G) { let windows = graph.expanding("1 day").unwrap(); @@ -531,7 +531,7 @@ mod time_tests { assert_bounds(windows, expected); } test2(&graph); - #[cfg(feature = "arrow")] - test2(&arrow_graph); + #[cfg(feature = "storage")] + test2(&disk_graph); } } diff --git a/raphtory/src/db/graph/edge.rs b/raphtory/src/db/graph/edge.rs index 86d3a61662..fe022665ae 100644 --- a/raphtory/src/db/graph/edge.rs +++ b/raphtory/src/db/graph/edge.rs @@ -419,8 +419,8 @@ mod test_edge { graph.add_edge(2, 1, 2, props.clone(), None).unwrap(); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G, props: [(ArcStr, Prop); 1]) { let e1 = graph.edge(1, 2).unwrap(); @@ -429,8 +429,8 @@ mod test_edge { assert!(e1_w.properties().as_vec().is_empty()) } test(&graph, props.clone()); - #[cfg(feature = "arrow")] - test(&arrow_graph, props); + #[cfg(feature = "storage")] + test(&disk_graph, props); } #[test] @@ -448,8 +448,8 @@ mod test_edge { .unwrap(); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { assert_eq!( @@ -481,7 +481,7 @@ mod test_edge { } test(&graph); // FIXME: multilayer edge views are not supported yet (Issue #47) - // test(&arrow_graph); + // test(&disk_graph); } #[test] diff --git a/raphtory/src/db/graph/graph.rs b/raphtory/src/db/graph/graph.rs index bb4bfb116e..e1ef581d33 100644 --- a/raphtory/src/db/graph/graph.rs +++ b/raphtory/src/db/graph/graph.rs @@ -209,9 +209,7 @@ mod db_tests { time::internal::InternalTimeOps, EdgeViewOps, Layer, LayerOps, NodeViewOps, StaticGraphViewOps, TimeOps, }, - graph::{ - edge::EdgeView, edges::Edges, node::NodeView, nodes::Nodes, path::PathFromNode, - }, + graph::{edge::EdgeView, edges::Edges, node::NodeView, path::PathFromNode}, }, graphgen::random_attachment::random_attachment, prelude::{AdditionOps, PropertyAdditionOps}, @@ -229,8 +227,8 @@ mod db_tests { let graph = Graph::new(); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { assert!(!graph.has_edge(1, 2)); @@ -291,8 +289,8 @@ mod db_tests { assert!(graph.earliest_time_global().is_none()); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[quickcheck] @@ -581,8 +579,8 @@ mod db_tests { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let e = graph @@ -595,8 +593,8 @@ mod db_tests { assert_eq!(e.dst().id(), 3u64); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -616,8 +614,8 @@ mod db_tests { graph.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); } let test_dir = tempfile::TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let expected = vec![(2, 3, 1), (1, 0, 0), (1, 0, 0)]; @@ -635,8 +633,8 @@ mod db_tests { assert_eq!(actual, expected); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -657,8 +655,8 @@ mod db_tests { } let test_dir = tempfile::TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let expected = vec![(2, 3, 1), (1, 0, 0), (1, 0, 0)]; @@ -676,8 +674,8 @@ mod db_tests { assert_eq!(actual, expected); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -889,8 +887,8 @@ mod db_tests { .unwrap(); let test_dir = tempfile::TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let wg = graph.window(3, 15); @@ -921,7 +919,7 @@ mod db_tests { } test(&graph); // FIXME: boolean properties not yet supported (Issue #48) - // test(&arrow_graph); + // test(&disk_graph); } #[test] @@ -933,8 +931,8 @@ mod db_tests { .expect("add edge"); let test_dir = tempfile::TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let e = graph.edge(0, 1).unwrap(); @@ -943,8 +941,8 @@ mod db_tests { assert_eq!(prop, Prop::U32(5)); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -965,8 +963,8 @@ mod db_tests { } let test_dir = tempfile::TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let expected = vec![ @@ -988,8 +986,8 @@ mod db_tests { assert_eq!(actual, expected); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -997,8 +995,8 @@ mod db_tests { let graph = Graph::new(); let test_dir = tempfile::TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let rolling = graph.rolling(1, None).unwrap().collect_vec(); @@ -1008,8 +1006,8 @@ mod db_tests { assert!(expanding.is_empty()); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -1021,8 +1019,8 @@ mod db_tests { graph.add_node(1, 831, NO_PROPS, None).unwrap(); let test_dir = tempfile::TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { assert!(graph.has_node(831)); @@ -1032,8 +1030,8 @@ mod db_tests { assert_eq!(graph.count_nodes(), 3); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -1047,8 +1045,8 @@ mod db_tests { graph.add_edge(0, 11, 44, NO_PROPS, Some("layer2"))?; let test_dir = tempfile::TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { assert!(graph.has_edge(11, 22)); @@ -1162,7 +1160,7 @@ mod db_tests { } test(&graph); // FIXME: needs multilayer support (Issue #47) - // test(&arrow_graph); + // test(&disk_graph); Ok(()) } @@ -1197,8 +1195,8 @@ mod db_tests { .unwrap(); let test_dir = tempfile::TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let exploded = graph.edge(1, 2).unwrap().explode(); @@ -1223,8 +1221,8 @@ mod db_tests { assert_eq!(e, expected); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -1238,8 +1236,8 @@ mod db_tests { graph.add_edge(2, 1, 3, NO_PROPS, None).unwrap(); let test_dir = tempfile::TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let mut res = graph.edge(1, 2).unwrap().earliest_time().unwrap(); @@ -1345,8 +1343,8 @@ mod db_tests { assert_eq!(res_list, vec![2, 2]); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -1365,8 +1363,8 @@ mod db_tests { graph.add_node(8, "Lord Farquaad", NO_PROPS, None).unwrap(); let test_dir = tempfile::TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let times_of_one = graph.node(1).unwrap().history(); @@ -1383,8 +1381,8 @@ mod db_tests { assert_eq!(windowed_times_of_farquaad, [4, 6, 7]); } test(&graph); - // FIXME: Node updates without properties or edges are currently not supported in arrow (see issue #46) - // test(&arrow_graph); + // FIXME: Node updates without properties or edges are currently not supported in disk_graph (see issue #46) + // test(&disk_graph); } #[test] @@ -1397,8 +1395,8 @@ mod db_tests { graph.add_edge(4, 1, 4, NO_PROPS, None).unwrap(); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let times_of_onetwo = graph.edge(1, 2).unwrap().history(); @@ -1411,8 +1409,8 @@ mod db_tests { assert!(windowed_times_of_four.is_empty()); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -1431,8 +1429,8 @@ mod db_tests { graph.add_edge(10, 1, 4, NO_PROPS, None).unwrap(); let test_dir = tempfile::TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let times_of_onetwo = graph.edge(1, 2).unwrap().history(); @@ -1452,8 +1450,8 @@ mod db_tests { assert_eq!(windowed_times_of_four_higher, [8, 9, 10]); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -1477,8 +1475,8 @@ mod db_tests { graph.add_node(8, "Lord Farquaad", NO_PROPS, None).unwrap(); let test_dir = tempfile::TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let times_of_one = graph.node(1).unwrap().history(); @@ -1500,7 +1498,7 @@ mod db_tests { } test(&graph); // FIXME: Issue #46 - // test(&arrow_graph); + // test(&disk_graph); } #[derive(Debug)] @@ -1744,8 +1742,8 @@ mod db_tests { .unwrap(); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let e = graph.node(1).unwrap().out_edges().iter().next().unwrap(); @@ -1765,8 +1763,8 @@ mod db_tests { assert_eq!(res, exp); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -1777,8 +1775,8 @@ mod db_tests { graph.add_node(3, 1, NO_PROPS, None).unwrap(); let test_dir = tempfile::TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { assert_eq!(graph.node(1).unwrap().earliest_time(), Some(1)); @@ -1795,7 +1793,7 @@ mod db_tests { } test(&graph); // FIXME: Node add without properties not showing up (Issue #46) - // test(&arrow_graph); + // test(&disk_graph); } #[test] @@ -1806,8 +1804,8 @@ mod db_tests { graph.add_node(2, 3, NO_PROPS, None).unwrap(); let test_dir = tempfile::TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { assert_eq!(graph.nodes().id().collect::>(), vec![1, 2, 3]); @@ -1817,7 +1815,7 @@ mod db_tests { } test(&graph); // FIXME: Node add without properties not showing up (Issue #46) - // test(&arrow_graph); + // test(&disk_graph); } #[test] @@ -1827,8 +1825,8 @@ mod db_tests { graph.add_edge(0, 0, 1, NO_PROPS, Some("awesome name"))?; let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let what = graph.edges().id().collect_vec(); @@ -1839,7 +1837,7 @@ mod db_tests { } test(&graph); // FIXME: Needs multilayer support (Issue #47) - // test(&arrow_graph); + // test(&disk_graph); Ok(()) } @@ -1849,8 +1847,8 @@ mod db_tests { graph.add_edge(0, 1, 2, NO_PROPS, Some("layer")).unwrap(); let test_dir = tempfile::TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { assert!(graph.edge(1, 2).is_some()); @@ -1858,7 +1856,7 @@ mod db_tests { } test(&graph); // FIXME: Needs multilayer support (Issue #47) - // test(&arrow_graph); + // test(&disk_graph); } #[test] @@ -1874,8 +1872,8 @@ mod db_tests { graph.add_edge(1, 1, 4, NO_PROPS, None).expect("add edge"); let test_dir = tempfile::TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let g_layers = graph.layers(vec!["layer1", "layer3"]).expect("layer"); @@ -1906,7 +1904,7 @@ mod db_tests { } test(&graph); // FIXME: Needs multilayer support (Issue #47) - // test(&arrow_graph); + // test(&disk_graph); } #[test] @@ -1920,8 +1918,8 @@ mod db_tests { } let test_dir = tempfile::TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let windowed_graph = graph.window(0, 5); @@ -1934,8 +1932,8 @@ mod db_tests { assert_eq!(ns_win, ns); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -1947,8 +1945,8 @@ mod db_tests { graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); let test_dir = tempfile::TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let e = graph.edge(1, 2).expect("edge"); @@ -1968,7 +1966,7 @@ mod db_tests { } test(&graph); // FIXME: Needs multilayer support (Issue #47) - // test(&arrow_graph); + // test(&disk_graph); } #[test] @@ -1980,8 +1978,8 @@ mod db_tests { graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); let test_dir = tempfile::TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let g = graph.window(0, 3); @@ -2002,7 +2000,7 @@ mod db_tests { } test(&graph); // FIXME: Needs multilayer support (Issue #47) - // test(&arrow_graph); + // test(&disk_graph); } #[test] @@ -2014,8 +2012,8 @@ mod db_tests { graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); let test_dir = tempfile::TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let e = graph.edge(1, 2).expect("edge"); @@ -2040,7 +2038,7 @@ mod db_tests { } test(&graph); // FIXME: Needs multilayer support (Issue #47) - // test(&arrow_graph); + // test(&disk_graph); } #[test] @@ -2052,8 +2050,8 @@ mod db_tests { graph.add_edge(3, 1, 2, NO_PROPS, None).unwrap(); let test_dir = tempfile::TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let g = graph.window(0, 3); @@ -2079,7 +2077,7 @@ mod db_tests { } test(&graph); // FIXME: Needs multilayer support (Issue #47) - // test(&arrow_graph); + // test(&disk_graph); } #[test] @@ -2097,8 +2095,8 @@ mod db_tests { .expect("failed"); let test_dir = tempfile::TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let e = graph.edge(1, 2).expect("failed to get edge"); @@ -2175,7 +2173,7 @@ mod db_tests { } test(&graph); // FIXME: Needs multilayer support (Issue #47) - // test(&arrow_graph); + // test(&disk_graph); } #[test] @@ -2184,8 +2182,8 @@ mod db_tests { graph.add_edge(0, 1, 2, NO_PROPS, Some("layer1")).unwrap(); graph.add_edge(0, 1, 2, NO_PROPS, Some("layer2")).unwrap(); let test_dir = tempfile::TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { assert_eq!( @@ -2198,8 +2196,8 @@ mod db_tests { ) } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } //TODO this needs to be fixed as part of the algorithm result switch to returning noderefs // #[quickcheck] @@ -2316,8 +2314,8 @@ mod db_tests { graph.add_edge(0, 1, 3, [("layer", 2)], Some("2")).unwrap(); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let v = graph.node(1).unwrap(); @@ -2375,8 +2373,8 @@ mod db_tests { assert!(out_out_2.is_empty()); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); fn test2(graph: &G) { let v = graph.node(1).unwrap(); @@ -2413,7 +2411,7 @@ mod db_tests { test2(&graph); // FIXME: requires multilayer edge view (Issue #47) - // test2(&arrow_graph); + // test2(&disk_graph); } #[test] @@ -2426,8 +2424,8 @@ mod db_tests { graph.add_edge(0, 1, 3, [("layer", 2)], Some("2")).unwrap(); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let wl = graph.window(0, 3).layers(vec!["1", "2"]).unwrap(); @@ -2438,7 +2436,7 @@ mod db_tests { } test(&graph); // FIXME: Requires mutlilayer edge views - // test(&arrow_graph); + // test(&disk_graph); } #[test] @@ -2471,8 +2469,8 @@ mod db_tests { .unwrap(); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { assert_eq!( @@ -2486,7 +2484,7 @@ mod db_tests { } test(&graph); // FIXME: Needs multilayer support (Issue #47) - // test(&arrow_graph); + // test(&disk_graph); } #[test] diff --git a/raphtory/src/db/graph/mod.rs b/raphtory/src/db/graph/mod.rs index 7589337fc5..664b8f1709 100644 --- a/raphtory/src/db/graph/mod.rs +++ b/raphtory/src/db/graph/mod.rs @@ -1,5 +1,4 @@ use crate::core::entities::properties::props::DictMapper; -use itertools::Itertools; use std::sync::Arc; pub mod edge; diff --git a/raphtory/src/db/graph/node.rs b/raphtory/src/db/graph/node.rs index da7329fe7b..75dbde35ed 100644 --- a/raphtory/src/db/graph/node.rs +++ b/raphtory/src/db/graph/node.rs @@ -386,8 +386,8 @@ mod node_test { graph.add_node(2, 1, NO_PROPS, None).unwrap(); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let view = graph.before(2); @@ -412,7 +412,7 @@ mod node_test { } test(&graph); // FIXME: Node add without properties not showing up (Issue #46) - // test(&arrow_graph); + // test(&disk_graph); } #[test] @@ -423,8 +423,8 @@ mod node_test { graph.add_node(2, 1, props, None).unwrap(); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let v1 = graph.node(1).unwrap(); @@ -437,7 +437,7 @@ mod node_test { } test(&graph); // FIXME: Node add without properties not showing up (Issue #46) - // test(&arrow_graph); + // test(&disk_graph); } #[test] diff --git a/raphtory/src/db/graph/views/layer_graph.rs b/raphtory/src/db/graph/views/layer_graph.rs index 5f1bf55a03..e9671f2b23 100644 --- a/raphtory/src/db/graph/views/layer_graph.rs +++ b/raphtory/src/db/graph/views/layer_graph.rs @@ -142,8 +142,8 @@ mod test_layers { graph.add_edge(3, 2, 4, NO_PROPS, Some("layer1")).unwrap(); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let neighbours = graph @@ -198,7 +198,7 @@ mod test_layers { } test(&graph); // FIXME: Needs multilayer support (Issue #47) - // test(&arrow_graph); + // test(&disk_graph); } #[test] @@ -212,8 +212,8 @@ mod test_layers { assert!(e1.layers("2").unwrap().history().is_empty()); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let e = graph.edge(1, 2).unwrap(); @@ -232,6 +232,6 @@ mod test_layers { } test(&graph); // FIXME: Needs multilayer support (Issue #47) - // test(&arrow_graph); + // test(&disk_graph); } } diff --git a/raphtory/src/db/graph/views/node_subgraph.rs b/raphtory/src/db/graph/views/node_subgraph.rs index 5b062bf6eb..9d9d0d71eb 100644 --- a/raphtory/src/db/graph/views/node_subgraph.rs +++ b/raphtory/src/db/graph/views/node_subgraph.rs @@ -117,8 +117,8 @@ mod subgraph_tests { graph.add_node(2, 2, NO_PROPS, None).unwrap(); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let sg = graph.subgraph([1, 2]); @@ -128,7 +128,7 @@ mod subgraph_tests { } test(&graph); // FIXME: Needs multilayer support (Issue #47) - // test(&arrow_graph); + // test(&disk_graph); } #[test] @@ -163,8 +163,8 @@ mod subgraph_tests { graph.add_edge(ts, src, dst, NO_PROPS, None).unwrap(); } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let subgraph = graph.subgraph(graph.nodes().into_iter().filter(|v| v.degree() > 1)); @@ -173,8 +173,8 @@ mod subgraph_tests { assert_eq!(ts, tg) } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -184,8 +184,8 @@ mod subgraph_tests { graph.add_edge(0, 3, 4, NO_PROPS, Some("2")).unwrap(); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let sg = graph.subgraph([1, 2]); @@ -197,6 +197,6 @@ mod subgraph_tests { } test(&graph); // FIXME: Needs multilayer support (Issue #47) - // test(&arrow_graph); + // test(&disk_graph); } } diff --git a/raphtory/src/db/graph/views/window_graph.rs b/raphtory/src/db/graph/views/window_graph.rs index 9c53cf4af9..ae557460c4 100644 --- a/raphtory/src/db/graph/views/window_graph.rs +++ b/raphtory/src/db/graph/views/window_graph.rs @@ -564,8 +564,8 @@ mod views_test { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let wg = graph.window(-1, 1); @@ -581,8 +581,8 @@ mod views_test { assert_eq!(actual, expected); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -603,8 +603,8 @@ mod views_test { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let wg = graph.window(i64::MIN, i64::MAX); @@ -612,8 +612,8 @@ mod views_test { assert_eq!(wg.edge(1, 3).unwrap().dst().id(), 3); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -634,8 +634,8 @@ mod views_test { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let wg = graph.window(-1, 1); @@ -643,8 +643,8 @@ mod views_test { assert_eq!(wg.node(1).unwrap().id(), 1); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -667,8 +667,8 @@ mod views_test { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let wg = graph.window(1, 2); @@ -676,7 +676,7 @@ mod views_test { } test(&graph); // FIXME: Issue #46 - // test(&arrow_graph); + // test(&disk_graph); } #[quickcheck] @@ -727,7 +727,7 @@ mod views_test { // FIXME: Issue #46 // #[quickcheck] - // fn windowed_arrow_graph_has_node(mut vs: Vec<(i64, u64)>) -> TestResult { + // fn windowed_disk_graph_has_node(mut vs: Vec<(i64, u64)>) -> TestResult { // if vs.is_empty() { // return TestResult::discard(); // } @@ -746,8 +746,8 @@ mod views_test { // .ok(); // } // let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - // let g = g.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + // let g = g.persist_as_disk_graph(test_dir.path()).unwrap(); // // let start = vs.get(rand_start_index).expect("start index in range").0; // let end = vs.get(rand_end_index).expect("end index in range").0; @@ -818,7 +818,7 @@ mod views_test { } #[quickcheck] - fn windowed_arrow_graph_has_edge(mut edges: Vec<(i64, (u64, u64))>) -> TestResult { + fn windowed_disk_graph_has_edge(mut edges: Vec<(i64, (u64, u64))>) -> TestResult { if edges.is_empty() { return TestResult::discard(); } @@ -836,8 +836,8 @@ mod views_test { g.add_edge(*t, e.0, e.1, NO_PROPS, None).unwrap(); } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let g = g.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let g = g.persist_as_disk_graph(test_dir.path()).unwrap(); let start = edges.get(rand_start_index).expect("start index in range").0; let end = edges.get(rand_end_index).expect("end index in range").0; @@ -946,8 +946,8 @@ mod views_test { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test1(graph: &G, args: &[(i64, i64)], expected: &[Vec]) { let res: Vec<_> = (0..=3) @@ -962,16 +962,16 @@ mod views_test { assert_eq!(res, expected); } test1(&graph, &args, &expected); - #[cfg(feature = "arrow")] - test1(&arrow_graph, &args, &expected); + #[cfg(feature = "storage")] + test1(&disk_graph, &args, &expected); let graph = Graph::new(); for (src, dst, t) in &vs { graph.add_edge(*src, *dst, *t, NO_PROPS, None).unwrap(); } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test2(graph: &G, args: &[(i64, i64)], expected: &[Vec]) { let res: Vec<_> = (0..=3) @@ -985,8 +985,8 @@ mod views_test { assert_eq!(res, expected); } test2(&graph, &args, &expected); - #[cfg(feature = "arrow")] - test2(&arrow_graph, &args, &expected); + #[cfg(feature = "storage")] + test2(&disk_graph, &args, &expected); } #[test] @@ -1036,8 +1036,8 @@ mod views_test { } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let wg = graph.window(-2, 0); @@ -1049,8 +1049,8 @@ mod views_test { assert_eq!(actual, expected); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -1058,8 +1058,8 @@ mod views_test { let graph = Graph::new(); graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let mut w = WindowedGraph::new(&graph, Some(0), Some(1)); @@ -1068,8 +1068,8 @@ mod views_test { assert_eq!(w, Graph::new()); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -1078,8 +1078,8 @@ mod views_test { graph.add_edge(0, 1, 2, NO_PROPS, None).unwrap(); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let w = graph.window(0, 1); @@ -1088,8 +1088,8 @@ mod views_test { println!("{:?}", res) } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -1104,8 +1104,8 @@ mod views_test { graph.add_edge(t3, 3, 1, NO_PROPS, None).unwrap(); } let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { assert_graph_equal(&graph.before(9).after(2), &graph.window(3, 9)); @@ -1124,8 +1124,8 @@ mod views_test { ); } test(&graph); - #[cfg(feature = "arrow")] - test(&arrow_graph); + #[cfg(feature = "storage")] + test(&disk_graph); } #[test] @@ -1145,8 +1145,8 @@ mod views_test { graph.add_edge(7, 1, 3, NO_PROPS, None).unwrap(); let test_dir = TempDir::new().unwrap(); - #[cfg(feature = "arrow")] - let _arrow_graph = graph.persist_as_arrow(test_dir.path()).unwrap(); + #[cfg(feature = "storage")] + let _disk_graph = graph.persist_as_disk_graph(test_dir.path()).unwrap(); fn test(graph: &G) { let e = graph.edge(1, 2).unwrap(); @@ -1209,6 +1209,6 @@ mod views_test { } test(&graph); // FIXME: Issue #46 - // test(&arrow_graph); + // test(&disk_graph); } } diff --git a/raphtory/src/arrow/graph_impl/const_properties_ops.rs b/raphtory/src/disk_graph/graph_impl/const_properties_ops.rs similarity index 91% rename from raphtory/src/arrow/graph_impl/const_properties_ops.rs rename to raphtory/src/disk_graph/graph_impl/const_properties_ops.rs index 2f5ee5b989..eda8658b28 100644 --- a/raphtory/src/arrow/graph_impl/const_properties_ops.rs +++ b/raphtory/src/disk_graph/graph_impl/const_properties_ops.rs @@ -1,8 +1,8 @@ use crate::{core::ArcStr, db::api::properties::internal::ConstPropertiesOps, prelude::Prop}; -use super::ArrowGraph; +use super::DiskGraph; -impl ConstPropertiesOps for ArrowGraph { +impl ConstPropertiesOps for DiskGraph { #[doc = " Find id for property name (note this only checks the meta-data, not if the property actually exists for the entity)"] fn get_const_prop_id(&self, name: &str) -> Option { self.graph_props.get_const_prop_id(name) diff --git a/raphtory/src/arrow/graph_impl/core_ops.rs b/raphtory/src/disk_graph/graph_impl/core_ops.rs similarity index 87% rename from raphtory/src/arrow/graph_impl/core_ops.rs rename to raphtory/src/disk_graph/graph_impl/core_ops.rs index d6a400d18e..daeb3b895d 100644 --- a/raphtory/src/arrow/graph_impl/core_ops.rs +++ b/raphtory/src/disk_graph/graph_impl/core_ops.rs @@ -1,13 +1,4 @@ use crate::{ - arrow::{ - graph_impl::ArrowGraph, - storage_interface::{ - edge::ArrowOwnedEdge, - edges::ArrowEdges, - node::{ArrowNode, ArrowOwnedNode}, - nodes::ArrowNodesOwned, - }, - }, core::{ entities::{ edges::edge_ref::EdgeRef, @@ -28,18 +19,24 @@ use crate::{ }, storage_ops::GraphStorage, }, - view::{ - internal::{CoreGraphOps, DelegateCoreOps}, - BoxedIter, + view::{internal::CoreGraphOps, BoxedIter}, + }, + disk_graph::{ + graph_impl::DiskGraph, + storage_interface::{ + edge::DiskOwnedEdge, + edges::DiskEdges, + node::{DiskNode, DiskOwnedNode}, + nodes::DiskNodesOwned, }, }, }; use itertools::Itertools; use polars_arrow::datatypes::ArrowDataType; -use raphtory_arrow::{properties::Properties, GidRef, GID}; +use pometry_storage::{properties::Properties, GidRef, GID}; use rayon::prelude::*; -impl CoreGraphOps for ArrowGraph { +impl CoreGraphOps for DiskGraph { fn unfiltered_num_nodes(&self) -> usize { self.inner.num_nodes() } @@ -96,7 +93,7 @@ impl CoreGraphOps for ArrowGraph { } fn get_all_node_types(&self) -> Vec { - todo!("Node types are not supported on arrow yet") + todo!("Node types are not supported on diskgraph yet") } fn node_id(&self, v: VID) -> u64 { @@ -185,7 +182,7 @@ impl CoreGraphOps for ArrowGraph { } fn core_edges(&self) -> EdgesStorage { - EdgesStorage::Arrow(ArrowEdges::new(&self.inner)) + EdgesStorage::Disk(DiskEdges::new(&self.inner)) } fn unfiltered_num_layers(&self) -> usize { @@ -193,30 +190,30 @@ impl CoreGraphOps for ArrowGraph { } fn core_graph(&self) -> GraphStorage { - GraphStorage::Arrow(self.inner.clone()) + GraphStorage::Disk(self.inner.clone()) } fn core_edge(&self, eid: ELID) -> EdgeStorageEntry { let layer_id = eid .layer() - .expect("EdgeRefs in arrow should always have layer"); - EdgeStorageEntry::Arrow(self.inner.layer(layer_id).edge(eid.pid())) + .expect("EdgeRefs in disk_graph should always have layer"); + EdgeStorageEntry::Disk(self.inner.layer(layer_id).edge(eid.pid())) } fn core_nodes(&self) -> NodesStorage { - NodesStorage::Arrow(ArrowNodesOwned::new(self.inner.clone())) + NodesStorage::Disk(DiskNodesOwned::new(self.inner.clone())) } fn core_node_entry(&self, vid: VID) -> NodeStorageEntry { - NodeStorageEntry::Arrow(ArrowNode::new(&self.inner, vid)) + NodeStorageEntry::Disk(DiskNode::new(&self.inner, vid)) } fn core_node_arc(&self, vid: VID) -> NodeOwnedEntry { - NodeOwnedEntry::Arrow(ArrowOwnedNode::new(self.inner.clone(), vid)) + NodeOwnedEntry::Disk(DiskOwnedNode::new(self.inner.clone(), vid)) } fn core_edge_arc(&self, eid: ELID) -> EdgeOwnedEntry { - EdgeOwnedEntry::Arrow(ArrowOwnedEdge::new(&self.inner, eid)) + EdgeOwnedEntry::Disk(DiskOwnedEdge::new(&self.inner, eid)) } fn unfiltered_num_edges(&self) -> usize { @@ -227,8 +224,8 @@ impl CoreGraphOps for ArrowGraph { .sum() } - fn node_type_id(&self, v: VID) -> usize { - // self.graph().node_type_id(v) TODO: Impl node types for arrow graphs + fn node_type_id(&self, _v: VID) -> usize { + // self.graph().node_type_id(v) TODO: Impl node types for disk_graph graphs 0 } } diff --git a/raphtory/src/arrow/graph_impl/edge_filter_ops.rs b/raphtory/src/disk_graph/graph_impl/edge_filter_ops.rs similarity index 88% rename from raphtory/src/arrow/graph_impl/edge_filter_ops.rs rename to raphtory/src/disk_graph/graph_impl/edge_filter_ops.rs index 329b915e7f..5d7beed9f2 100644 --- a/raphtory/src/arrow/graph_impl/edge_filter_ops.rs +++ b/raphtory/src/disk_graph/graph_impl/edge_filter_ops.rs @@ -1,10 +1,10 @@ -use super::ArrowGraph; +use super::DiskGraph; use crate::{ core::entities::LayerIds, db::api::{storage::edges::edge_ref::EdgeStorageRef, view::internal::EdgeFilterOps}, }; -impl EdgeFilterOps for ArrowGraph { +impl EdgeFilterOps for DiskGraph { fn edges_filtered(&self) -> bool { false } diff --git a/raphtory/src/arrow/graph_impl/edge_storage_ops.rs b/raphtory/src/disk_graph/graph_impl/edge_storage_ops.rs similarity index 95% rename from raphtory/src/arrow/graph_impl/edge_storage_ops.rs rename to raphtory/src/disk_graph/graph_impl/edge_storage_ops.rs index ef41512437..b8e6a2ee1e 100644 --- a/raphtory/src/arrow/graph_impl/edge_storage_ops.rs +++ b/raphtory/src/disk_graph/graph_impl/edge_storage_ops.rs @@ -1,5 +1,4 @@ use crate::{ - arrow::graph_impl::tprops::read_tprop_column, core::{ entities::{edges::edge_ref::EdgeRef, LayerIds, VID}, storage::timeindex::{TimeIndex, TimeIndexOps}, @@ -8,9 +7,10 @@ use crate::{ edges::edge_storage_ops::{EdgeStorageOps, TimeIndexRef}, tprop_storage_ops::TPropOps, }, + disk_graph::graph_impl::tprops::read_tprop_column, }; +use pometry_storage::{edge::Edge, tprops::DiskTProp}; use raphtory_api::core::storage::timeindex::TimeIndexEntry; -use raphtory_arrow::{edge::Edge, tprops::ArrowTProp}; use rayon::prelude::*; use std::{iter, ops::Range}; @@ -94,6 +94,6 @@ impl<'a> EdgeStorageOps<'a> for Edge<'a> { } else { None } - .unwrap_or(ArrowTProp::empty()) + .unwrap_or(DiskTProp::empty()) } } diff --git a/raphtory/src/arrow/graph_impl/interop.rs b/raphtory/src/disk_graph/graph_impl/interop.rs similarity index 97% rename from raphtory/src/arrow/graph_impl/interop.rs rename to raphtory/src/disk_graph/graph_impl/interop.rs index 416d31a2f3..8cdc906a46 100644 --- a/raphtory/src/arrow/graph_impl/interop.rs +++ b/raphtory/src/disk_graph/graph_impl/interop.rs @@ -1,5 +1,4 @@ use crate::{ - arrow::graph_impl::prop_conversion::arrow_array_from_props, core::{ entities::{LayerIds, ELID}, storage::timeindex::TimeIndexOps, @@ -12,15 +11,16 @@ use crate::{ }, view::internal::CoreGraphOps, }, + disk_graph::graph_impl::prop_conversion::arrow_array_from_props, prelude::*, }; use itertools::Itertools; use polars_arrow::array::Array; +use pometry_storage::interop::GraphLike; use raphtory_api::core::{ entities::{EID, VID}, storage::timeindex::TimeIndexEntry, }; -use raphtory_arrow::interop::GraphLike; impl GraphLike for Graph { fn external_ids(&self) -> Vec { diff --git a/raphtory/src/arrow/graph_impl/layer_ops.rs b/raphtory/src/disk_graph/graph_impl/layer_ops.rs similarity index 95% rename from raphtory/src/arrow/graph_impl/layer_ops.rs rename to raphtory/src/disk_graph/graph_impl/layer_ops.rs index e86be131be..fe9e9a6cf5 100644 --- a/raphtory/src/arrow/graph_impl/layer_ops.rs +++ b/raphtory/src/disk_graph/graph_impl/layer_ops.rs @@ -4,9 +4,9 @@ use crate::{ prelude::Layer, }; -use super::ArrowGraph; +use super::DiskGraph; -impl InternalLayerOps for ArrowGraph { +impl InternalLayerOps for DiskGraph { fn layer_ids(&self) -> &LayerIds { match self.inner.layers().len() { 0 => &LayerIds::None, @@ -54,7 +54,7 @@ impl InternalLayerOps for ArrowGraph { .map(|(i, _)| LayerIds::One(i)) .unwrap_or(LayerIds::None) } - _ => todo!("Layer ids for multiple names not implemented for ArrowGraph"), + _ => todo!("Layer ids for multiple names not implemented for Diskgraph"), } } } diff --git a/raphtory/src/arrow/graph_impl/list_ops.rs b/raphtory/src/disk_graph/graph_impl/list_ops.rs similarity index 87% rename from raphtory/src/arrow/graph_impl/list_ops.rs rename to raphtory/src/disk_graph/graph_impl/list_ops.rs index 3ea3135fda..b9b5a66d99 100644 --- a/raphtory/src/arrow/graph_impl/list_ops.rs +++ b/raphtory/src/disk_graph/graph_impl/list_ops.rs @@ -1,10 +1,10 @@ use crate::{ - arrow::graph_impl::ArrowGraph, db::api::view::internal::{CoreGraphOps, EdgeList, ListOps, NodeList}, + disk_graph::graph_impl::DiskGraph, }; use rayon::prelude::*; -impl ListOps for ArrowGraph { +impl ListOps for DiskGraph { fn node_list(&self) -> NodeList { NodeList::All { num_nodes: self.unfiltered_num_nodes(), diff --git a/raphtory/src/arrow/graph_impl/materialize.rs b/raphtory/src/disk_graph/graph_impl/materialize.rs similarity index 82% rename from raphtory/src/arrow/graph_impl/materialize.rs rename to raphtory/src/disk_graph/graph_impl/materialize.rs index 8bda2840df..4561428d9a 100644 --- a/raphtory/src/arrow/graph_impl/materialize.rs +++ b/raphtory/src/disk_graph/graph_impl/materialize.rs @@ -3,9 +3,9 @@ use crate::{ db::api::view::{internal::InternalMaterialize, MaterializedGraph}, }; -use super::ArrowGraph; +use super::DiskGraph; -impl InternalMaterialize for ArrowGraph { +impl InternalMaterialize for DiskGraph { fn new_base_graph(&self, _graph: InternalGraph) -> MaterializedGraph { todo!() } diff --git a/raphtory/src/arrow/graph_impl/mod.rs b/raphtory/src/disk_graph/graph_impl/mod.rs similarity index 83% rename from raphtory/src/arrow/graph_impl/mod.rs rename to raphtory/src/disk_graph/graph_impl/mod.rs index 0ead35ef73..46e4e19935 100644 --- a/raphtory/src/arrow/graph_impl/mod.rs +++ b/raphtory/src/disk_graph/graph_impl/mod.rs @@ -4,23 +4,21 @@ use std::{ sync::Arc, }; -use raphtory_api::core::storage::timeindex::TimeIndexEntry; -use raphtory_arrow::{ - arrow_hmap::ArrowHashMap, graph::TemporalGraph, graph_fragment::TempColGraphFragment, +use pometry_storage::{ + disk_hmap::DiskHashMap, graph::TemporalGraph, graph_fragment::TempColGraphFragment, load::ExternalEdgeList, RAError, }; +use raphtory_api::core::storage::timeindex::TimeIndexEntry; use rayon::prelude::*; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use crate::{ - arrow::{graph_impl::prop_conversion::make_node_properties_from_graph, Error}, arrow2::{ array::{PrimitiveArray, StructArray}, datatypes::{ArrowDataType as DataType, Field}, }, core::{ entities::{ - graph::tgraph::InternalGraph, properties::{graph_meta::GraphMeta, props::Meta}, LayerIds, EID, VID, }, @@ -31,6 +29,7 @@ use crate::{ mutation::internal::{InternalAdditionOps, InternalPropertyAdditionOps}, view::{internal::Immutable, DynamicGraph, IntoDynamic}, }, + disk_graph::{graph_impl::prop_conversion::make_node_properties_from_graph, Error}, prelude::{Graph, GraphViewOps}, }; @@ -59,7 +58,7 @@ pub struct ParquetLayerCols<'a> { } #[derive(Clone, Debug)] -pub struct ArrowGraph { +pub struct DiskGraph { pub(crate) inner: Arc, node_meta: Arc, edge_meta: Arc, @@ -67,7 +66,7 @@ pub struct ArrowGraph { graph_dir: PathBuf, } -impl Serialize for ArrowGraph { +impl Serialize for DiskGraph { fn serialize(&self, serializer: S) -> Result where S: Serializer, @@ -77,51 +76,51 @@ impl Serialize for ArrowGraph { } } -impl<'de> Deserialize<'de> for ArrowGraph { +impl<'de> Deserialize<'de> for DiskGraph { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { let path = PathBuf::deserialize(deserializer)?; - let graph_result = ArrowGraph::load_from_dir(&path).map_err(|err| { - serde::de::Error::custom(format!("Failed to load ArrowGraph: {:?}", err)) + let graph_result = DiskGraph::load_from_dir(&path).map_err(|err| { + serde::de::Error::custom(format!("Failed to load Diskgraph: {:?}", err)) })?; Ok(graph_result) } } -impl Display for ArrowGraph { +impl Display for DiskGraph { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!( f, - "ArrowGraph(num_nodes={}, num_temporal_edges={}", + "Diskgraph(num_nodes={}, num_temporal_edges={}", self.count_nodes(), self.count_temporal_edges() ) } } -impl AsRef for ArrowGraph { +impl AsRef for DiskGraph { fn as_ref(&self) -> &TemporalGraph { &self.inner } } impl Graph { - pub fn persist_as_arrow(&self, graph_dir: impl AsRef) -> Result { - ArrowGraph::from_graph(self, graph_dir) + pub fn persist_as_disk_graph(&self, graph_dir: impl AsRef) -> Result { + DiskGraph::from_graph(self, graph_dir) } } -impl Immutable for ArrowGraph {} +impl Immutable for DiskGraph {} -impl IntoDynamic for ArrowGraph { +impl IntoDynamic for DiskGraph { fn into_dynamic(self) -> DynamicGraph { DynamicGraph::new(self) } } -impl ArrowGraph { +impl DiskGraph { pub fn layer_from_ids(&self, layer_ids: &LayerIds) -> Option { match layer_ids { LayerIds::One(layer_id) => Some(*layer_id), @@ -129,12 +128,12 @@ impl ArrowGraph { LayerIds::All => match self.inner.layers().len() { 0 => None, 1 => Some(0), - _ => todo!("multilayer edge views not yet supported in arrow"), + _ => todo!("multilayer edge views not yet supported in Diskgraph"), }, LayerIds::Multiple(ids) => match ids.len() { 0 => None, 1 => Some(ids[0]), - _ => todo!("multilayer edge views not yet supported in arrow"), + _ => todo!("multilayer edge views not yet supported in Diskgraph"), }, } } @@ -144,7 +143,7 @@ impl ArrowGraph { edges: &[(u64, u64, i64, f64)], chunk_size: usize, t_props_chunk_size: usize, - ) -> ArrowGraph { + ) -> DiskGraph { // unzip into 4 vectors let (src, (dst, (time, weight))): (Vec<_>, (Vec<_>, (Vec<_>, Vec<_>))) = edges .iter() @@ -166,7 +165,7 @@ impl ArrowGraph { ], None, )]; - ArrowGraph::load_from_edge_lists( + DiskGraph::load_from_edge_lists( &edge_lists, chunk_size, t_props_chunk_size, @@ -194,7 +193,7 @@ impl ArrowGraph { .resolve_prop_id(prop_name, data_type.into(), false) .expect("Arrow data types should without failing"); if id != resolved_id { - println!("Warning: Layers with different edge properties are not supported by the high-level apis on top of the arrow graph yet, edge properties will not be available to high-level apis"); + println!("Warning: Layers with different edge properties are not supported by the high-level apis on top of the disk_graph graph yet, edge properties will not be available to high-level apis"); edge_meta = Meta::new(); break; } @@ -259,7 +258,7 @@ impl ArrowGraph { Ok(Self::new(inner, path)) } - pub fn load_from_dir(graph_dir: impl AsRef) -> Result { + pub fn load_from_dir(graph_dir: impl AsRef) -> Result { let path = graph_dir.as_ref().to_path_buf(); let inner = TemporalGraph::new(graph_dir)?; Ok(Self::new(inner, path)) @@ -274,7 +273,7 @@ impl ArrowGraph { read_chunk_size: Option, concurrent_files: Option, num_threads: usize, - ) -> Result { + ) -> Result { let layered_edge_list: Vec> = layer_parquet_cols .iter() .map( @@ -332,7 +331,7 @@ impl ArrowGraph { let path = layer.graph_dir().to_path_buf(); let global_ordering = layer.nodes_storage().gids().clone(); - let global_order = ArrowHashMap::from_sorted_dedup(global_ordering.clone()) + let global_order = DiskHashMap::from_sorted_dedup(global_ordering.clone()) .expect("Failed to create global order"); let inner = TemporalGraph::new_from_layers( @@ -345,124 +344,128 @@ impl ArrowGraph { } } -impl InternalAdditionOps for ArrowGraph { +impl InternalAdditionOps for DiskGraph { fn next_event_id(&self) -> usize { - unimplemented!("ArrowGraph is immutable") + unimplemented!("Diskgraph is immutable") } - fn resolve_layer(&self, layer: Option<&str>) -> usize { + fn resolve_layer(&self, _layer: Option<&str>) -> usize { // Will check this - unimplemented!("ArrowGraph is immutable") + unimplemented!("Diskgraph is immutable") } - fn resolve_node_type(&self, v_id: VID, node_type: Option<&str>) -> Result { - unimplemented!("ArrowGraph is immutable") + fn resolve_node_type( + &self, + _v_id: VID, + _node_typee: Option<&str>, + ) -> Result { + unimplemented!("Diskgraph is immutable") } - fn resolve_node(&self, id: u64, name: Option<&str>) -> VID { - unimplemented!("ArrowGraph is immutable") + fn resolve_node(&self, _id: u64, _name: Option<&str>) -> VID { + unimplemented!("Diskgraph is immutable") } - fn resolve_graph_property(&self, prop: &str, is_static: bool) -> usize { - unimplemented!("ArrowGraph is immutable") + fn resolve_graph_property(&self, _prop: &str, _is_static: bool) -> usize { + unimplemented!("Diskgraph is immutable") } fn resolve_node_property( &self, - prop: &str, - dtype: PropType, - is_static: bool, + _prop: &str, + _dtype: PropType, + _is_static: bool, ) -> Result { - unimplemented!("ArrowGraph is immutable") + unimplemented!("Diskgraph is immutable") } fn resolve_edge_property( &self, - prop: &str, - dtype: PropType, - is_static: bool, + _prop: &str, + _dtype: PropType, + _is_static: bool, ) -> Result { - unimplemented!("ArrowGraph is immutable") + unimplemented!("Diskgraph is immutable") } - fn process_prop_value(&self, prop: Prop) -> Prop { - unimplemented!("ArrowGraph is immutable") + fn process_prop_value(&self, _prop: Prop) -> Prop { + unimplemented!("Diskgraph is immutable") } fn internal_add_node( &self, - t: TimeIndexEntry, - v: VID, - props: Vec<(usize, Prop)>, - node_type_id: usize, + _t: TimeIndexEntry, + _v: VID, + _props: Vec<(usize, Prop)>, + _node_type_id: usize, ) -> Result<(), GraphError> { - unimplemented!("ArrowGraph is immutable") + unimplemented!("Diskgraph is immutable") } fn internal_add_edge( &self, - t: TimeIndexEntry, - src: VID, - dst: VID, - props: Vec<(usize, Prop)>, - layer: usize, + _t: TimeIndexEntry, + _src: VID, + _dst: VID, + _props: Vec<(usize, Prop)>, + _layer: usize, ) -> Result { - unimplemented!("ArrowGraph is immutable") + unimplemented!("Diskgraph is immutable") } } -impl InternalPropertyAdditionOps for ArrowGraph { +impl InternalPropertyAdditionOps for DiskGraph { fn internal_add_properties( &self, - t: TimeIndexEntry, - props: Vec<(usize, Prop)>, + _t: TimeIndexEntry, + _props: Vec<(usize, Prop)>, ) -> Result<(), GraphError> { - unimplemented!("ArrowGraph is immutable") + unimplemented!("Diskgraph is immutable") } - fn internal_add_static_properties(&self, props: Vec<(usize, Prop)>) -> Result<(), GraphError> { - unimplemented!("ArrowGraph is immutable") + fn internal_add_static_properties(&self, _props: Vec<(usize, Prop)>) -> Result<(), GraphError> { + unimplemented!("Diskgraph is immutable") } fn internal_update_static_properties( &self, - props: Vec<(usize, Prop)>, + _props: Vec<(usize, Prop)>, ) -> Result<(), GraphError> { - unimplemented!("ArrowGraph is immutable") + unimplemented!("Diskgraph is immutable") } fn internal_add_constant_node_properties( &self, - vid: VID, - props: Vec<(usize, Prop)>, + _vid: VID, + _props: Vec<(usize, Prop)>, ) -> Result<(), GraphError> { - unimplemented!("ArrowGraph is immutable") + unimplemented!("Diskgraph is immutable") } fn internal_update_constant_node_properties( &self, - vid: VID, - props: Vec<(usize, Prop)>, + _vid: VID, + _props: Vec<(usize, Prop)>, ) -> Result<(), GraphError> { - unimplemented!("ArrowGraph is immutable") + unimplemented!("Diskgraph is immutable") } fn internal_add_constant_edge_properties( &self, - eid: EID, - layer: usize, - props: Vec<(usize, Prop)>, + _eid: EID, + _layer: usize, + _props: Vec<(usize, Prop)>, ) -> Result<(), GraphError> { - unimplemented!("ArrowGraph is immutable") + unimplemented!("Diskgraph is immutable") } fn internal_update_constant_edge_properties( &self, - eid: EID, - layer: usize, - props: Vec<(usize, Prop)>, + _eid: EID, + _layer: usize, + _props: Vec<(usize, Prop)>, ) -> Result<(), GraphError> { - unimplemented!("ArrowGraph is immutable") + unimplemented!("Diskgraph is immutable") } } @@ -471,23 +474,20 @@ mod test { use std::{cmp::Reverse, iter::once, path::Path}; use itertools::{chain, Itertools}; + use pometry_storage::graph::TemporalGraph; use proptest::{prelude::*, sample::size_range}; - use raphtory_arrow::graph::TemporalGraph; use rayon::prelude::*; use tempfile::TempDir; use crate::{ - algorithms::components::weakly_connected_components, arrow::Time, - db::api::view::StaticGraphViewOps, prelude::*, + algorithms::components::weakly_connected_components, db::api::view::StaticGraphViewOps, + disk_graph::Time, prelude::*, }; - use super::ArrowGraph; + use super::DiskGraph; - fn make_simple_graph( - graph_dir: impl AsRef, - edges: &[(u64, u64, i64, f64)], - ) -> ArrowGraph { - ArrowGraph::make_simple_graph(graph_dir, edges, 1000, 1000) + fn make_simple_graph(graph_dir: impl AsRef, edges: &[(u64, u64, i64, f64)]) -> DiskGraph { + DiskGraph::make_simple_graph(graph_dir, edges, 1000, 1000) } fn check_graph_counts(edges: &[(u64, u64, Time, f64)], g: &impl StaticGraphViewOps) { @@ -765,14 +765,14 @@ mod test { } #[test] - fn test_mem_to_arrow_graph() { + fn test_mem_to_disk_graph() { let mem_graph = Graph::new(); mem_graph.add_edge(0, 0, 1, [("test", 0u64)], None).unwrap(); let test_dir = TempDir::new().unwrap(); - let arrow_graph = + let disk_graph = TemporalGraph::from_graph(&mem_graph, test_dir.path(), || Ok(None)).unwrap(); - assert_eq!(arrow_graph.num_nodes(), 2); - assert_eq!(arrow_graph.num_edges(0), 1); + assert_eq!(disk_graph.num_nodes(), 2); + assert_eq!(disk_graph.num_edges(0), 1); } #[test] @@ -795,18 +795,18 @@ mod test { ]) .unwrap(); let test_dir = TempDir::new().unwrap(); - let arrow_graph = ArrowGraph::from_graph(&mem_graph, test_dir.path()).unwrap(); - assert_eq!(arrow_graph.count_nodes(), 1); - let props = arrow_graph.node(0).unwrap().properties(); + let disk_graph = DiskGraph::from_graph(&mem_graph, test_dir.path()).unwrap(); + assert_eq!(disk_graph.count_nodes(), 1); + let props = disk_graph.node(0).unwrap().properties(); assert_eq!(props.get("test_num").unwrap_u64(), 0); assert_eq!(props.get("test_str").unwrap_str(), "test"); assert_eq!(props.get("const_str").unwrap_str(), "test_c"); assert_eq!(props.get("const_float").unwrap_f64(), 0.314); - drop(arrow_graph); + drop(disk_graph); - let arrow_graph = ArrowGraph::load_from_dir(test_dir.path()).unwrap(); - let props = arrow_graph.node(0).unwrap().properties(); + let disk_graph = DiskGraph::load_from_dir(test_dir.path()).unwrap(); + let props = disk_graph.node(0).unwrap().properties(); assert_eq!(props.get("test_num").unwrap_u64(), 0); assert_eq!(props.get("test_str").unwrap_str(), "test"); assert_eq!(props.get("const_str").unwrap_str(), "test_c"); @@ -819,9 +819,9 @@ mod test { let v = g.add_node(0, 1, NO_PROPS, None).unwrap(); v.add_constant_properties([("test", "test")]).unwrap(); let test_dir = TempDir::new().unwrap(); - let arrow_graph = g.persist_as_arrow(test_dir.path()).unwrap(); + let disk_graph = g.persist_as_disk_graph(test_dir.path()).unwrap(); assert_eq!( - arrow_graph + disk_graph .node(1) .unwrap() .properties() @@ -829,9 +829,9 @@ mod test { .unwrap_str(), "test" ); - let arrow_graph = ArrowGraph::load_from_dir(test_dir.path()).unwrap(); + let disk_graph = DiskGraph::load_from_dir(test_dir.path()).unwrap(); assert_eq!( - arrow_graph + disk_graph .node(1) .unwrap() .properties() diff --git a/raphtory/src/arrow/graph_impl/node_filter_ops.rs b/raphtory/src/disk_graph/graph_impl/node_filter_ops.rs similarity index 82% rename from raphtory/src/arrow/graph_impl/node_filter_ops.rs rename to raphtory/src/disk_graph/graph_impl/node_filter_ops.rs index f1670f3465..bb699b700b 100644 --- a/raphtory/src/arrow/graph_impl/node_filter_ops.rs +++ b/raphtory/src/disk_graph/graph_impl/node_filter_ops.rs @@ -1,10 +1,10 @@ use crate::{ - arrow::graph_impl::ArrowGraph, core::entities::LayerIds, db::api::{storage::nodes::node_ref::NodeStorageRef, view::internal::NodeFilterOps}, + disk_graph::graph_impl::DiskGraph, }; -impl NodeFilterOps for ArrowGraph { +impl NodeFilterOps for DiskGraph { fn nodes_filtered(&self) -> bool { false } diff --git a/raphtory/src/arrow/graph_impl/prop_conversion.rs b/raphtory/src/disk_graph/graph_impl/prop_conversion.rs similarity index 98% rename from raphtory/src/arrow/graph_impl/prop_conversion.rs rename to raphtory/src/disk_graph/graph_impl/prop_conversion.rs index 3e6bdd3e82..40b02c0166 100644 --- a/raphtory/src/arrow/graph_impl/prop_conversion.rs +++ b/raphtory/src/disk_graph/graph_impl/prop_conversion.rs @@ -11,7 +11,7 @@ use crate::{ prelude::{Graph, Prop, PropUnwrap}, }; use itertools::Itertools; -use raphtory_arrow::{ +use pometry_storage::{ properties::{node_ts, NodePropsBuilder, Properties}, RAError, }; @@ -114,7 +114,7 @@ pub fn arrow_dtype_from_prop_type(prop_type: PropType) -> DataType { | PropType::Graph | PropType::PersistentGraph | PropType::Document - | PropType::DTime => panic!("{prop_type:?} not supported as arrow property"), + | PropType::DTime => panic!("{prop_type:?} not supported as disk_graph property"), } } @@ -171,7 +171,7 @@ pub fn arrow_array_from_props( | PropType::Graph | PropType::PersistentGraph | PropType::Document - | PropType::DTime => panic!("{prop_type:?} not supported as arrow property"), + | PropType::DTime => panic!("{prop_type:?} not supported as disk_graph property"), } } @@ -218,7 +218,7 @@ pub fn schema_from_prop_meta(prop_map: &PropMapper) -> Schema { | PropType::Graph | PropType::PersistentGraph | PropType::Document - | PropType::DTime) => panic!("{:?} not supported as arrow property", prop_type), + | PropType::DTime) => panic!("{:?} not supported as disk_graph property", prop_type), } } diff --git a/raphtory/src/arrow/graph_impl/temporal_properties_ops.rs b/raphtory/src/disk_graph/graph_impl/temporal_properties_ops.rs similarity index 90% rename from raphtory/src/arrow/graph_impl/temporal_properties_ops.rs rename to raphtory/src/disk_graph/graph_impl/temporal_properties_ops.rs index 0564041e6c..45c830b86e 100644 --- a/raphtory/src/arrow/graph_impl/temporal_properties_ops.rs +++ b/raphtory/src/disk_graph/graph_impl/temporal_properties_ops.rs @@ -4,9 +4,9 @@ use crate::{ prelude::Prop, }; -use super::ArrowGraph; +use super::DiskGraph; -impl TemporalPropertiesOps for ArrowGraph { +impl TemporalPropertiesOps for DiskGraph { fn get_temporal_prop_id(&self, name: &str) -> Option { self.graph_props.get_temporal_id(name) } @@ -24,7 +24,7 @@ impl TemporalPropertiesOps for ArrowGraph { } } -impl TemporalPropertyViewOps for ArrowGraph { +impl TemporalPropertyViewOps for DiskGraph { fn temporal_history(&self, id: usize) -> Vec { self.graph_props .get_temporal_prop(id) diff --git a/raphtory/src/arrow/graph_impl/time_index_into_ops.rs b/raphtory/src/disk_graph/graph_impl/time_index_into_ops.rs similarity index 97% rename from raphtory/src/arrow/graph_impl/time_index_into_ops.rs rename to raphtory/src/disk_graph/graph_impl/time_index_into_ops.rs index e658c6e0c9..bcbba8f57a 100644 --- a/raphtory/src/arrow/graph_impl/time_index_into_ops.rs +++ b/raphtory/src/disk_graph/graph_impl/time_index_into_ops.rs @@ -2,11 +2,11 @@ use crate::{ core::storage::timeindex::{TimeIndexIntoOps, TimeIndexOps}, db::api::view::IntoDynBoxed, }; -use raphtory_api::core::storage::timeindex::TimeIndexEntry; -use raphtory_arrow::{ +use pometry_storage::{ prelude::{ArrayOps, BaseArrayOps}, timestamps::TimeStamps, }; +use raphtory_api::core::storage::timeindex::TimeIndexEntry; use std::ops::Range; impl<'a> TimeIndexIntoOps for TimeStamps<'a, TimeIndexEntry> { @@ -23,7 +23,9 @@ impl<'a> TimeIndexIntoOps for TimeStamps<'a, TimeIndexEntry> { sec_index.map(|sec_index| sec_index.sliced(start..end)), ) } - fn into_iter(self) -> impl Iterator + 'static { + + #[allow(refining_impl_trait)] + fn into_iter(self) -> impl Iterator + Send + 'static { let (timestamps, sec_index) = self.into_inner(); let sec_iter: Box + Send> = sec_index .map(|v| v.into_owned().map(|i| i as usize).into_dyn_boxed()) @@ -45,7 +47,7 @@ impl<'a> TimeIndexIntoOps for TimeStamps<'a, i64> { let (timestamps, _) = self.into_inner(); TimeStamps::new(timestamps.sliced(start..end), None) } - fn into_iter(self) -> impl Iterator + 'a { + fn into_iter(self) -> impl Iterator { let (timestamps, _) = self.into_inner(); timestamps } diff --git a/raphtory/src/arrow/graph_impl/time_semantics.rs b/raphtory/src/disk_graph/graph_impl/time_semantics.rs similarity index 90% rename from raphtory/src/arrow/graph_impl/time_semantics.rs rename to raphtory/src/disk_graph/graph_impl/time_semantics.rs index 8800090f12..f93e6072eb 100644 --- a/raphtory/src/arrow/graph_impl/time_semantics.rs +++ b/raphtory/src/disk_graph/graph_impl/time_semantics.rs @@ -1,6 +1,5 @@ -use super::ArrowGraph; +use super::DiskGraph; use crate::{ - arrow::graph_impl::tprops::read_tprop_column, core::{ entities::{edges::edge_ref::EdgeRef, LayerIds, VID}, storage::timeindex::{AsTime, TimeIndexIntoOps, TimeIndexOps}, @@ -13,6 +12,7 @@ use crate::{ }, view::{internal::TimeSemantics, BoxedIter}, }, + disk_graph::graph_impl::tprops::read_tprop_column, prelude::*, }; use itertools::Itertools; @@ -20,7 +20,7 @@ use raphtory_api::core::storage::timeindex::TimeIndexEntry; use rayon::prelude::*; use std::{iter, ops::Range}; -impl TimeSemantics for ArrowGraph { +impl TimeSemantics for DiskGraph { fn node_earliest_time(&self, v: VID) -> Option { self.inner .layers() @@ -179,7 +179,7 @@ impl TimeSemantics for ArrowGraph { vec![] } } - None => panic!("arrow edges should always have a layer currently"), + None => panic!("disk_graph edges should always have a layer currently"), } } @@ -198,7 +198,7 @@ impl TimeSemantics for ArrowGraph { vec![] } } - None => panic!("arrow edges should always have a layer currently"), + None => panic!("disk_graph edges should always have a layer currently"), } } @@ -241,14 +241,14 @@ impl TimeSemantics for ArrowGraph { Box::new(iter::empty()) } } - None => panic!("arrow edges should always have a layer currently"), + None => panic!("disk_graph edges should always have a layer currently"), } } fn edge_layers(&self, e: EdgeRef, layer_ids: &LayerIds) -> BoxedIter { let layer = e .layer() - .expect("arrow edges should always have a layer currently"); + .expect("disk_graph edges should always have a layer currently"); if layer_ids.contains(layer) { Box::new(iter::once(e)) } else { @@ -284,7 +284,7 @@ impl TimeSemantics for ArrowGraph { Box::new(iter::empty()) } } - None => panic!("arrow edges should always have a layer currently"), + None => panic!("disk_graph edges should always have a layer currently"), } } @@ -296,7 +296,7 @@ impl TimeSemantics for ArrowGraph { ) -> BoxedIter { let layer = e .layer() - .expect("arrow edges should always have a layer currently"); + .expect("disk_graph edges should always have a layer currently"); if layer_ids.contains(layer) && e.time_t().map(|t| w.contains(&t)).unwrap_or(true) { Box::new(iter::once(e)) } else { @@ -307,7 +307,7 @@ impl TimeSemantics for ArrowGraph { fn edge_earliest_time(&self, e: EdgeRef, layer_ids: &LayerIds) -> Option { let layer = e .layer() - .expect("arrow edges should always have a layer currently"); + .expect("disk_graph edges should always have a layer currently"); if layer_ids.contains(layer) { e.time_t().or_else(|| { self.inner @@ -330,7 +330,7 @@ impl TimeSemantics for ArrowGraph { ) -> Option { let layer = e .layer() - .expect("arrow edges should always have a layer currently"); + .expect("disk_graph edges should always have a layer currently"); if layer_ids.contains(layer) { match e.time_t() { Some(t) => w.contains(&t).then_some(t), @@ -351,7 +351,7 @@ impl TimeSemantics for ArrowGraph { fn edge_latest_time(&self, e: EdgeRef, layer_ids: &LayerIds) -> Option { let layer = e .layer() - .expect("arrow edges should always have a layer currently"); + .expect("disk_graph edges should always have a layer currently"); if layer_ids.contains(layer) { e.time_t().or_else(|| { self.inner @@ -374,7 +374,7 @@ impl TimeSemantics for ArrowGraph { ) -> Option { let layer = e .layer() - .expect("arrow edges should always have a layer currently"); + .expect("disk_graph edges should always have a layer currently"); if layer_ids.contains(layer) { match e.time_t() { Some(t) => w.contains(&t).then_some(t), @@ -409,14 +409,14 @@ impl TimeSemantics for ArrowGraph { fn edge_is_valid(&self, e: EdgeRef, layer_ids: &LayerIds) -> bool { let layer = e .layer() - .expect("arrow edges should always have layer currently"); + .expect("disk_graph edges should always have layer currently"); layer_ids.contains(layer) } fn edge_is_valid_at_end(&self, e: EdgeRef, layer_ids: &LayerIds, t: i64) -> bool { let layer = e .layer() - .expect("arrow edges should always have layer currently"); + .expect("disk_graph edges should always have layer currently"); layer_ids.contains(layer) && self .inner @@ -428,16 +428,16 @@ impl TimeSemantics for ArrowGraph { } fn has_temporal_prop(&self, _prop_id: usize) -> bool { - //FIXME: arrow graph does not have properties yet + //FIXME: disk_graph graph does not have properties yet false } fn temporal_prop_vec(&self, _prop_id: usize) -> Vec<(i64, Prop)> { - todo!("arrow graph does not have properties yet") + todo!("Diskgraph does not have properties yet") } fn has_temporal_prop_window(&self, _prop_id: usize, _w: Range) -> bool { - //FIXME: arrow graph does not have properties yet + //FIXME: disk_graph graph does not have properties yet false } @@ -447,7 +447,7 @@ impl TimeSemantics for ArrowGraph { _start: i64, _end: i64, ) -> Vec<(i64, Prop)> { - todo!("arrow graph does not have properties yet") + todo!("Diskgraph does not have properties yet") } fn has_temporal_node_prop(&self, v: VID, prop_id: usize) -> bool { @@ -498,7 +498,9 @@ impl TimeSemantics for ArrowGraph { w: Range, layer_ids: &LayerIds, ) -> bool { - let layer_id = e.layer().expect("arrow edges always have layer currently"); + let layer_id = e + .layer() + .expect("disk_graph edges always have layer currently"); if !layer_ids.contains(layer_id) { return false; } @@ -538,7 +540,9 @@ impl TimeSemantics for ArrowGraph { } fn has_temporal_edge_prop(&self, e: EdgeRef, prop_id: usize, layer_ids: &LayerIds) -> bool { - let layer_id = e.layer().expect("arrow edges always have layer currently"); + let layer_id = e + .layer() + .expect("disk_graph edges always have layer currently"); if !layer_ids.contains(layer_id) { return false; } @@ -559,7 +563,7 @@ impl TimeSemantics for ArrowGraph { vec![] } LayerIds::All => { - todo!("multilayer edge view not supported in arrow yet") + todo!("multilayer edge view not supported in Diskgraph yet") } LayerIds::One(layer_id) => { let edge = self.inner.layer(layer_id).edge(e.pid()); @@ -576,7 +580,7 @@ impl TimeSemantics for ArrowGraph { } } LayerIds::Multiple(_) => { - todo!("multilayer edge view not supported in arrow yet") + todo!("multilayer edge view not supported in Diskgraph yet") } } } diff --git a/raphtory/src/arrow/graph_impl/tprops.rs b/raphtory/src/disk_graph/graph_impl/tprops.rs similarity index 85% rename from raphtory/src/arrow/graph_impl/tprops.rs rename to raphtory/src/disk_graph/graph_impl/tprops.rs index e779d0d223..c9477298fa 100644 --- a/raphtory/src/arrow/graph_impl/tprops.rs +++ b/raphtory/src/disk_graph/graph_impl/tprops.rs @@ -7,14 +7,14 @@ use crate::{ db::api::{storage::tprop_storage_ops::TPropOps, view::IntoDynBoxed}, prelude::Prop, }; -use raphtory_api::core::storage::timeindex::TimeIndexEntry; -use raphtory_arrow::{ +use pometry_storage::{ chunked_array::{col::ChunkedPrimitiveCol, utf8_col::StringCol}, edge::Edge, prelude::{ArrayOps, BaseArrayOps}, timestamps::TimeStamps, - tprops::{ArrowTProp, EmptyTProp, TPropColumn}, + tprops::{DiskTProp, EmptyTProp, TPropColumn}, }; +use raphtory_api::core::storage::timeindex::TimeIndexEntry; use rayon::prelude::*; use std::{iter, ops::Range}; @@ -126,27 +126,23 @@ where Some(TPropColumn::new(props, timestamps)) } -pub fn read_tprop_column( - id: usize, - field: Field, - edge: Edge, -) -> Option> { +pub fn read_tprop_column(id: usize, field: Field, edge: Edge) -> Option> { match field.data_type() { - DataType::Int64 => new_tprop_column::(edge, id).map(ArrowTProp::I64), - DataType::Int32 => new_tprop_column::(edge, id).map(ArrowTProp::I32), - DataType::UInt32 => new_tprop_column::(edge, id).map(ArrowTProp::U32), - DataType::UInt64 => new_tprop_column::(edge, id).map(ArrowTProp::U64), - DataType::Float32 => new_tprop_column::(edge, id).map(ArrowTProp::F32), - DataType::Float64 => new_tprop_column::(edge, id).map(ArrowTProp::F64), + DataType::Int64 => new_tprop_column::(edge, id).map(DiskTProp::I64), + DataType::Int32 => new_tprop_column::(edge, id).map(DiskTProp::I32), + DataType::UInt32 => new_tprop_column::(edge, id).map(DiskTProp::U32), + DataType::UInt64 => new_tprop_column::(edge, id).map(DiskTProp::U64), + DataType::Float32 => new_tprop_column::(edge, id).map(DiskTProp::F32), + DataType::Float64 => new_tprop_column::(edge, id).map(DiskTProp::F64), DataType::Utf8 => { let props = edge.prop_str_values::(id)?; let timestamps = TimeStamps::new(edge.timestamp_slice(), None); - Some(ArrowTProp::Str32(TPropColumn::new(props, timestamps))) + Some(DiskTProp::Str32(TPropColumn::new(props, timestamps))) } DataType::LargeUtf8 => { let props = edge.prop_str_values::(id)?; let timestamps = TimeStamps::new(edge.timestamp_slice(), None); - Some(ArrowTProp::Str64(TPropColumn::new(props, timestamps))) + Some(DiskTProp::Str64(TPropColumn::new(props, timestamps))) } _ => todo!(), } @@ -190,22 +186,22 @@ impl<'a> TPropOps<'a> for EmptyTProp { macro_rules! for_all { ($value:expr, $pattern:pat => $result:expr) => { match $value { - ArrowTProp::Empty($pattern) => $result, - ArrowTProp::Str64($pattern) => $result, - ArrowTProp::Str32($pattern) => $result, - ArrowTProp::I32($pattern) => $result, - ArrowTProp::I64($pattern) => $result, - ArrowTProp::U8($pattern) => $result, - ArrowTProp::U16($pattern) => $result, - ArrowTProp::U32($pattern) => $result, - ArrowTProp::U64($pattern) => $result, - ArrowTProp::F32($pattern) => $result, - ArrowTProp::F64($pattern) => $result, + DiskTProp::Empty($pattern) => $result, + DiskTProp::Str64($pattern) => $result, + DiskTProp::Str32($pattern) => $result, + DiskTProp::I32($pattern) => $result, + DiskTProp::I64($pattern) => $result, + DiskTProp::U8($pattern) => $result, + DiskTProp::U16($pattern) => $result, + DiskTProp::U32($pattern) => $result, + DiskTProp::U64($pattern) => $result, + DiskTProp::F32($pattern) => $result, + DiskTProp::F64($pattern) => $result, } }; } -impl<'a> TPropOps<'a> for ArrowTProp<'a, TimeIndexEntry> { +impl<'a> TPropOps<'a> for DiskTProp<'a, TimeIndexEntry> { fn last_before(self, t: i64) -> Option<(TimeIndexEntry, Prop)> { for_all!(self, v => v.last_before(t)) } diff --git a/raphtory/src/arrow/mod.rs b/raphtory/src/disk_graph/mod.rs similarity index 95% rename from raphtory/src/arrow/mod.rs rename to raphtory/src/disk_graph/mod.rs index 862342e644..61e18c437b 100644 --- a/raphtory/src/arrow/mod.rs +++ b/raphtory/src/disk_graph/mod.rs @@ -5,13 +5,13 @@ pub mod storage_interface; pub type Time = i64; pub mod prelude { - pub use raphtory_arrow::chunked_array::array_ops::*; + pub use pometry_storage::chunked_array::array_ops::*; } #[derive(thiserror::Error, Debug)] pub enum Error { #[error("Raphtory Arrow Error: {0}")] - RAError(#[from] raphtory_arrow::RAError), + RAError(#[from] pometry_storage::RAError), } #[cfg(test)] @@ -28,12 +28,12 @@ mod test { array::{PrimitiveArray, StructArray}, datatypes::Field, }; + use pometry_storage::{global_order::GlobalMap, graph_fragment::TempColGraphFragment, RAError}; use proptest::{prelude::*, sample::size_range}; use raphtory_api::core::{ entities::{EID, VID}, Direction, }; - use raphtory_arrow::{global_order::GlobalMap, graph_fragment::TempColGraphFragment, RAError}; use tempfile::TempDir; fn edges_sanity_node_list(edges: &[(u64, u64, i64)]) -> Vec { @@ -118,9 +118,11 @@ mod test { } let graph_dir = TempDir::new().unwrap(); - // check persist_as_arrow works - let arrow_from_expected = expected_graph.persist_as_arrow(graph_dir.path()).unwrap(); - assert_graph_equal(&arrow_from_expected, &expected_graph); + // check persist_as_disk_graph works + let disk_graph_from_expected = expected_graph + .persist_as_disk_graph(graph_dir.path()) + .unwrap(); + assert_graph_equal(&disk_graph_from_expected, &expected_graph); let actual_num_verts = nodes.len(); let g_num_verts = graph.num_nodes(); @@ -337,8 +339,8 @@ mod test { graph.add_edge(1, 0, 1, [("weight", 1.)], None).unwrap(); graph.add_edge(2, 0, 1, [("weight", 2.)], None).unwrap(); graph.add_edge(3, 1, 2, [("weight", 3.)], None).unwrap(); - let arrow_graph = graph.persist_as_arrow(graph_dir.path()).unwrap(); - let graph = arrow_graph.inner.layer(0); + let disk_graph = graph.persist_as_disk_graph(graph_dir.path()).unwrap(); + let graph = disk_graph.inner.layer(0); let all_exploded: Vec<_> = graph .exploded_edges() @@ -422,7 +424,7 @@ mod test { g.add_edge(0, 1, 2, [("test", "test1")], Some("1")).unwrap(); g.add_edge(1, 2, 3, [("test", "test2")], Some("2")).unwrap(); let test_dir = TempDir::new().unwrap(); - let _ = g.persist_as_arrow(test_dir.path()).unwrap(); + let _ = g.persist_as_disk_graph(test_dir.path()).unwrap(); } #[test] diff --git a/raphtory/src/arrow/query/ast.rs b/raphtory/src/disk_graph/query/ast.rs similarity index 100% rename from raphtory/src/arrow/query/ast.rs rename to raphtory/src/disk_graph/query/ast.rs diff --git a/raphtory/src/arrow/query/executors/mod.rs b/raphtory/src/disk_graph/query/executors/mod.rs similarity index 100% rename from raphtory/src/arrow/query/executors/mod.rs rename to raphtory/src/disk_graph/query/executors/mod.rs diff --git a/raphtory/src/arrow/query/executors/rayon2.rs b/raphtory/src/disk_graph/query/executors/rayon2.rs similarity index 94% rename from raphtory/src/arrow/query/executors/rayon2.rs rename to raphtory/src/disk_graph/query/executors/rayon2.rs index 6a86ff0387..091bfe8861 100644 --- a/raphtory/src/arrow/query/executors/rayon2.rs +++ b/raphtory/src/disk_graph/query/executors/rayon2.rs @@ -1,25 +1,25 @@ use crate::{ - arrow::{ - graph_impl::ArrowGraph, + core::{entities::VID, Direction}, + db::{api::view::StaticGraphViewOps, graph::node::NodeView}, + disk_graph::{ + graph_impl::DiskGraph, query::{ ast::{Hop, Query, Sink}, state::{HopState, StaticGraphHopState}, NodeSource, }, }, - core::{entities::VID, Direction}, - db::{api::view::StaticGraphViewOps, graph::node::NodeView}, prelude::*, }; use itertools::Itertools; -use raphtory_arrow::{nodes::Node, RAError}; +use pometry_storage::{nodes::Node, RAError}; use rayon::{current_thread_index, Scope, ThreadPoolBuilder}; use std::{cell::RefCell, fs::File, io::BufWriter, path::Path, sync::Arc}; pub fn execute( query: Query, source: NodeSource, - graph: &ArrowGraph, + graph: &DiskGraph, make_state: impl Fn(Node) -> S + Send + Sync, ) -> Result<(), RAError> { let tp = ThreadPoolBuilder::new() @@ -44,7 +44,7 @@ pub fn execute( for node in node_chunk { let node = graph.inner.layer(layer).node(node); let state = (make_state)(node); - hop_arrow_graph(node, &query, 0, state, graph, s, tl); + hop_disk_graph(node, &query, 0, state, graph, s, tl); } }) } @@ -93,7 +93,7 @@ fn node_view<'a, G: StaticGraphViewOps>( NodeView::new_internal(graph, node) } -fn lookup_layer(layer: &str, graph: &ArrowGraph) -> usize { +fn lookup_layer(layer: &str, graph: &DiskGraph) -> usize { graph.inner.find_layer_id(layer).expect("No layer") } @@ -112,12 +112,12 @@ fn get_writer( out } -fn hop_arrow_graph<'a, S: HopState + 'a>( +fn hop_disk_graph<'a, S: HopState + 'a>( node: Node, query: &'a Query, step: usize, state: S, - graph: &'a ArrowGraph, + graph: &'a DiskGraph, s: &rayon::Scope<'a>, tl: &'a Arc>>>, ) { @@ -149,7 +149,7 @@ fn hop_arrow_graph<'a, S: HopState + 'a>( }) .take(limit) .for_each(|(_, node, state)| { - hop_arrow_graph(node, query, step + 1, state, graph, s, tl); + hop_disk_graph(node, query, step + 1, state, graph, s, tl); }); }), Direction::IN => s.spawn(move |s| { @@ -165,7 +165,7 @@ fn hop_arrow_graph<'a, S: HopState + 'a>( }) .take(limit) .for_each(|(_, node, state)| { - hop_arrow_graph(node, query, step + 1, state, graph, s, tl); + hop_disk_graph(node, query, step + 1, state, graph, s, tl); }); }), Direction::BOTH => { diff --git a/raphtory/src/arrow/query/mod.rs b/raphtory/src/disk_graph/query/mod.rs similarity index 92% rename from raphtory/src/arrow/query/mod.rs rename to raphtory/src/disk_graph/query/mod.rs index 6d8356732b..2627ceb4f4 100644 --- a/raphtory/src/arrow/query/mod.rs +++ b/raphtory/src/disk_graph/query/mod.rs @@ -1,4 +1,4 @@ -use raphtory_arrow::{edge::Edge, GID}; +use pometry_storage::{edge::Edge, GID}; use std::sync::Arc; use crate::{ @@ -9,8 +9,8 @@ use crate::{ use self::state::HopState; use crate::core::storage::timeindex::TimeIndexOps; -use super::graph_impl::ArrowGraph; -use raphtory_arrow::nodes::Node; +use super::graph_impl::DiskGraph; +use pometry_storage::nodes::Node; pub mod ast; pub mod executors; @@ -21,11 +21,11 @@ pub enum NodeSource { All, NodeIds(Vec), ExternalIds(Vec), - Filter(Arc bool + Send + Sync>), + Filter(Arc bool + Send + Sync>), } impl NodeSource { - fn into_iter(self, graph: &ArrowGraph) -> Box + '_> { + fn into_iter(self, graph: &DiskGraph) -> Box + '_> { match self { NodeSource::All => Box::new((0..graph.inner.num_nodes()).map(VID)), NodeSource::NodeIds(ids) => Box::new(ids.into_iter()), @@ -120,7 +120,7 @@ mod test { g.add_edge(0, 0u64, 1, NO_PROPS, None).unwrap(); g.add_edge(1, 1u64, 2, NO_PROPS, None).unwrap(); - let graph = ArrowGraph::from_graph(&g, graph_dir.path()).unwrap(); + let graph = DiskGraph::from_graph(&g, graph_dir.path()).unwrap(); let result = rayon2::execute::(query, NodeSource::All, &graph, |_| NoState::new()); assert!(result.is_ok()); @@ -146,7 +146,7 @@ mod test { g.add_edge(0, 0u64, 1, NO_PROPS, None).unwrap(); g.add_edge(1, 1u64, 2, NO_PROPS, None).unwrap(); - let graph = ArrowGraph::from_graph(&g, graph_dir.path()).unwrap(); + let graph = DiskGraph::from_graph(&g, graph_dir.path()).unwrap(); let result = rayon2::execute::(query, NodeSource::All, &graph, |_| NoState::new()); assert!(result.is_ok()); @@ -170,7 +170,7 @@ mod test { g.add_edge(0, 0u64, 1, NO_PROPS, None).unwrap(); g.add_edge(1, 1u64, 2, NO_PROPS, None).unwrap(); - let graph = ArrowGraph::from_graph(&g, graph_dir.path()).unwrap(); + let graph = DiskGraph::from_graph(&g, graph_dir.path()).unwrap(); let result = rayon2::execute::(query, NodeSource::All, &graph, VecState::new); assert!(result.is_ok()); @@ -203,7 +203,7 @@ mod test { g.add_edge(0, 0u64, 1, NO_PROPS, None).unwrap(); g.add_edge(1, 1u64, 2, NO_PROPS, None).unwrap(); - let graph = ArrowGraph::from_graph(&g, graph_dir.path()).unwrap(); + let graph = DiskGraph::from_graph(&g, graph_dir.path()).unwrap(); let result = rayon2::execute::(query, NodeSource::All, &graph, VecState::new); assert!(result.is_ok()); @@ -228,7 +228,7 @@ mod test { g.add_edge(1, 1u64, 2, NO_PROPS, None).unwrap(); g.add_edge(2, 1u64, 3, NO_PROPS, None).unwrap(); - let graph = ArrowGraph::from_graph(&g, graph_dir.path()).unwrap(); + let graph = DiskGraph::from_graph(&g, graph_dir.path()).unwrap(); let result = rayon2::execute::(query, NodeSource::All, &graph, VecState::new); assert!(result.is_ok()); @@ -279,7 +279,7 @@ mod test { g.add_edge(*t, *src, *dst, NO_PROPS, None).unwrap(); } - let graph = ArrowGraph::from_graph(&g, graph_dir.path()).unwrap(); + let graph = DiskGraph::from_graph(&g, graph_dir.path()).unwrap(); let t = 10; let result = rayon2::execute::( diff --git a/raphtory/src/arrow/query/state.rs b/raphtory/src/disk_graph/query/state.rs similarity index 97% rename from raphtory/src/arrow/query/state.rs rename to raphtory/src/disk_graph/query/state.rs index e91a89015e..39b1f556d8 100644 --- a/raphtory/src/arrow/query/state.rs +++ b/raphtory/src/disk_graph/query/state.rs @@ -1,4 +1,4 @@ -use raphtory_arrow::{edge::Edge, nodes::Node}; +use pometry_storage::{edge::Edge, nodes::Node}; use std::option::Option; use crate::{ diff --git a/raphtory/src/arrow/storage_interface/edge.rs b/raphtory/src/disk_graph/storage_interface/edge.rs similarity index 86% rename from raphtory/src/arrow/storage_interface/edge.rs rename to raphtory/src/disk_graph/storage_interface/edge.rs index b4f11038f6..7616864d25 100644 --- a/raphtory/src/arrow/storage_interface/edge.rs +++ b/raphtory/src/disk_graph/storage_interface/edge.rs @@ -5,34 +5,34 @@ use crate::{ }, db::api::storage::edges::edge_storage_ops::EdgeStorageIntoOps, }; +use pometry_storage::{edge::Edge, edges::Edges, graph::TemporalGraph, timestamps::TimeStamps}; use raphtory_api::core::storage::timeindex::TimeIndexEntry; -use raphtory_arrow::{edge::Edge, edges::Edges, graph::TemporalGraph, timestamps::TimeStamps}; use std::ops::Range; -pub type ArrowEdge<'a> = Edge<'a>; +pub type DiskEdge<'a> = Edge<'a>; #[derive(Debug, Clone)] -pub struct ArrowOwnedEdge { +pub struct DiskOwnedEdge { edges: Edges, eid: EID, } -impl ArrowOwnedEdge { +impl DiskOwnedEdge { pub(crate) fn new(graph: &TemporalGraph, eid: ELID) -> Self { let layer = eid .layer() - .expect("arrow EdgeRefs should have layer always defined"); + .expect("disk_graph EdgeRefs should have layer always defined"); Self { edges: graph.layer(layer).edges_storage().clone(), eid: eid.pid(), } } - pub fn as_ref(&self) -> ArrowEdge { + pub fn as_ref(&self) -> DiskEdge { self.edges.edge(self.eid) } } -impl EdgeStorageIntoOps for ArrowOwnedEdge { +impl EdgeStorageIntoOps for DiskOwnedEdge { fn into_layers( self, layer_ids: LayerIds, diff --git a/raphtory/src/arrow/storage_interface/edges.rs b/raphtory/src/disk_graph/storage_interface/edges.rs similarity index 88% rename from raphtory/src/arrow/storage_interface/edges.rs rename to raphtory/src/disk_graph/storage_interface/edges.rs index 02ce4bfd9c..443e199bab 100644 --- a/raphtory/src/arrow/storage_interface/edges.rs +++ b/raphtory/src/disk_graph/storage_interface/edges.rs @@ -1,25 +1,25 @@ use crate::{ - arrow::storage_interface::{edge::ArrowEdge, edges_ref::ArrowEdgesRef}, core::entities::{LayerIds, EID}, db::api::storage::variants::layer_variants::LayerVariants, + disk_graph::storage_interface::{edge::DiskEdge, edges_ref::DiskEdgesRef}, }; -use raphtory_arrow::{graph::TemporalGraph, graph_fragment::TempColGraphFragment}; +use pometry_storage::{graph::TemporalGraph, graph_fragment::TempColGraphFragment}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use std::{iter, sync::Arc}; #[derive(Clone, Debug)] -pub struct ArrowEdges { +pub struct DiskEdges { layers: Arc<[TempColGraphFragment]>, } -impl ArrowEdges { +impl DiskEdges { pub(crate) fn new(graph: &TemporalGraph) -> Self { Self { layers: graph.arc_layers().clone(), } } - pub fn as_ref(&self) -> ArrowEdgesRef { - ArrowEdgesRef { + pub fn as_ref(&self) -> DiskEdgesRef { + DiskEdgesRef { layers: &self.layers, } } @@ -75,7 +75,7 @@ impl ArrowEdges { } } - pub fn get(&self, eid: EID, layer_id: usize) -> ArrowEdge { + pub fn get(&self, eid: EID, layer_id: usize) -> DiskEdge { self.layers[layer_id].edge(eid) } } diff --git a/raphtory/src/arrow/storage_interface/edges_ref.rs b/raphtory/src/disk_graph/storage_interface/edges_ref.rs similarity index 90% rename from raphtory/src/arrow/storage_interface/edges_ref.rs rename to raphtory/src/disk_graph/storage_interface/edges_ref.rs index 3f7a62d7cc..5e08e30dc4 100644 --- a/raphtory/src/arrow/storage_interface/edges_ref.rs +++ b/raphtory/src/disk_graph/storage_interface/edges_ref.rs @@ -1,29 +1,29 @@ use crate::{ - arrow::storage_interface::edge::ArrowEdge, core::entities::{LayerIds, EID}, db::api::storage::variants::layer_variants::LayerVariants, + disk_graph::storage_interface::edge::DiskEdge, }; -use raphtory_arrow::{graph::TemporalGraph, graph_fragment::TempColGraphFragment}; +use pometry_storage::{graph::TemporalGraph, graph_fragment::TempColGraphFragment}; use rayon::iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator}; use std::iter; #[derive(Copy, Clone, Debug)] -pub struct ArrowEdgesRef<'a> { +pub struct DiskEdgesRef<'a> { pub(super) layers: &'a [TempColGraphFragment], } -impl<'a> ArrowEdgesRef<'a> { +impl<'a> DiskEdgesRef<'a> { pub(crate) fn new(storage: &'a TemporalGraph) -> Self { Self { layers: storage.layers(), } } - pub fn edge(self, eid: EID, layer_id: usize) -> ArrowEdge<'a> { + pub fn edge(self, eid: EID, layer_id: usize) -> DiskEdge<'a> { self.layers[layer_id].edge(eid) } - pub fn iter(self, layers: LayerIds) -> impl Iterator> { + pub fn iter(self, layers: LayerIds) -> impl Iterator> { match layers { LayerIds::None => LayerVariants::None(iter::empty()), LayerIds::All => LayerVariants::All( @@ -43,7 +43,7 @@ impl<'a> ArrowEdgesRef<'a> { } } - pub fn par_iter(self, layers: LayerIds) -> impl ParallelIterator> { + pub fn par_iter(self, layers: LayerIds) -> impl ParallelIterator> { match layers { LayerIds::None => LayerVariants::None(rayon::iter::empty()), LayerIds::All => LayerVariants::All( diff --git a/raphtory/src/arrow/storage_interface/mod.rs b/raphtory/src/disk_graph/storage_interface/mod.rs similarity index 100% rename from raphtory/src/arrow/storage_interface/mod.rs rename to raphtory/src/disk_graph/storage_interface/mod.rs diff --git a/raphtory/src/arrow/storage_interface/node.rs b/raphtory/src/disk_graph/storage_interface/node.rs similarity index 97% rename from raphtory/src/arrow/storage_interface/node.rs rename to raphtory/src/disk_graph/storage_interface/node.rs index 24bb91563e..3afcda05d3 100644 --- a/raphtory/src/arrow/storage_interface/node.rs +++ b/raphtory/src/disk_graph/storage_interface/node.rs @@ -13,17 +13,17 @@ use crate::{ }, }; use itertools::Itertools; -use raphtory_arrow::{graph::TemporalGraph, timestamps::TimeStamps, GidRef}; +use pometry_storage::{graph::TemporalGraph, timestamps::TimeStamps, GidRef}; use rayon::iter::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; use std::{iter, sync::Arc}; #[derive(Copy, Clone, Debug)] -pub struct ArrowNode<'a> { +pub struct DiskNode<'a> { graph: &'a TemporalGraph, pub(super) vid: VID, } -impl<'a> ArrowNode<'a> { +impl<'a> DiskNode<'a> { pub(crate) fn new(graph: &'a TemporalGraph, vid: VID) -> Self { Self { graph, vid } } @@ -165,7 +165,7 @@ impl<'a> ArrowNode<'a> { } } -impl<'a> NodeStorageOps<'a> for ArrowNode<'a> { +impl<'a> NodeStorageOps<'a> for DiskNode<'a> { fn degree(self, layers: &LayerIds, dir: Direction) -> usize { let single_layer = match layers { LayerIds::None => return 0, @@ -270,7 +270,7 @@ impl<'a> NodeStorageOps<'a> for ArrowNode<'a> { .find_edge(self.vid, dst)?; Some(EdgeRef::new_outgoing(eid, self.vid, dst).at_layer(0)) } - _ => todo!("multilayer edge views not implemented in arrow yet"), + _ => todo!("multilayer edge views not implemented in diskgraph yet"), }, LayerIds::One(id) => { let eid = self.graph.layers()[*id] @@ -287,24 +287,24 @@ impl<'a> NodeStorageOps<'a> for ArrowNode<'a> { .find_edge(self.vid, dst)?; Some(EdgeRef::new_outgoing(eid, self.vid, dst).at_layer(layer)) } - _ => todo!("multtilayer edge views not implemented in arrow yet"), + _ => todo!("multtilayer edge views not implemented in diskgraph yet"), }, } } } #[derive(Clone, Debug)] -pub struct ArrowOwnedNode { +pub struct DiskOwnedNode { graph: Arc, vid: VID, } -impl ArrowOwnedNode { +impl DiskOwnedNode { pub(crate) fn new(graph: Arc, vid: VID) -> Self { Self { graph, vid } } - pub fn as_ref(&self) -> ArrowNode { - ArrowNode { + pub fn as_ref(&self) -> DiskNode { + DiskNode { graph: &self.graph, vid: self.vid, } @@ -451,7 +451,7 @@ impl ArrowOwnedNode { } } -impl<'a> NodeStorageOps<'a> for &'a ArrowOwnedNode { +impl<'a> NodeStorageOps<'a> for &'a DiskOwnedNode { #[inline] fn degree(self, layers: &LayerIds, dir: Direction) -> usize { self.as_ref().degree(layers, dir) @@ -503,7 +503,7 @@ impl<'a> NodeStorageOps<'a> for &'a ArrowOwnedNode { } } -impl NodeStorageIntoOps for ArrowOwnedNode { +impl NodeStorageIntoOps for DiskOwnedNode { fn into_edges_iter(self, layers: LayerIds, dir: Direction) -> impl Iterator { match dir { Direction::OUT => DirectionVariants::Out(self.out_edges(layers)), diff --git a/raphtory/src/disk_graph/storage_interface/nodes.rs b/raphtory/src/disk_graph/storage_interface/nodes.rs new file mode 100644 index 0000000000..fa05594b6c --- /dev/null +++ b/raphtory/src/disk_graph/storage_interface/nodes.rs @@ -0,0 +1,26 @@ +use crate::{ + core::entities::VID, + disk_graph::storage_interface::{node::DiskNode, nodes_ref::DiskNodesRef}, +}; + +use pometry_storage::graph::TemporalGraph; +use std::sync::Arc; + +#[derive(Clone, Debug)] +pub struct DiskNodesOwned { + graph: Arc, +} + +impl DiskNodesOwned { + pub(crate) fn new(graph: Arc) -> Self { + Self { graph } + } + + pub fn node(&self, vid: VID) -> DiskNode { + DiskNode::new(&self.graph, vid) + } + + pub fn as_ref(&self) -> DiskNodesRef { + DiskNodesRef::new(&self.graph) + } +} diff --git a/raphtory/src/arrow/storage_interface/nodes_ref.rs b/raphtory/src/disk_graph/storage_interface/nodes_ref.rs similarity index 59% rename from raphtory/src/arrow/storage_interface/nodes_ref.rs rename to raphtory/src/disk_graph/storage_interface/nodes_ref.rs index 441bdbad60..1f66fbdcae 100644 --- a/raphtory/src/arrow/storage_interface/nodes_ref.rs +++ b/raphtory/src/disk_graph/storage_interface/nodes_ref.rs @@ -1,28 +1,28 @@ -use crate::{arrow::storage_interface::node::ArrowNode, core::entities::VID}; -use raphtory_arrow::graph::TemporalGraph; +use crate::{core::entities::VID, disk_graph::storage_interface::node::DiskNode}; +use pometry_storage::graph::TemporalGraph; use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}; #[derive(Copy, Clone, Debug)] -pub struct ArrowNodesRef<'a> { +pub struct DiskNodesRef<'a> { graph: &'a TemporalGraph, } -impl<'a> ArrowNodesRef<'a> { +impl<'a> DiskNodesRef<'a> { pub(crate) fn new(graph: &'a TemporalGraph) -> Self { Self { graph } } - pub fn node(self, vid: VID) -> ArrowNode<'a> { - ArrowNode::new(self.graph, vid) + pub fn node(self, vid: VID) -> DiskNode<'a> { + DiskNode::new(self.graph, vid) } - pub fn par_iter(self) -> impl IndexedParallelIterator> { + pub fn par_iter(self) -> impl IndexedParallelIterator> { (0..self.graph.num_nodes()) .into_par_iter() .map(move |vid| self.node(VID(vid))) } - pub fn iter(self) -> impl Iterator> { + pub fn iter(self) -> impl Iterator> { (0..self.graph.num_nodes()).map(move |vid| self.node(VID(vid))) } } diff --git a/raphtory/src/lib.rs b/raphtory/src/lib.rs index 1aafebaa75..cc29983e9a 100644 --- a/raphtory/src/lib.rs +++ b/raphtory/src/lib.rs @@ -88,8 +88,8 @@ pub mod core; pub mod db; pub mod graphgen; -#[cfg(feature = "arrow")] -pub mod arrow; +#[cfg(feature = "storage")] +pub mod disk_graph; #[cfg(all(feature = "python", not(doctest)))] // no doctests in python as the docstrings are python not rust format @@ -121,5 +121,5 @@ pub mod prelude { } pub const BINCODE_VERSION: u32 = 1u32; -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] pub use polars_arrow as arrow2; diff --git a/raphtory/src/python/graph/arrow.rs b/raphtory/src/python/graph/disk_graph.rs similarity index 90% rename from raphtory/src/python/graph/arrow.rs rename to raphtory/src/python/graph/disk_graph.rs index 77c0e4d722..826076477f 100644 --- a/raphtory/src/python/graph/arrow.rs +++ b/raphtory/src/python/graph/disk_graph.rs @@ -1,11 +1,6 @@ use std::{io::Write, sync::Arc}; use crate::{ - arrow::{ - graph_impl::{ArrowGraph, ParquetLayerCols}, - query::{ast::Query, executors::rayon2, state::StaticGraphHopState, NodeSource}, - Error, - }, arrow2::{ array::StructArray, datatypes::{ArrowDataType as DataType, Field}, @@ -18,6 +13,11 @@ use crate::{ api::view::{DynamicGraph, IntoDynamic}, graph::{edge::EdgeView, node::NodeView}, }, + disk_graph::{ + graph_impl::{DiskGraph, ParquetLayerCols}, + query::{ast::Query, executors::rayon2, state::StaticGraphHopState, NodeSource}, + Error, + }, prelude::{EdgeViewOps, GraphViewOps, NodeViewOps, TimeOps}, python::{ graph::{edge::PyDirection, graph::PyGraph, views::graph_view::PyGraphView}, @@ -26,12 +26,12 @@ use crate::{ }, }; use itertools::Itertools; +use pometry_storage::GID; /// A columnar temporal graph. use pyo3::{ prelude::*, types::{IntoPyDict, PyDict, PyList, PyString}, }; -use raphtory_arrow::GID; use super::pandas::dataframe::{process_pandas_py_df, PretendDF}; @@ -42,52 +42,52 @@ impl From for PyErr { } #[derive(Clone)] -#[pyclass(name = "ArrowGraph", extends = PyGraphView)] -pub struct PyArrowGraph { - pub graph: ArrowGraph, +#[pyclass(name = "DiskGraph", extends = PyGraphView)] +pub struct PyDiskGraph { + pub graph: DiskGraph, } -impl AsRef for PyArrowGraph +impl AsRef for PyDiskGraph where - ArrowGraph: AsRef, + DiskGraph: AsRef, { fn as_ref(&self) -> &G { self.graph.as_ref() } } -impl From for PyArrowGraph { - fn from(value: ArrowGraph) -> Self { +impl From for PyDiskGraph { + fn from(value: DiskGraph) -> Self { Self { graph: value } } } -impl From for ArrowGraph { - fn from(value: PyArrowGraph) -> Self { +impl From for DiskGraph { + fn from(value: PyDiskGraph) -> Self { value.graph } } -impl From for DynamicGraph { - fn from(value: PyArrowGraph) -> Self { +impl From for DynamicGraph { + fn from(value: PyDiskGraph) -> Self { value.graph.into_dynamic() } } -impl IntoPy for ArrowGraph { +impl IntoPy for DiskGraph { fn into_py(self, py: Python<'_>) -> PyObject { Py::new( py, - (PyArrowGraph::from(self.clone()), PyGraphView::from(self)), + (PyDiskGraph::from(self.clone()), PyGraphView::from(self)), ) .unwrap() .into_py(py) } } -impl<'source> FromPyObject<'source> for ArrowGraph { +impl<'source> FromPyObject<'source> for DiskGraph { fn extract(ob: &'source PyAny) -> PyResult { - let py_graph: PyRef = ob.extract()?; + let py_graph: PyRef = ob.extract()?; Ok(py_graph.graph.clone()) } } @@ -141,14 +141,14 @@ impl<'a> FromPyObject<'a> for ParquetLayerColsList<'a> { #[pymethods] impl PyGraph { - /// save graph in arrow format and memory map the result - pub fn persist_as_arrow(&self, graph_dir: &str) -> Result { - self.graph.persist_as_arrow(graph_dir) + /// save graph in disk_graph format and memory map the result + pub fn persist_as_disk_graph(&self, graph_dir: &str) -> Result { + self.graph.persist_as_disk_graph(graph_dir) } } #[pymethods] -impl PyArrowGraph { +impl PyDiskGraph { #[staticmethod] #[pyo3(signature = (graph_dir, edge_df, src_col, dst_col, time_col))] pub fn load_from_pandas( @@ -157,8 +157,8 @@ impl PyArrowGraph { src_col: &str, dst_col: &str, time_col: &str, - ) -> Result { - let graph: Result = Python::with_gil(|py| { + ) -> Result { + let graph: Result = Python::with_gil(|py| { let size: usize = py .eval( "index.__len__()", @@ -188,8 +188,8 @@ impl PyArrowGraph { } #[staticmethod] - fn load_from_dir(graph_dir: &str) -> Result { - ArrowGraph::load_from_dir(graph_dir).map_err(|err| { + fn load_from_dir(graph_dir: &str) -> Result { + DiskGraph::load_from_dir(graph_dir).map_err(|err| { GraphError::LoadFailure(format!("Failed to load graph {err:?} from dir {graph_dir}")) }) } @@ -205,7 +205,7 @@ impl PyArrowGraph { read_chunk_size: Option, concurrent_files: Option, num_threads: usize, - ) -> Result { + ) -> Result { let graph = Self::from_parquets( graph_dir, layer_parquet_cols.0, @@ -222,7 +222,7 @@ impl PyArrowGraph { } fn __repr__(&self) -> String { - StructReprBuilder::new("ArrowGraph") + StructReprBuilder::new("DiskGraph") .add_field("number_of_nodes", self.graph.count_nodes()) .add_field( "number_of_temporal_edges", @@ -234,14 +234,14 @@ impl PyArrowGraph { } } -impl PyArrowGraph { +impl PyDiskGraph { fn from_pandas( graph_dir: &str, df: PretendDF, src: &str, dst: &str, time: &str, - ) -> Result { + ) -> Result { let src_col_idx = df.names.iter().position(|x| x == src).unwrap(); let dst_col_idx = df.names.iter().position(|x| x == dst).unwrap(); let time_col_idx = df.names.iter().position(|x| x == time).unwrap(); @@ -272,7 +272,7 @@ impl PyArrowGraph { }) .collect::>(); - ArrowGraph::load_from_edge_lists( + DiskGraph::load_from_edge_lists( &edge_lists, chunk_size, t_props_chunk_size, @@ -293,8 +293,8 @@ impl PyArrowGraph { read_chunk_size: Option, concurrent_files: Option, num_threads: usize, - ) -> Result { - ArrowGraph::load_from_parquets( + ) -> Result { + DiskGraph::load_from_parquets( graph_dir, layer_parquet_cols, node_properties, @@ -527,7 +527,7 @@ impl PyGraphQuery { } } - pub fn run(&self, graph: ArrowGraph, state: PyState) -> PyResult<()> { + pub fn run(&self, graph: DiskGraph, state: PyState) -> PyResult<()> { rayon2::execute_static_graph( self.query.clone(), self.source.as_ref().clone(), @@ -541,9 +541,9 @@ impl PyGraphQuery { pub fn run_to_vec( &self, - graph: ArrowGraph, + graph: DiskGraph, state: PyState, - ) -> PyResult>, NodeView)>> { + ) -> PyResult>, NodeView)>> { let (sender, receiver) = std::sync::mpsc::channel(); let query = self.query.clone().channel([sender]); diff --git a/raphtory/src/python/graph/graph.rs b/raphtory/src/python/graph/graph.rs index bcbc295c4f..4347b87ad8 100644 --- a/raphtory/src/python/graph/graph.rs +++ b/raphtory/src/python/graph/graph.rs @@ -24,7 +24,7 @@ use pyo3::{prelude::*, types::PyBytes}; use std::{ collections::HashMap, fmt::{Debug, Formatter}, - path::{Path, PathBuf}, + path::Path, }; /// A temporal graph. diff --git a/raphtory/src/python/graph/mod.rs b/raphtory/src/python/graph/mod.rs index 8b82eeb5de..7b457b6a1b 100644 --- a/raphtory/src/python/graph/mod.rs +++ b/raphtory/src/python/graph/mod.rs @@ -1,6 +1,8 @@ +#![allow(non_local_definitions)] + pub mod algorithm_result; -#[cfg(feature = "arrow")] -pub mod arrow; +#[cfg(feature = "storage")] +pub mod disk_graph; pub mod edge; pub mod graph; pub mod graph_with_deletions; diff --git a/raphtory/src/python/graph/views/graph_view.rs b/raphtory/src/python/graph/views/graph_view.rs index ff5b011856..b4ee6156be 100644 --- a/raphtory/src/python/graph/views/graph_view.rs +++ b/raphtory/src/python/graph/views/graph_view.rs @@ -38,8 +38,8 @@ use pyo3::{prelude::*, types::PyBytes}; impl IntoPy for MaterializedGraph { fn into_py(self, py: Python<'_>) -> PyObject { match self { - #[cfg(feature = "arrow")] - MaterializedGraph::ArrowEventGraph(g) => g.into_py(py), + #[cfg(feature = "storage")] + MaterializedGraph::DiskEventGraph(g) => g.into_py(py), MaterializedGraph::EventGraph(g) => g.into_py(py), MaterializedGraph::PersistentGraph(g) => g.into_py(py), } diff --git a/raphtory/src/python/packages/algorithms.rs b/raphtory/src/python/packages/algorithms.rs index 3d2f8ca93e..067efde4b7 100644 --- a/raphtory/src/python/packages/algorithms.rs +++ b/raphtory/src/python/packages/algorithms.rs @@ -59,11 +59,11 @@ use pyo3::prelude::*; use rand::{prelude::StdRng, SeedableRng}; use std::collections::{HashMap, HashSet}; -#[cfg(feature = "arrow")] -use raphtory_arrow::algorithms::connected_components::connected_components as connected_components_rs; +#[cfg(feature = "storage")] +use pometry_storage::algorithms::connected_components::connected_components as connected_components_rs; -#[cfg(feature = "arrow")] -use crate::python::graph::arrow::PyArrowGraph; +#[cfg(feature = "storage")] +use crate::python::graph::disk_graph::PyDiskGraph; /// Implementations of various graph algorithms that can be run on a graph. /// @@ -123,10 +123,10 @@ pub fn strongly_connected_components( components::strongly_connected_components(&g.graph, None) } -#[cfg(feature = "arrow")] +#[cfg(feature = "storage")] #[pyfunction] #[pyo3(signature = (g))] -pub fn connected_components(g: &PyArrowGraph) -> Vec { +pub fn connected_components(g: &PyDiskGraph) -> Vec { connected_components_rs(g.graph.as_ref()) } diff --git a/raphtory/src/python/types/wrappers/document.rs b/raphtory/src/python/types/wrappers/document.rs index 53644e5f3c..6049268894 100644 --- a/raphtory/src/python/types/wrappers/document.rs +++ b/raphtory/src/python/types/wrappers/document.rs @@ -1,3 +1,4 @@ +#![allow(non_local_definitions)] use crate::core::{DocumentInput, Lifespan}; use itertools::Itertools; use pyo3::{exceptions::PyAttributeError, prelude::*, types::PyTuple}; diff --git a/resource/netflowsorted/nft_sorted/nft2.parquet b/resource/netflowsorted/nft_sorted/nft2.parquet deleted file mode 100644 index 8c41baa1d385e95dad7095cbdd40d36599b853e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68981 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 O*I7u`GPKX|`2Pc~?8^=S diff --git a/resource/netflowsorted/v1_sorted/v1.parquet b/resource/netflowsorted/v1_sorted/v1.parquet deleted file mode 100644 index 522eecc3766fc2b77c0fe096e772ecee514f4a24..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76219 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∾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 diff --git a/resource/netflowsorted/v2_sorted/v2.parquet b/resource/netflowsorted/v2_sorted/v2.parquet deleted file mode 100644 index 4d0fb2940d4d7677312c4ac19c8239ddbc29c568..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 97326 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

ZX4w5#Z0vUGZo*e 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 diff --git a/scripts/activate_private_arrow.py b/scripts/activate_private_storage.py similarity index 56% rename from scripts/activate_private_arrow.py rename to scripts/activate_private_storage.py index 5711de5513..319c5fcd09 100755 --- a/scripts/activate_private_arrow.py +++ b/scripts/activate_private_storage.py @@ -3,7 +3,7 @@ from pathlib import Path import re -directory = "./raphtory-arrow" +directory = "./pometry-storage" root_dir = Path(__file__).parent.parent toml_file = root_dir / "Cargo.toml" @@ -11,14 +11,14 @@ lines = f.readlines() for i, line in enumerate(lines[:-1]): - if "#[private-arrow]" in line: + if "#[private-storage]" in line: next_line = lines[i + 1] - if next_line.strip().startswith("#") and "raphtory-arrow" in next_line: + if next_line.strip().startswith("#") and "pometry-storage" in next_line: lines[i + 1] = re.sub(r"#\s*", "", next_line, 1) - if "#[public-arrow]" in line: + if "#[public-storage]" in line: next_line = lines[i + 1] - if next_line.strip().startswith("raphtory-arrow"): - lines[i + 1] = next_line.replace("raphtory-arrow", "# raphtory-arrow", 1) + if next_line.strip().startswith("pometry-storage"): + lines[i + 1] = next_line.replace("pometry-storage", "# pometry-storage", 1) with open(toml_file, "w") as f: f.writelines(lines) diff --git a/scripts/deactivate_private_arrow.py b/scripts/deactivate_private_storage.py similarity index 56% rename from scripts/deactivate_private_arrow.py rename to scripts/deactivate_private_storage.py index c928ab6e30..4d287ef876 100755 --- a/scripts/deactivate_private_arrow.py +++ b/scripts/deactivate_private_storage.py @@ -3,7 +3,7 @@ from pathlib import Path import re -directory = "./raphtory-arrow" +directory = "./pometry-storage" root_dir = Path(__file__).parent.parent toml_file = root_dir / "Cargo.toml" @@ -11,14 +11,14 @@ lines = f.readlines() for i, line in enumerate(lines[:-1]): - if "#[public-arrow]" in line: + if "#[public-storage]" in line: next_line = lines[i + 1] - if next_line.strip().startswith("#") and "raphtory-arrow" in next_line: + if next_line.strip().startswith("#") and "pometry-storage" in next_line: lines[i + 1] = re.sub(r"#\s*", "", next_line, 1) - if "#[private-arrow]" in line: + if "#[private-storage]" in line: next_line = lines[i + 1] - if next_line.strip().startswith("raphtory-arrow"): - lines[i + 1] = next_line.replace("raphtory-arrow", "# raphtory-arrow", 1) + if next_line.strip().startswith("pometry-storage"): + lines[i + 1] = next_line.replace("pometry-storage", "# pometry-storage", 1) with open(toml_file, "w") as f: f.writelines(lines)