use serde_json::json;
use rust_store_core::dialect::{restore_rows_json, translate, Backend};
use rust_store_core::schema::Registry;
fn reg() -> Registry {
let mut r = Registry::new();
r.register(&json!({
"name": "Course",
"collection": "courses",
"timestamps": false,
"fields": {
"title": { "type": "string" },
"meta": { "type": "object", "fields": {
"level": { "type": "string" },
"seo": { "type": "object", "fields": { "title": { "type": "string" } } }
} }
},
"relations": {}
}))
.unwrap();
r
}
#[test]
fn json_type_name_per_backend() {
assert_eq!(Backend::Mysql.json_type_name(), "JSON");
assert_eq!(Backend::Postgres.json_type_name(), "jsonb");
assert_eq!(Backend::Sqlite.json_type_name(), "TEXT");
}
#[test]
fn json_extract_scalar_multi_segment() {
assert_eq!(
Backend::Mysql.json_extract_scalar("`meta`", &["a", "b"]),
"JSON_UNQUOTE(JSON_EXTRACT(`meta`, '$.a.b'))"
);
assert_eq!(
Backend::Postgres.json_extract_scalar("\"meta\"", &["a", "b"]),
"(\"meta\" #>> '{a,b}')"
);
assert_eq!(
Backend::Sqlite.json_extract_scalar("\"meta\"", &["a", "b"]),
"json_extract(\"meta\", '$.a.b')"
);
}
#[test]
fn json_extract_scalar_single_segment() {
assert_eq!(
Backend::Mysql.json_extract_scalar("`meta`", &["title"]),
"JSON_UNQUOTE(JSON_EXTRACT(`meta`, '$.title'))"
);
assert_eq!(
Backend::Postgres.json_extract_scalar("\"meta\"", &["title"]),
"(\"meta\" #>> '{title}')"
);
assert_eq!(
Backend::Sqlite.json_extract_scalar("\"meta\"", &["title"]),
"json_extract(\"meta\", '$.title')"
);
}
#[test]
fn u3_object_dotted_filter_pushes_json_extract() {
let cmd = json!({
"kind": "find", "collection": "courses",
"filter": { "meta.seo.title": "x" },
"projection": { "_id": 1, "title": 1 }
});
let sqlite = translate(Backend::Sqlite, &cmd, ®()).unwrap();
let text = sqlite["stmts"][0]["text"].as_str().unwrap();
assert!(
text.contains("json_extract(t.\"meta\", '$.seo.title')"),
"SQLite U3: {text}"
);
assert!(sqlite["unsupported"].as_array().unwrap().is_empty());
let pg = translate(Backend::Postgres, &cmd, ®()).unwrap();
let text = pg["stmts"][0]["text"].as_str().unwrap();
assert!(text.contains("(t.\"meta\" #>> '{seo,title}')"), "PG U3: {text}");
let my = translate(Backend::Mysql, &cmd, ®()).unwrap();
let text = my["stmts"][0]["text"].as_str().unwrap();
assert!(
text.contains("JSON_UNQUOTE(JSON_EXTRACT(t.`meta`, '$.seo.title'))"),
"MySQL U3: {text}"
);
}
#[test]
fn u4_object_dotted_sort_pushes_json_extract() {
let cmd = json!({
"kind": "aggregate", "collection": "courses",
"pipeline": [ { "$sort": { "meta.level": 1 } }, { "$project": { "_id": 1 } } ]
});
let sqlite = translate(Backend::Sqlite, &cmd, ®()).unwrap();
let text = sqlite["stmts"][0]["text"].as_str().unwrap();
assert!(
text.contains("ORDER BY json_extract(t.\"meta\", '$.level') ASC"),
"SQLite U4: {text}"
);
assert!(sqlite["unsupported"].as_array().unwrap().is_empty());
let pg = translate(Backend::Postgres, &cmd, ®()).unwrap();
let text = pg["stmts"][0]["text"].as_str().unwrap();
assert!(text.contains("ORDER BY (t.\"meta\" #>> '{level}') ASC"), "PG U4: {text}");
}
#[test]
fn insert_object_field_serialized_as_json_text() {
let cmd = json!({
"kind": "insertOne", "collection": "courses",
"doc": { "_id": "c1", "title": "t", "meta": { "level": "a" } }
});
let out = translate(Backend::Sqlite, &cmd, ®()).unwrap();
let params = out["stmts"][0]["params"].as_array().unwrap();
assert!(
params.iter().any(|p| p.as_str() == Some("{\"level\":\"a\"}")),
"object 字段应序列化为 JSON 文本: {params:?}"
);
}
#[test]
fn json_column_restores_nested_object() {
let cmd = json!({
"kind": "find", "collection": "courses", "filter": {},
"projection": { "_id": 1, "title": 1, "meta": 1 }
});
let out = translate(Backend::Sqlite, &cmd, ®()).unwrap();
let shape = &out["stmts"][0]["rowShape"];
let rows = json!([
{ "_id": "c1", "title": "t",
"meta": "{\"level\":\"a\",\"seo\":{\"title\":\"z\"}}",
"__present": ",title,meta," }
]);
let restored = restore_rows_json(shape, &rows).unwrap();
assert_eq!(
restored,
json!([{ "_id": "c1", "title": "t",
"meta": { "level": "a", "seo": { "title": "z" } } }])
);
}