+7
-1
api/src/database/records.rs
+7
-1
api/src/database/records.rs
···
347
347
348
348
query_builder = query_builder.bind(limit as i64);
349
349
350
-
let records = query_builder.fetch_all(&self.pool).await?;
350
+
let mut records = query_builder.fetch_all(&self.pool).await?;
351
+
352
+
// Deduplicate lexicon records by URI (same URI can exist with different slice_uri values)
353
+
if is_lexicon {
354
+
let mut seen_uris = std::collections::HashSet::new();
355
+
records.retain(|record| seen_uris.insert(record.uri.clone()));
356
+
}
351
357
352
358
// Only return cursor if we got a full page, indicating there might be more
353
359
let cursor = if records.len() < limit as usize {
+15
-1
api/src/graphql/schema_builder.rs
+15
-1
api/src/graphql/schema_builder.rs
···
29
29
slice_uri: String,
30
30
) -> Result<Schema, String> {
31
31
// Fetch all lexicons for this slice
32
-
let lexicons = database
32
+
let all_lexicons = database
33
33
.get_lexicons_by_slice(&slice_uri)
34
34
.await
35
35
.map_err(|e| format!("Failed to load lexicons: {}", e))?;
36
+
37
+
// Deduplicate by NSID for schema building (keep most recent due to ORDER BY indexed_at DESC)
38
+
// This prevents duplicate type registration errors without hiding duplicates from users
39
+
let mut seen_nsids = std::collections::HashSet::new();
40
+
let lexicons: Vec<serde_json::Value> = all_lexicons
41
+
.into_iter()
42
+
.filter(|lexicon| {
43
+
if let Some(nsid) = lexicon.get("id").and_then(|n| n.as_str()) {
44
+
seen_nsids.insert(nsid.to_string())
45
+
} else {
46
+
true // Keep lexicons without an ID (will fail validation later)
47
+
}
48
+
})
49
+
.collect();
36
50
37
51
// Build Query root type and collect all object types
38
52
let mut query = Object::new("Query");