#[cfg(feature = "napi")]
use napi_derive::napi;
use serde::{Deserialize, Serialize};
use std::collections::{BTreeSet, HashMap, HashSet};
use std::fmt;
pub type NodeId = u64;
pub type LabelId = u32;
pub type ETypeId = u32;
pub type PropKeyId = u32;
pub type TxId = u64;
pub type Timestamp = u64;
pub type PhysNode = u32;
pub type StringId = u32;
bitflags::bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SnapshotFlags: u32 {
const HAS_IN_EDGES = 1 << 0;
const HAS_PROPERTIES = 1 << 1;
const HAS_KEY_BUCKETS = 1 << 2;
const HAS_EDGE_BLOOM = 1 << 3; const HAS_NODE_LABELS = 1 << 4;
const HAS_VECTORS = 1 << 5;
const HAS_VECTOR_STORES = 1 << 6;
}
}
#[derive(Debug, Clone)]
pub struct SnapshotHeaderV1 {
pub magic: u32, pub version: u32, pub min_reader_version: u32, pub flags: SnapshotFlags,
pub generation: u64,
pub created_unix_ns: u64,
pub num_nodes: u64,
pub num_edges: u64,
pub max_node_id: u64,
pub num_labels: u64,
pub num_etypes: u64,
pub num_propkeys: u64,
pub num_strings: u64,
}
#[derive(Debug, Clone, Copy, Default)]
pub struct SectionEntry {
pub offset: u64, pub length: u64, pub compression: u32, pub uncompressed_size: u32, }
#[repr(u32)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SectionId {
PhysToNodeId = 0,
NodeIdToPhys = 1,
OutOffsets = 2,
OutDst = 3,
OutEtype = 4,
InOffsets = 5,
InSrc = 6,
InEtype = 7,
InOutIndex = 8,
StringOffsets = 9,
StringBytes = 10,
LabelStringIds = 11,
EtypeStringIds = 12,
PropkeyStringIds = 13,
NodeKeyString = 14,
KeyEntries = 15,
KeyBuckets = 16,
NodePropOffsets = 17,
NodePropKeys = 18,
NodePropVals = 19,
EdgePropOffsets = 20,
EdgePropKeys = 21,
EdgePropVals = 22,
NodeLabelOffsets = 23,
NodeLabelIds = 24,
VectorOffsets = 25,
VectorData = 26,
VectorStoreIndex = 27,
VectorStoreData = 28,
}
impl SectionId {
pub const COUNT_V1: usize = 23;
pub const COUNT_V2: usize = 25;
pub const COUNT_V3: usize = 27;
pub const COUNT: usize = 29;
pub fn from_u32(v: u32) -> Option<Self> {
match v {
0 => Some(Self::PhysToNodeId),
1 => Some(Self::NodeIdToPhys),
2 => Some(Self::OutOffsets),
3 => Some(Self::OutDst),
4 => Some(Self::OutEtype),
5 => Some(Self::InOffsets),
6 => Some(Self::InSrc),
7 => Some(Self::InEtype),
8 => Some(Self::InOutIndex),
9 => Some(Self::StringOffsets),
10 => Some(Self::StringBytes),
11 => Some(Self::LabelStringIds),
12 => Some(Self::EtypeStringIds),
13 => Some(Self::PropkeyStringIds),
14 => Some(Self::NodeKeyString),
15 => Some(Self::KeyEntries),
16 => Some(Self::KeyBuckets),
17 => Some(Self::NodePropOffsets),
18 => Some(Self::NodePropKeys),
19 => Some(Self::NodePropVals),
20 => Some(Self::EdgePropOffsets),
21 => Some(Self::EdgePropKeys),
22 => Some(Self::EdgePropVals),
23 => Some(Self::NodeLabelOffsets),
24 => Some(Self::NodeLabelIds),
25 => Some(Self::VectorOffsets),
26 => Some(Self::VectorData),
27 => Some(Self::VectorStoreIndex),
28 => Some(Self::VectorStoreData),
_ => None,
}
}
}
pub const SECTION_ENTRY_SIZE: usize = 8 + 8 + 4 + 4;
pub const SNAPSHOT_HEADER_SIZE: usize = 4 + 4 + 4 + 4 + 8 + 8 + 8 * 7; pub const SNAPSHOT_SECTION_TABLE_OFFSET: usize = SNAPSHOT_HEADER_SIZE;
pub const SNAPSHOT_SECTION_TABLE_SIZE: usize = SectionId::COUNT * SECTION_ENTRY_SIZE;
pub const SNAPSHOT_DATA_START: usize = SNAPSHOT_HEADER_SIZE + SNAPSHOT_SECTION_TABLE_SIZE;
#[derive(Debug, Clone, Copy)]
pub struct KeyIndexEntry {
pub hash64: u64, pub string_id: u32, pub reserved: u32,
pub node_id: u64,
}
pub const KEY_INDEX_ENTRY_SIZE: usize = 8 + 4 + 4 + 8;
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WalRecordType {
Begin = 1,
Commit = 2,
Rollback = 3,
CreateNode = 10,
DeleteNode = 11,
CreateNodesBatch = 12,
AddEdge = 20,
DeleteEdge = 21,
AddEdgeProps = 22,
AddEdgesBatch = 23,
AddEdgesPropsBatch = 24,
DefineLabel = 30,
AddNodeLabel = 31,
RemoveNodeLabel = 32,
DefineEtype = 40,
DefinePropkey = 50,
SetNodeProp = 51,
DelNodeProp = 52,
SetEdgeProp = 53,
DelEdgeProp = 54,
SetEdgeProps = 55,
SetNodeVector = 60,
DelNodeVector = 61,
BatchVectors = 62,
SealFragment = 63,
CompactFragments = 64,
}
impl WalRecordType {
pub fn from_u8(v: u8) -> Option<Self> {
match v {
1 => Some(Self::Begin),
2 => Some(Self::Commit),
3 => Some(Self::Rollback),
10 => Some(Self::CreateNode),
11 => Some(Self::DeleteNode),
12 => Some(Self::CreateNodesBatch),
20 => Some(Self::AddEdge),
21 => Some(Self::DeleteEdge),
22 => Some(Self::AddEdgeProps),
23 => Some(Self::AddEdgesBatch),
24 => Some(Self::AddEdgesPropsBatch),
30 => Some(Self::DefineLabel),
31 => Some(Self::AddNodeLabel),
32 => Some(Self::RemoveNodeLabel),
40 => Some(Self::DefineEtype),
50 => Some(Self::DefinePropkey),
51 => Some(Self::SetNodeProp),
52 => Some(Self::DelNodeProp),
53 => Some(Self::SetEdgeProp),
54 => Some(Self::DelEdgeProp),
55 => Some(Self::SetEdgeProps),
60 => Some(Self::SetNodeVector),
61 => Some(Self::DelNodeVector),
62 => Some(Self::BatchVectors),
63 => Some(Self::SealFragment),
64 => Some(Self::CompactFragments),
_ => None,
}
}
}
#[repr(C, packed)]
#[derive(Debug, Clone, Copy)]
pub struct WalRecordHeader {
pub rec_len: u32, pub record_type: u8, pub flags: u8,
pub reserved: u16,
pub txid: u64,
pub payload_len: u32,
}
pub const WAL_RECORD_HEADER_SIZE: usize = 4 + 1 + 1 + 2 + 8 + 4;
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "napi", napi)]
pub enum PropValueTag {
Null = 0,
Bool = 1,
I64 = 2,
F64 = 3,
String = 4,
VectorF32 = 5, }
impl PropValueTag {
pub fn from_u8(v: u8) -> Option<Self> {
match v {
0 => Some(Self::Null),
1 => Some(Self::Bool),
2 => Some(Self::I64),
3 => Some(Self::F64),
4 => Some(Self::String),
5 => Some(Self::VectorF32),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum PropValue {
Null,
Bool(bool),
I64(i64),
F64(f64),
String(String),
VectorF32(Vec<f32>),
}
pub type PropValueRef = std::sync::Arc<PropValue>;
pub type VectorRef = std::sync::Arc<[f32]>;
pub type EdgeProp = (PropKeyId, PropValue);
pub type EdgeWithProps = (NodeId, ETypeId, NodeId, Vec<EdgeProp>);
impl PropValue {
pub fn tag(&self) -> PropValueTag {
match self {
PropValue::Null => PropValueTag::Null,
PropValue::Bool(_) => PropValueTag::Bool,
PropValue::I64(_) => PropValueTag::I64,
PropValue::F64(_) => PropValueTag::F64,
PropValue::String(_) => PropValueTag::String,
PropValue::VectorF32(_) => PropValueTag::VectorF32,
}
}
}
pub const PROP_VALUE_DISK_SIZE: usize = 16;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Edge {
pub src: NodeId,
pub etype: ETypeId,
pub dst: NodeId,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct EdgePatch {
pub etype: ETypeId,
pub other: NodeId, }
#[derive(Debug, Default, Clone)]
pub struct NodeDelta {
pub key: Option<String>,
pub labels: Option<HashSet<LabelId>>,
pub labels_deleted: Option<HashSet<LabelId>>,
pub props: Option<HashMap<PropKeyId, Option<PropValueRef>>>, }
impl NodeDelta {
pub fn for_version(&self) -> NodeDelta {
NodeDelta {
key: self.key.clone(),
labels: None,
labels_deleted: None,
props: None,
}
}
}
#[derive(Debug, Default, Clone)]
pub struct DeltaState {
pub created_nodes: HashMap<NodeId, NodeDelta>,
pub deleted_nodes: HashSet<NodeId>,
pub modified_nodes: HashMap<NodeId, NodeDelta>,
pub out_add: HashMap<NodeId, BTreeSet<EdgePatch>>,
pub out_del: HashMap<NodeId, BTreeSet<EdgePatch>>,
pub in_add: HashMap<NodeId, BTreeSet<EdgePatch>>,
pub in_del: HashMap<NodeId, BTreeSet<EdgePatch>>,
pub edge_props: HashMap<(NodeId, ETypeId, NodeId), HashMap<PropKeyId, Option<PropValueRef>>>,
pub new_labels: HashMap<LabelId, String>,
pub new_etypes: HashMap<ETypeId, String>,
pub new_propkeys: HashMap<PropKeyId, String>,
pub key_index: HashMap<String, NodeId>,
pub key_index_deleted: HashSet<String>,
pub incoming_edge_sources: HashMap<NodeId, HashSet<NodeId>>,
pub pending_vectors: HashMap<(NodeId, PropKeyId), Option<VectorRef>>,
}
#[derive(Debug, Clone, Default)]
pub struct OpenOptions {
pub read_only: bool,
pub create_if_missing: bool,
pub lock_file: bool,
pub mvcc: bool,
pub mvcc_gc_interval_ms: Option<u64>, pub mvcc_retention_ms: Option<u64>, pub mvcc_max_chain_depth: Option<usize>,
pub auto_checkpoint: bool, pub checkpoint_threshold: f64, pub cache_snapshot: bool,
pub page_size: Option<usize>, pub wal_size: Option<usize>, }
#[derive(Debug, Clone, Default)]
pub struct CacheOptions {
pub enabled: bool,
pub property_cache: Option<PropertyCacheConfig>,
pub traversal_cache: Option<TraversalCacheConfig>,
pub query_cache: Option<QueryCacheConfig>,
}
#[derive(Debug, Clone)]
pub struct PropertyCacheConfig {
pub max_node_props: usize, pub max_edge_props: usize, }
impl Default for PropertyCacheConfig {
fn default() -> Self {
Self {
max_node_props: 10000,
max_edge_props: 10000,
}
}
}
#[derive(Debug, Clone)]
pub struct TraversalCacheConfig {
pub max_entries: usize, pub max_neighbors_per_entry: usize, }
impl Default for TraversalCacheConfig {
fn default() -> Self {
Self {
max_entries: 5000,
max_neighbors_per_entry: 100,
}
}
}
#[derive(Debug, Clone)]
pub struct QueryCacheConfig {
pub max_entries: usize, pub ttl_ms: Option<u64>,
}
impl Default for QueryCacheConfig {
fn default() -> Self {
Self {
max_entries: 1000,
ttl_ms: None,
}
}
}
#[derive(Debug, Clone, Default)]
pub struct CacheStats {
pub property_cache_hits: u64,
pub property_cache_misses: u64,
pub property_cache_size: usize,
pub traversal_cache_hits: u64,
pub traversal_cache_misses: u64,
pub traversal_cache_size: usize,
pub query_cache_hits: u64,
pub query_cache_misses: u64,
pub query_cache_size: usize,
}
#[derive(Debug, Clone)]
pub struct DbStats {
pub snapshot_gen: u64,
pub snapshot_nodes: u64,
pub snapshot_edges: u64,
pub snapshot_max_node_id: u64,
pub delta_nodes_created: usize,
pub delta_nodes_deleted: usize,
pub delta_edges_added: usize,
pub delta_edges_deleted: usize,
pub wal_segment: u64,
pub wal_bytes: u64,
pub recommend_compact: bool,
pub mvcc_stats: Option<MvccStats>,
}
#[derive(Debug, Clone)]
pub struct MvccStats {
pub active_transactions: usize,
pub min_active_ts: u64,
pub versions_pruned: u64,
pub gc_runs: u64,
pub last_gc_time: u64,
pub committed_writes_size: usize,
pub committed_writes_pruned: usize,
}
#[derive(Debug, Clone)]
pub struct CheckResult {
pub valid: bool,
pub errors: Vec<String>,
pub warnings: Vec<String>,
}
#[derive(Debug, Default)]
pub struct TxState {
pub txid: TxId,
pub read_only: bool,
pub snapshot_ts: u64,
pub pending_created_nodes: HashMap<NodeId, NodeDelta>,
pub pending_deleted_nodes: HashSet<NodeId>,
pub pending_out_add: HashMap<NodeId, BTreeSet<EdgePatch>>,
pub pending_out_del: HashMap<NodeId, BTreeSet<EdgePatch>>,
pub pending_in_add: HashMap<NodeId, BTreeSet<EdgePatch>>,
pub pending_in_del: HashMap<NodeId, BTreeSet<EdgePatch>>,
pub pending_node_props: HashMap<NodeId, HashMap<PropKeyId, Option<PropValueRef>>>,
pub pending_node_labels_add: HashMap<NodeId, HashSet<LabelId>>,
pub pending_node_labels_del: HashMap<NodeId, HashSet<LabelId>>,
pub pending_edge_props:
HashMap<(NodeId, ETypeId, NodeId), HashMap<PropKeyId, Option<PropValueRef>>>,
pub pending_new_labels: HashMap<LabelId, String>,
pub pending_new_etypes: HashMap<ETypeId, String>,
pub pending_new_propkeys: HashMap<PropKeyId, String>,
pub pending_key_updates: HashMap<String, NodeId>,
pub pending_key_deletes: HashSet<String>,
pub pending_vector_sets: HashMap<(NodeId, PropKeyId), Vec<f32>>,
pub pending_vector_deletes: HashSet<(NodeId, PropKeyId)>,
}
impl TxState {
pub fn new(txid: TxId, read_only: bool, snapshot_ts: u64) -> Self {
Self {
txid,
read_only,
snapshot_ts,
..Default::default()
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum TxKey {
Node(NodeId),
Edge {
src: NodeId,
etype: ETypeId,
dst: NodeId,
},
NodeProp {
node_id: NodeId,
key_id: PropKeyId,
},
EdgeProp {
src: NodeId,
etype: ETypeId,
dst: NodeId,
key_id: PropKeyId,
},
Key(std::sync::Arc<str>),
NeighborsOut {
node_id: NodeId,
etype: Option<ETypeId>,
},
NeighborsIn {
node_id: NodeId,
etype: Option<ETypeId>,
},
NodeLabels(NodeId),
NodeLabel {
node_id: NodeId,
label_id: LabelId,
},
}
impl fmt::Display for TxKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TxKey::Node(node_id) => write!(f, "node:{node_id}"),
TxKey::Edge { src, etype, dst } => write!(f, "edge:{src}:{etype}:{dst}"),
TxKey::NodeProp { node_id, key_id } => write!(f, "nodeprop:{node_id}:{key_id}"),
TxKey::EdgeProp {
src,
etype,
dst,
key_id,
} => write!(f, "edgeprop:{src}:{etype}:{dst}:{key_id}"),
TxKey::Key(key) => write!(f, "key:{key}"),
TxKey::NeighborsOut { node_id, etype } => match etype {
Some(etype) => write!(f, "neighbors_out:{node_id}:{etype}"),
None => write!(f, "neighbors_out:{node_id}:*"),
},
TxKey::NeighborsIn { node_id, etype } => match etype {
Some(etype) => write!(f, "neighbors_in:{node_id}:{etype}"),
None => write!(f, "neighbors_in:{node_id}:*"),
},
TxKey::NodeLabels(node_id) => write!(f, "nodelabels:{node_id}"),
TxKey::NodeLabel { node_id, label_id } => write!(f, "nodelabel:{node_id}:{label_id}"),
}
}
}
#[derive(Debug, Clone)]
pub struct MvccTransaction {
pub txid: TxId,
pub start_ts: Timestamp,
pub commit_ts: Option<Timestamp>,
pub status: MvccTxStatus,
pub read_set: HashSet<TxKey>,
pub write_set: HashSet<TxKey>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MvccTxStatus {
Active,
Committed,
Aborted,
}
#[derive(Debug)]
pub struct VersionedRecord<T> {
pub data: T,
pub txid: TxId,
pub commit_ts: Timestamp,
pub prev: Option<Box<VersionedRecord<T>>>,
pub deleted: bool,
}
#[derive(Debug, Clone)]
pub struct NodeVersionData {
pub node_id: NodeId,
pub delta: NodeDelta,
}
#[derive(Debug, Clone)]
pub struct EdgeVersionData {
pub src: NodeId,
pub etype: ETypeId,
pub dst: NodeId,
pub added: bool, }
#[derive(Debug, Clone)]
pub struct DbHeaderV1 {
pub magic: [u8; 16], pub page_size: u32,
pub version: u32,
pub min_reader_version: u32,
pub flags: u32,
pub change_counter: u64,
pub db_size_pages: u64,
pub snapshot_start_page: u64,
pub snapshot_page_count: u64,
pub wal_start_page: u64,
pub wal_page_count: u64,
pub wal_head: u64,
pub wal_tail: u64,
pub active_snapshot_gen: u64,
pub prev_snapshot_gen: u64,
pub max_node_id: u64,
pub next_tx_id: u64,
pub last_commit_ts: u64,
pub schema_cookie: u64,
pub wal_primary_head: u64,
pub wal_secondary_head: u64,
pub active_wal_region: u8, pub checkpoint_in_progress: u8, }
pub const DB_HEADER_FIXED_SIZE: usize = 176;
pub const DB_HEADER_RESERVED_SIZE: usize = 14;
pub const DB_HEADER_V2_FIELDS_SIZE: usize = 8 + 8 + 1 + 1;
#[derive(Debug, Default, Clone)]
pub struct NodeOpts {
pub key: Option<String>,
pub labels: Option<Vec<LabelId>>,
pub props: Option<HashMap<PropKeyId, PropValue>>,
}