{"id":21703712,"url":"https://github.com/carvilsi/gruphst","last_synced_at":"2025-04-12T15:12:45.710Z","repository":{"id":246818238,"uuid":"819055958","full_name":"carvilsi/gruphst","owner":"carvilsi","description":"Rust in-memory graph databse","archived":false,"fork":false,"pushed_at":"2025-04-12T15:02:41.000Z","size":1642,"stargazers_count":8,"open_issues_count":1,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-12T15:12:40.699Z","etag":null,"topics":["graph-database","in-memory-database","persistece","rust","rust-library"],"latest_commit_sha":null,"homepage":"https://docs.rs/gruphst/latest/gruphst","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/carvilsi.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-06-23T16:46:50.000Z","updated_at":"2025-04-09T09:02:02.000Z","dependencies_parsed_at":"2024-09-07T07:45:53.044Z","dependency_job_id":"99c2b14e-b3e3-4fbe-a688-fec004cc92ff","html_url":"https://github.com/carvilsi/gruphst","commit_stats":null,"previous_names":["carvilsi/gruphst"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/carvilsi%2Fgruphst","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/carvilsi%2Fgruphst/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/carvilsi%2Fgruphst/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/carvilsi%2Fgruphst/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/carvilsi","download_url":"https://codeload.github.com/carvilsi/gruphst/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248586245,"owners_count":21128998,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["graph-database","in-memory-database","persistece","rust","rust-library"],"created_at":"2024-11-25T21:34:52.935Z","updated_at":"2025-04-12T15:12:45.685Z","avatar_url":"https://github.com/carvilsi.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv class=\"text\" align=\"center\"\u003e\n    \u003cimg src=\"https://img.shields.io/github/actions/workflow/status/carvilsi/gruphst/test.yml?logo=github\u0026label=tests\" alt=\"test\"\u003e\n    \u003cimg src=\"https://img.shields.io/crates/v/gruphst.svg\" alt=\"crates\"\u003e\n    \u003cimg src=\"https://img.shields.io/docsrs/gruphst/latest\" alt=\"docs\"\u003e\n    \u003cimg src=\"https://codecov.io/github/carvilsi/gruphst/graph/badge.svg?token=W1XVSQB3H0\" alt=\"cov\"/\u003e \n    \u003cimg src=\"https://img.shields.io/badge/License-MIT-purple.svg\" alt=\"cov\"/\u003e \n    \u003cp\u003e\u003c/p\u003e\n    \u003cp\u003eGruPHst\u003c/p\u003e\n    \u003cp\u003eAn in-memory graph database\u003c/p\u003e\n\u003c/div\u003e \n\n---\n\n# GruPHst\n\nPossible to persists on file (just because is something that we always expect from an in-memory databases).\n\nEarly state of development with lot of TODOs, just doing nerdy things with Graph Databases while trying to learn some Rust.\n\n[Documentation](https://docs.rs/gruphst/latest/gruphst/)\n\n[Code Coverage](https://app.codecov.io/github/carvilsi/gruphst)\n\n---\n\n1. [Basic Usage](#basic-usage)\n2. [Install](#install)\n3. [Tests \u0026 Coverage \u0026 Benchmarking](#tests-coverage-benchmarking)\n4. [Configuration](#configuration)\n    1. [Configurable variables](#configurable-variables)\n    2. [Maximum memory usage](#maximum-memory-usage)\n    3. [Level for logging](#level-for-logging) \n    4. [Character delimiter for CSV file](#character-delimiter-for-csv-file)\n5. [Save \u0026 Load](#save-load)\n6. [Export \u0026 Import](#export-import)\n    1. [CSV](#csv)\n        1. [File Format](#file-format)\n        2. [Export \u0026 Import Usage](#export-import-usage)\n7. [Cryptography](#cryptography)\n    1. [Argon2 Hashes](#argon2-hashes)\n8. [Examples](#examples)\n\n---\n\n## Basic Usage\u003ca name=\"basic-usage\"\u003e\n\n```rust\nuse gruphst::{edge::Edge, graphs::Graphs, vertex::Vertex};\nuse std::error::Error;\n\n// The idea it's to create some graph related with \n// the Middle-Earth, relating some characters and\n// places\n\nfn main() -\u003e Result\u003c(), Box\u003cdyn Error\u003e\u003e {\n    // Create a new vertex\n    let frodo = Vertex::new(\"Frodo\");\n       \n    // Let's create another vertex\n    let mut gandalf = Vertex::new(\"Gandalf\");\n     \n    // A vertex can have attributes\n    gandalf.set_attr(\"known as\", \"The Gray\");\n    gandalf.set_attr(\"years old\", 24000);\n\n    // Now lets make a relation between these two friends\n    // by creating an Edge\n    let mut edge = Edge::create(\u0026gandalf, \"friend of\", \u0026frodo);\n\n    // An Edge can have attributes\n    edge.set_attr(\"duration in years\", 42);\n\n    // Now we need something to hold, and store the created Edge\n    // and the new ones that we'll create later.\n    // Lets init a Graphs, we could do this step at the begining \n    // of the main function.\n    let mut graphs = Graphs::init(\"middle-earth\");\n    \n    // Now we add the edge or relation between Gandalf and Frodo\n    graphs.add_edge(\u0026edge, None);\n\n    // We can add another relation or Edge to the graphs\n    // for these two friends, e.g.\n    graphs.add_edge(\u0026Edge::create(\u0026frodo, \"has best friend\", \u0026gandalf), None);\n\n    // Lets create more vertices for places and characters and edges\n    // for the relation between them\n    let mut sam = Vertex::new(\"Samwise\");\n    sam.set_attr(\"surname\", \"Gamgee\");\n    graphs.add_edge(\n        \u0026Edge::create(\n            \u0026sam,\n            \"has best friend\",\n            \u0026frodo),\n        None);\n\n    let mut vertex = Vertex::new(\"The Shire\");\n\n    // Vertices and Edges has a uuid generated on creation\n    let id_vertex_the_shire = vertex.get_id();\n\n    graphs.add_edge(\u0026Edge::create(\u0026frodo, \"lives at\", \u0026vertex), None); \n\n    vertex = Vertex::new(\"Isengard\");\n    vertex.set_attr(\"type\", \"tower\");\n\n    graphs.add_edge(\u0026Edge::create(\u0026Vertex::new(\"Saruman\"), \"lives at\", \u0026vertex), None); \n\n    // we can use the id or the label to retrieve a Vertex that we have on Graph\n    let the_shire = graphs.find_vertex_by_id(id_vertex_the_shire.as_str(), None)?;\n\n    graphs.add_edge(\u0026Edge::create(\u0026sam, \"lives at\", \u0026the_shire), None); \n\n    // Now we can do things like get stats of the Graphs\n    let stats = graphs.get_stats();\n\n    // and print it\n    println!(\"{:#?}\", stats);\n    // GraphsStats {\n    //    mem: 1578,\n    //    total_edges: 6,\n    //    total_graphs: 1,\n    //    total_attr: 8,\n    //    total_vertices: 12,\n    //    uniq_rel: 3,\n    //    max_mem: 104857600,\n    // }\n    \n    // or get some value from stats\n    // like the amount of vertices\n    assert_eq!(stats.get_total_vertices(), 12);\n\n    // We can print the current Graphs object\n    println!(\"{:#?}\", graphs);\n\n    // We can retrieve the uniq relations from the graph\n    let unique_relations_vertices = graphs.uniq_relations();\n    assert_eq!(unique_relations_vertices, vec![\"friend of\", \"has best friend\", \"lives at\"]);\n\n    // Also possible to retrieve the vertices that has a certain\n    // relation in\n    let vertices_with_relation_in = graphs.find_vertices_with_relation_in(\"lives at\", None)?; \n    assert_eq!(vertices_with_relation_in[0].get_label(), \"The Shire\");\n    assert_eq!(vertices_with_relation_in[1].get_label(), \"Isengard\");\n\n    // Or get the edge that has a vertex with an attribute equals to\n    let found = graphs.find_edges_with_vertex_attr_str_equals_to(\"years old\", 24000, None)?;\n    assert_eq!(found[0].get_from_vertex().get_label(), \"Gandalf\");\n\n    // Since we have a humble middle-earth network\n    // we can persists it for another day\n    // a file called \"middle-earth.grphst\" will be created, \n    // later we can load it with:\n    // let loaded_graphs = Graphs::load(\"middle-earth.grphst\")?;\n    graphs.save(None)?;\n\n    Ok(())\n}\n```\n\n## Install\u003ca name=\"install\"\u003e\n\nRun the following Cargo command in your project directory:\n\n`$ cargo add gruphst`\n\nOr add the following line to your Cargo.toml:\n\n`gruphst = \"0.15.0\"`\n\n## Tests \u0026 Coverage \u0026 Benchmarking\u003ca name=\"tests-coverage-benchmarking\"\u003e\n\n**To run tests locally**\nThis will show output, if a test name is provided as argument will run this tests \n\n`$ ./scripts/local-test.sh`\n\nIf nodemon is installed, you can use the tests in watch mode:\n\n`$ ./scripts/dev-watch.sh`\n\n**Coverage**\n\n`$ ./scripts/test-coverage.sh`\n\nIt will generate a report called *tarpauling-report.html*\n\n**Benchmarking**\n\n`$ ./scripts/benchmarking.sh`\n\nRight now only covers *add_edge* method.\n\n## Configuration\u003ca name=\"configuration\"\u003e\n\nGruPHst uses [dotenv](https://docs.rs/dotenv/latest/dotenv/index.html) to deal with configurations.\nYou can place a *.env* file in order to handle your configuration values or you can use *environment variables* instead to run your binary. The *environmental variables* will override the configuration from *.env* file.\n\n*e.g. override log level in your binary:*\n\n`$ GRUPHST_LOG_LEVEL=trace cargo run`\n\nThis is the currnet *.env* file:\n\n```toml\n# limit for memory usage in MB\nGRUPHST_MAX_MEM_USAGE=100 \n\n# log level, case insensitive, possible values:\n# trace\n# debug\n# info\n# warn \n# warning\n# err\n# error\nGRUPHST_LOG_LEVEL=info\n\n# delimiter character for CSV import-export \nGRUPHST_CSV_DELIMITER=;\n```\n\n### Configurable variables\u003ca name=\"configurable-variables\"\u003e\n\n#### Maximum memory usage\u003ca name=\"maximum-memory-usage\"\u003e\n\nConfigures the maximum memory in **MB** that GruPHst will use. In case that this limit will reach, before **panic** will persists the current status.\n\n`GRUPHST_MAX_MEM_USAGE=100`\n\n#### Level for logging\u003ca name=\"level-for-logging\"\u003e\n\nSets the level for logging in case insensitive, the possible values are:\n\n- trace\n- debug\n- info\n- warn \n- warning\n- err\n- error\n\n`GRUPHST_LOG_LEVEL=info`\n\nIn order to use it on your binary:\n\n```rust\n// import from config and logger level\nuse gruphst::config::get_log_level;\nuse gruphst::logger::enable_logging;\n\n// get the configured log level; on .env file or environmental\nlet log_level = get_log_level();\n\n// enable logging\nenable_logging(log_level);\n```\n\n#### Character delimiter for CSV file\u003ca name=\"character-delimiter-for-csv-file\"\u003e\n\nConfigures the character used to import and export for CSV format.\n\n`GRUPHST_CSV_DELIMITER=;`\n\n## Save \u0026 Load\u003ca name=\"save-load\"\u003e\n\nYou can persists the data on a file in GruPHst format.\nAnd later load the saved data.\n\n```rust\nuse gruphst::graphs::Graphs;\nuse gruphst::edge::Edge;\nuse gruphst::vertex::Vertex;\n\nlet mut graphs = Graphs::init(\"to_export\");\nlet foo = Vertex::new(\"foo\");\nlet bar = Vertex::new(\"bar\");\ngraphs.add_edge(\u0026Edge::create(\u0026foo, \"is related to\", \u0026bar), None);\n\n// persists the graphs data on file, \n// with \"./to_export.grphst\"\ngraphs.save(Some(\"./\"));\n\n// load the saved data\nlet saved_graphs = Graphs::load(\"./to_export.grphst\").unwrap();\n```\n\n## Export \u0026 Import\u003ca name=\"export-import\"\u003e\n\n### CSV\u003ca name=\"csv\"\u003e\n\nThe **delimiter** could be configured with **GRUPHST_CSV_DELIMITER** variable, via *.env* file or with *environmental var* usage. The default character is '**;**'.\n\n#### File Format\u003ca name=\"file-format\"\u003e\n\n**Headers:**\n\n```csv\ngraphs_vault;from_label;from_attributes;relation;to_label;to_attributes\n```\n\n**Row example:**\n```csv\nshire-friendships;gandalf;known as: Gandalf the Gray | name: Gandalf;friend of;frodo;name: Frodo Bolson\n```\n\n**Note:**\nThe different attributes are separated by '|' character and key followed by ':' and vaule.\n\n#### Export \u0026 Import Usage\u003ca name=\"export-import-usage\"\u003e\n\n```rust\nuse gruphst::graphs::Graphs;\nuse gruphst::edge::Edge;\nuse gruphst::vertex::Vertex;\nuse gruphst::exporter_importer::csv::*;\n\nlet mut graphs = Graphs::init(\"to_export\");\nlet foo = Vertex::new(\"foo\");\nlet bar = Vertex::new(\"bar\");\ngraphs.add_edge(\u0026Edge::create(\u0026foo, \"is related to\", \u0026bar), None);\n\n// export graphs to CSV file\nexport_to_csv_gruphst_format(\u0026graphs, Some(\"./\"), Some(\"export_csv_filename\")).unwrap();\n\n// import graphs from CSV file\nlet graphs: Graphs = import_from_csv_gruphst_format(\"./export_csv_filename.csv\").unwrap();\n```\n\n## Cryptography\u003ca name=\"cryptography\"\u003e\n\n### Argon2 Hashes\u003ca name=\"argon2-hashes\"\u003e\n\nYou can use [Argon2](https://docs.rs/argon2/latest/argon2/) to store passwords or whatever sensible data you are dealing with, and verify it.\n\n```rust\nuse gruphst::vertex::Vertex;\n\n// create a vertex\nlet mut vertex = Vertex::new(\"Brian\");\n// set an Argon2 hash\nvertex.set_hash(\"password\", \"53cr37\");\n\n// Check if the provided value is valid\nassert!(vertex.is_hash_valid(\"password\", \"53cr37\").unwrap());\nassert!(!vertex.is_hash_valid(\"password\", \"f00b4r\").unwrap());\n```\n\n## Examples\u003ca name=\"examples\"\u003e\n\nCheck the [Rock Paper Scissors Spock Lizard](https://github.com/carvilsi/gruphst/tree/main/examples/rock-paper-scissors-lizard-spock) example.\n\nCheck the [Middle-Earth](https://github.com/carvilsi/gruphst/tree/main/examples/middle-earth) example.\n\nAlso worth to check the [tests](https://github.com/carvilsi/gruphst/tree/main/tests) folder.\n\n---\n\nThanks [@ChrisMcMStone](https://github.com/ChrisMcMStone) for all the help and memory tips ;-)\n\nFeedback from usage and contributions are very welcome.\nAlso if you like it, please leave a :star: I would appreciate it ;)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcarvilsi%2Fgruphst","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcarvilsi%2Fgruphst","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcarvilsi%2Fgruphst/lists"}