diff --git a/src/lua/db_api.rs b/src/lua/db_api.rs index 3afea15..e97fa7f 100644 --- a/src/lua/db_api.rs +++ b/src/lua/db_api.rs @@ -126,7 +126,7 @@ fn build_filter_sql(node: &FilterNode, binds: &mut Vec) -> String { match node { FilterNode::Condition { field, op, value } => { binds.push(value.clone()); - format!("json_extract(record, '$.value.{field}') {op} ?") + format!("json_extract(record, '$.{field}') {op} ?") } FilterNode::Group { combine, children } => { let parts: Vec = children @@ -205,7 +205,7 @@ pub fn register_db_api(lua: &Lua, state: Arc) -> LuaResult<()> { let order_expr = if top_level_columns.contains(&sort_field.as_str()) { format!("{sort_field} {direction}") } else { - format!("json_extract(record, '$.value.{sort_field}') {direction}") + format!("json_extract(record, '$.{sort_field}') {direction}") }; let did_clause = if did.is_some() { " AND did = ?" } else { "" }; @@ -965,7 +965,7 @@ mod tests { let node = parse_filter_node(&t, 0).unwrap(); let mut binds = Vec::new(); let sql = build_filter_sql(&node, &mut binds); - assert_eq!(sql, "json_extract(record, '$.value.name') = ?"); + assert_eq!(sql, "json_extract(record, '$.name') = ?"); assert_eq!(binds, vec!["alice"]); } @@ -978,7 +978,7 @@ mod tests { let node = parse_filter_node(&t, 0).unwrap(); let mut binds = Vec::new(); let sql = build_filter_sql(&node, &mut binds); - assert_eq!(sql, "json_extract(record, '$.value.status') = ?"); + assert_eq!(sql, "json_extract(record, '$.status') = ?"); } #[test] @@ -1011,7 +1011,7 @@ mod tests { let sql = build_filter_sql(&node, &mut binds); assert_eq!( sql, - "(json_extract(record, '$.value.status') = ? AND json_extract(record, '$.value.age') > ?)" + "(json_extract(record, '$.status') = ? AND json_extract(record, '$.age') > ?)" ); assert_eq!(binds, vec!["active", "18"]); } @@ -1030,7 +1030,7 @@ mod tests { let sql = build_filter_sql(&node, &mut binds); assert_eq!( sql, - "(json_extract(record, '$.value.role') = ? OR json_extract(record, '$.value.role') = ?)" + "(json_extract(record, '$.role') = ? OR json_extract(record, '$.role') = ?)" ); assert_eq!(binds, vec!["admin", "mod"]); } @@ -1045,7 +1045,7 @@ mod tests { let node = parse_filter_node(&group, 0).unwrap(); let mut binds = Vec::new(); let sql = build_filter_sql(&node, &mut binds); - assert_eq!(sql, "json_extract(record, '$.value.x') = ?"); + assert_eq!(sql, "json_extract(record, '$.x') = ?"); } #[test] @@ -1095,7 +1095,7 @@ mod tests { let node = parse_filter_node(&t, 0).unwrap(); let mut binds = Vec::new(); let sql = build_filter_sql(&node, &mut binds); - assert_eq!(sql, "json_extract(record, '$.value.name') LIKE ?"); + assert_eq!(sql, "json_extract(record, '$.name') LIKE ?"); } #[test] @@ -1130,10 +1130,7 @@ mod tests { let node = parse_filter_node(&t, 0).unwrap(); let mut binds = Vec::new(); let sql = build_filter_sql(&node, &mut binds); - assert_eq!( - sql, - "json_extract(record, '$.value.author.websites[0].url') = ?" - ); + assert_eq!(sql, "json_extract(record, '$.author.websites[0].url') = ?"); } // ----------------------------------------------------------------------- diff --git a/tests/lua_db_api.rs b/tests/lua_db_api.rs index d32bb90..3fa11aa 100644 --- a/tests/lua_db_api.rs +++ b/tests/lua_db_api.rs @@ -345,3 +345,146 @@ async fn db_raw_select_works() { let cnt: i64 = first_row.get("cnt").unwrap(); assert_eq!(cnt, 3); } + +#[tokio::test] +#[serial] +async fn db_query_filter_equals() { + common::require_db!(); + let pool = db::test_pool().await; + let backend = db::test_backend(); + db::truncate_all(&pool).await; + seed_records(&pool, backend).await; + let state = test_state_with_pool(pool, backend).await; + let lua = setup_lua(&state); + + let result: mlua::Table = lua + .load( + r#"return db.query({ + collection = "test.collection", + filter = { field = "name", value = "Test One" } + })"#, + ) + .eval_async() + .await + .unwrap(); + + let records: mlua::Table = result.get("records").unwrap(); + assert_eq!(records.raw_len(), 1); + let first: mlua::Table = records.get(1).unwrap(); + assert_eq!(first.get::("name").unwrap(), "Test One"); +} + +#[tokio::test] +#[serial] +async fn db_query_filter_not_equals() { + common::require_db!(); + let pool = db::test_pool().await; + let backend = db::test_backend(); + db::truncate_all(&pool).await; + seed_records(&pool, backend).await; + let state = test_state_with_pool(pool, backend).await; + let lua = setup_lua(&state); + + let result: mlua::Table = lua + .load( + r#"return db.query({ + collection = "test.collection", + filter = { field = "name", op = "!=", value = "Test One" } + })"#, + ) + .eval_async() + .await + .unwrap(); + + let records: mlua::Table = result.get("records").unwrap(); + assert_eq!(records.raw_len(), 2); +} + +#[tokio::test] +#[serial] +async fn db_query_filter_no_match_returns_empty() { + common::require_db!(); + let pool = db::test_pool().await; + let backend = db::test_backend(); + db::truncate_all(&pool).await; + seed_records(&pool, backend).await; + let state = test_state_with_pool(pool, backend).await; + let lua = setup_lua(&state); + + let result: mlua::Table = lua + .load( + r#"return db.query({ + collection = "test.collection", + filter = { field = "name", value = "Nonexistent" } + })"#, + ) + .eval_async() + .await + .unwrap(); + + let records: mlua::Table = result.get("records").unwrap(); + assert_eq!(records.raw_len(), 0); +} + +#[tokio::test] +#[serial] +async fn db_query_filter_and_group() { + common::require_db!(); + let pool = db::test_pool().await; + let backend = db::test_backend(); + db::truncate_all(&pool).await; + seed_records(&pool, backend).await; + let state = test_state_with_pool(pool, backend).await; + let lua = setup_lua(&state); + + let result: mlua::Table = lua + .load( + r#"return db.query({ + collection = "test.collection", + filter = { + combine = "AND", + { field = "name", op = "LIKE", value = "Test%" }, + { field = "value", op = ">", value = "1" } + } + })"#, + ) + .eval_async() + .await + .unwrap(); + + let records: mlua::Table = result.get("records").unwrap(); + assert_eq!(records.raw_len(), 1); + let first: mlua::Table = records.get(1).unwrap(); + assert_eq!(first.get::("name").unwrap(), "Test Two"); +} + +#[tokio::test] +#[serial] +async fn db_query_sort_by_json_field() { + common::require_db!(); + let pool = db::test_pool().await; + let backend = db::test_backend(); + db::truncate_all(&pool).await; + seed_records(&pool, backend).await; + let state = test_state_with_pool(pool, backend).await; + let lua = setup_lua(&state); + + let result: mlua::Table = lua + .load( + r#"return db.query({ + collection = "test.collection", + sort = "name", + sortDirection = "asc" + })"#, + ) + .eval_async() + .await + .unwrap(); + + let records: mlua::Table = result.get("records").unwrap(); + assert_eq!(records.raw_len(), 3); + let first: mlua::Table = records.get(1).unwrap(); + let last: mlua::Table = records.get(3).unwrap(); + assert_eq!(first.get::("name").unwrap(), "Other Record"); + assert_eq!(last.get::("name").unwrap(), "Test Two"); +}