haystack_server/ops/
read.rs1use actix_web::{HttpRequest, HttpResponse, web};
4
5use haystack_core::data::{HCol, HDict, HGrid};
6use haystack_core::filter::{matches, parse_filter};
7use haystack_core::kinds::Kind;
8
9use crate::content;
10use crate::error::HaystackError;
11use crate::state::AppState;
12
13pub async fn handle(
19 req: HttpRequest,
20 body: String,
21 state: web::Data<AppState>,
22) -> Result<HttpResponse, HaystackError> {
23 let content_type = req
24 .headers()
25 .get("Content-Type")
26 .and_then(|v| v.to_str().ok())
27 .unwrap_or("");
28 let accept = req
29 .headers()
30 .get("Accept")
31 .and_then(|v| v.to_str().ok())
32 .unwrap_or("");
33
34 let request_grid = content::decode_request_grid(&body, content_type)
35 .map_err(|e| HaystackError::bad_request(format!("failed to decode request: {e}")))?;
36
37 let result_grid = if request_grid.col("id").is_some() {
38 read_by_id(&request_grid, &state)
40 } else if request_grid.col("filter").is_some() {
41 read_by_filter(&request_grid, &state)
43 } else {
44 Err(HaystackError::bad_request(
45 "request must have 'id' or 'filter' column",
46 ))
47 }?;
48
49 let (encoded, ct) = content::encode_response_grid(&result_grid, accept)
50 .map_err(|e| HaystackError::internal(format!("encoding error: {e}")))?;
51
52 Ok(HttpResponse::Ok().content_type(ct).body(encoded))
53}
54
55fn read_by_filter(request_grid: &HGrid, state: &AppState) -> Result<HGrid, HaystackError> {
60 let row = request_grid
61 .row(0)
62 .ok_or_else(|| HaystackError::bad_request("request grid has no rows"))?;
63
64 let filter = match row.get("filter") {
65 Some(Kind::Str(s)) => s.as_str(),
66 _ => return Err(HaystackError::bad_request("filter must be a Str value")),
67 };
68
69 let limit = match row.get("limit") {
70 Some(Kind::Number(n)) => n.val as usize,
71 _ => 0, };
73
74 let effective_limit = if limit == 0 { usize::MAX } else { limit };
75
76 let local_grid = state
78 .graph
79 .read_filter(filter, limit)
80 .map_err(|e| HaystackError::bad_request(format!("filter error: {e}")))?;
81
82 let mut results: Vec<HDict> = local_grid.rows;
83
84 if results.len() < effective_limit {
86 let federated = state.federation.all_cached_entities();
87 if !federated.is_empty() {
88 let ast = parse_filter(filter)
89 .map_err(|e| HaystackError::bad_request(format!("filter error: {e}")))?;
90
91 for entity in federated {
92 if results.len() >= effective_limit {
93 break;
94 }
95 if matches(&ast, &entity, None) {
96 results.push(entity);
97 }
98 }
99 }
100 }
101
102 if results.is_empty() {
103 return Ok(HGrid::new());
104 }
105
106 let mut col_set: Vec<String> = Vec::new();
108 let mut seen = std::collections::HashSet::new();
109 for entity in &results {
110 for name in entity.tag_names() {
111 if seen.insert(name.to_string()) {
112 col_set.push(name.to_string());
113 }
114 }
115 }
116 col_set.sort();
117 let cols: Vec<HCol> = col_set.iter().map(|n| HCol::new(n.as_str())).collect();
118
119 Ok(HGrid::from_parts(HDict::new(), cols, results))
120}
121
122fn read_by_id(request_grid: &HGrid, state: &AppState) -> Result<HGrid, HaystackError> {
124 let mut results: Vec<HDict> = Vec::new();
125 let mut col_set: Vec<String> = Vec::new();
126 let mut seen = std::collections::HashSet::new();
127
128 for row in request_grid.rows.iter() {
129 let ref_val = match row.get("id") {
130 Some(Kind::Ref(r)) => &r.val,
131 _ => continue,
132 };
133
134 if let Some(entity) = state.graph.get(ref_val) {
135 for name in entity.tag_names() {
136 if seen.insert(name.to_string()) {
137 col_set.push(name.to_string());
138 }
139 }
140 results.push(entity);
141 } else {
142 let mut missing = HDict::new();
145 missing.set(
146 "id",
147 Kind::Ref(haystack_core::kinds::HRef::from_val(ref_val.as_str())),
148 );
149 if seen.insert("id".to_string()) {
150 col_set.push("id".to_string());
151 }
152 results.push(missing);
153 }
154 }
155
156 if results.is_empty() {
157 return Ok(HGrid::new());
158 }
159
160 col_set.sort();
161 let cols: Vec<HCol> = col_set.iter().map(|n| HCol::new(n.as_str())).collect();
162 Ok(HGrid::from_parts(HDict::new(), cols, results))
163}