mod helpers;
use arrow_array::{Array, StringArray};
use omnigraph::db::Omnigraph;
use omnigraph::loader::{LoadMode, load_jsonl};
use omnigraph_compiler::ir::ParamMap;
use omnigraph_compiler::result::QueryResult;
use helpers::*;
fn names_in_order(result: &QueryResult) -> Vec<String> {
let batch = result.concat_batches().unwrap();
if batch.num_rows() == 0 {
return Vec::new();
}
let col = batch
.column(0)
.as_any()
.downcast_ref::<StringArray>()
.unwrap();
(0..col.len()).map(|i| col.value(i).to_string()).collect()
}
async fn init_people(dir: &tempfile::TempDir, jsonl: &str) -> Omnigraph {
let uri = dir.path().to_str().unwrap();
let mut db = Omnigraph::init(uri, TEST_SCHEMA).await.unwrap();
load_jsonl(&mut db, jsonl, LoadMode::Overwrite).await.unwrap();
db
}
#[tokio::test]
async fn ordering_descending() {
let dir = tempfile::tempdir().unwrap();
let mut db = init_and_load(&dir).await;
let q = r#"
query q() {
match { $p: Person }
return { $p.name }
order { $p.age desc }
}
"#;
let got = names_in_order(&query_main(&mut db, q, "q", &ParamMap::new()).await.unwrap());
assert_eq!(got, vec!["Charlie", "Alice", "Diana", "Bob"]);
}
#[tokio::test]
async fn ordering_multi_key_age_desc_name_asc() {
let dir = tempfile::tempdir().unwrap();
let data = r#"{"type":"Person","data":{"name":"Bob","age":30}}
{"type":"Person","data":{"name":"Alice","age":30}}
{"type":"Person","data":{"name":"Charlie","age":25}}"#;
let mut db = init_people(&dir, data).await;
let q = r#"
query q() {
match { $p: Person }
return { $p.name }
order { $p.age desc, $p.name asc }
}
"#;
let got = names_in_order(&query_main(&mut db, q, "q", &ParamMap::new()).await.unwrap());
assert_eq!(got, vec!["Alice", "Bob", "Charlie"]);
}
#[tokio::test]
async fn ordering_tiebreak_by_key_is_deterministic() {
let dir = tempfile::tempdir().unwrap();
let data = r#"{"type":"Person","data":{"name":"Bob","age":30}}
{"type":"Person","data":{"name":"Alice","age":30}}
{"type":"Person","data":{"name":"Charlie","age":25}}"#;
let mut db = init_people(&dir, data).await;
let q = r#"
query q() {
match { $p: Person }
return { $p.name }
order { $p.age asc }
}
"#;
let got = names_in_order(&query_main(&mut db, q, "q", &ParamMap::new()).await.unwrap());
assert_eq!(got, vec!["Charlie", "Alice", "Bob"]);
}
#[tokio::test]
async fn ordering_nulls_placement_asc_and_desc() {
let dir = tempfile::tempdir().unwrap();
let data = r#"{"type":"Person","data":{"name":"Alice","age":30}}
{"type":"Person","data":{"name":"Bob","age":null}}
{"type":"Person","data":{"name":"Charlie","age":25}}"#;
let mut db = init_people(&dir, data).await;
let asc = r#"
query q() {
match { $p: Person }
return { $p.name }
order { $p.age asc }
}
"#;
let got_asc = names_in_order(&query_main(&mut db, asc, "q", &ParamMap::new()).await.unwrap());
assert_eq!(got_asc, vec!["Bob", "Charlie", "Alice"]);
let desc = r#"
query q() {
match { $p: Person }
return { $p.name }
order { $p.age desc }
}
"#;
let got_desc = names_in_order(&query_main(&mut db, desc, "q", &ParamMap::new()).await.unwrap());
assert_eq!(got_desc, vec!["Alice", "Charlie", "Bob"]);
}