Transform the CEDS Ontology Browser from a two-tier (domain → classes) to a three-tier navigation system (domain → middle tier → classes/option sets) using first-word grouping for the middle tier.
/Users/tqwhite/Documents/webdev/A4L/unityObjectGenerator/system/datastore/cedsIds.sqlite3
Use these actual refIds for testing:
C200015
(known to exist)P000748
(known to exist) DOM_Organizations___Institutions
(valid domain)loginStore.getAuthTokenProperty()
{ headers: authHeader }
{ headers: { 'Content-Type': 'application/json', ...authHeader }}
IMPORTANT: Test these existing components as if implementing fresh, verify they work correctly:
server/data-model/access-points-dot-d/accessPoints.d/fetch-ceds-class-relationships.js
- May already exist for RDF relationshipsgetClassName()
helper implementationGoal: Create entity lookup table and populate with all CEDS entities using first-word middle tier grouping
TQ SAYS (about the above):
File: Manual SQL execution or add to rdf-tester initialization
CREATE TABLE IF NOT EXISTS CEDS_RDF_UI_SUPPORT_INDEX (
refId TEXT PRIMARY KEY,
entityType TEXT NOT NULL,
code TEXT,
uri TEXT,
label TEXT,
prefLabel TEXT,
notation TEXT,
domainRefId TEXT,
functionalAreaRefId TEXT,
parentRefId TEXT,
isOptionSet INTEGER DEFAULT 0,
displayOrder INTEGER,
crossDomainList TEXT,
createdAt TEXT DEFAULT CURRENT_TIMESTAMP,
updatedAt TEXT DEFAULT CURRENT_TIMESTAMP
);
-- Create indexes
CREATE INDEX IF NOT EXISTS idx_entity_type ON CEDS_RDF_UI_SUPPORT_INDEX(entityType);
CREATE INDEX IF NOT EXISTS idx_domain ON CEDS_RDF_UI_SUPPORT_INDEX(domainRefId);
CREATE INDEX IF NOT EXISTS idx_functional_area ON CEDS_RDF_UI_SUPPORT_INDEX(functionalAreaRefId);
CREATE INDEX IF NOT EXISTS idx_parent ON CEDS_RDF_UI_SUPPORT_INDEX(parentRefId);
CREATE INDEX IF NOT EXISTS idx_code ON CEDS_RDF_UI_SUPPORT_INDEX(code);
TQ SAYS (about the above): Add to rdf-tester. It has to be able to create the entire working database for this in an empty sqlite file (in fact, it has to be able to create the file, too.)
Also, I would like to call this table CEDS_RDF_UI_SUPPORT_INDEX
File: /cli/lib.d/rdf-tester/rdf-tester.js
Add new command: --buildEntityLookup
Implementation Steps:
Add command line option handling
Create buildEntityLookupTable()
function
Implement first-word extraction:
function extractFirstWord(className) {
// Handle special cases
if (!className) return 'Uncategorized';
// Simple extraction: everything before first camelCase boundary
// This handles: StudentRecord → Student, LEAAccountability → LEA, 123ABC → 123ABC
const match = className.match(/^[A-Z][a-z]+|^[A-Z]+(?=[A-Z][a-z])|^[a-z]+|^[0-9]+|^./);
return match ? match[0] : 'Other';
}
Note: This is intentionally simple - just extract whatever comes before the first camelCase word boundary. We will refine categorization later. Edge cases:
Single word classes stay as-is: Student
→ Student
Acronyms: LEAAccountability
→ LEA
Numbers: 504Plan
→ 504
Any other character just takes first character
Process entities in order:
For each class:
jsonString LIKE '%ConceptScheme%'
For each property:
TQ SAYS (about the above): Shouldn't the list in #4 include code sets?
ANSWER: Code sets (option sets) are already included in Step C "Insert all classes" because option sets ARE classes, just marked with ConceptScheme. They're not subordinate to other classes - they're peer classes that get referenced by properties.
Also, make sure the README and the help text explain everything about how to use this for either updating or creating a new database from scratch.
Update rdf-tester README and help text to include:
./rdf-tester --initDatabase --buildEntityLookup
./rdf-tester --buildEntityLookup
# Run the population
./rdf-tester --buildEntityLookup
# Verify data in SQLite
sqlite3 /path/to/cedsIds.sqlite3
# 1. Verify exact counts by entity type
SELECT entityType, COUNT(*) FROM CEDS_RDF_UI_SUPPORT_INDEX GROUP BY entityType;
# Expected:
# domain: 9
# functionalArea: 50-150 (depends on unique first words)
# class: ~1344 (including option sets)
# property: 748
# 2. Check first-word extraction edge cases
SELECT functionalAreaRefId, COUNT(*) as cnt, GROUP_CONCAT(label, ' | ') as examples
FROM CEDS_RDF_UI_SUPPORT_INDEX
WHERE entityType IN ('class', 'optionSet')
GROUP BY functionalAreaRefId
ORDER BY cnt DESC
LIMIT 10;
# Expected: Logical groupings like "Student" (30-50), "Organization" (20-40), etc.
# Watch for: Single-item groups that should be merged, overly large groups (>100)
# 3. Verify functional areas were created as entities
SELECT refId, label FROM CEDS_RDF_UI_SUPPORT_INDEX
WHERE entityType = 'functionalArea'
ORDER BY label;
# Expected: Clean first-word entries like "Student", "Organization", "Course"
# 4. Verify option set detection accuracy
SELECT label, isOptionSet, jsonString LIKE '%ConceptScheme%' as hasConceptScheme
FROM CEDS_RDF_UI_SUPPORT_INDEX
WHERE entityType = 'class'
AND (isOptionSet = 1 OR jsonString LIKE '%ConceptScheme%')
LIMIT 10;
# Expected: isOptionSet and hasConceptScheme should always match
# 5. Check properties have proper metadata
SELECT COUNT(*) as total,
COUNT(label) as has_label,
COUNT(code) as has_code,
COUNT(uri) as has_uri
FROM CEDS_RDF_UI_SUPPORT_INDEX
WHERE entityType = 'property';
# Expected: All counts should be 748 (no missing metadata)
# 6. Verify cross-domain accuracy
SELECT c.refId, c.label, c.crossDomainList, COUNT(DISTINCT cd.domainRefId) as actual_domains
FROM CEDS_RDF_UI_SUPPORT_INDEX c
JOIN CEDS_ClassToDomain cd ON c.refId = cd.classRefId
WHERE c.entityType = 'class'
GROUP BY c.refId
HAVING actual_domains > 1
LIMIT 5;
# Expected: crossDomainList count matches actual_domains
# 7. Test rebuild capability
DROP TABLE CEDS_RDF_UI_SUPPORT_INDEX;
./rdf-tester --buildEntityLookup
SELECT COUNT(*) FROM CEDS_RDF_UI_SUPPORT_INDEX;
# Expected: Same counts as before, completes without errors
Success Criteria:
Goal: Create API endpoints to serve entity lookup data efficiently
TQ SAYS (about the above): I take your word that these comprise an adequate set to support the UI.
File: /server/data-model/access-points-dot-d/accessPoints.d/fetch-entity-lookup.js
Functions:
fetchAllEntities()
- Return all entities (lightweight)fetchEntitiesByDomain(domainRefId)
- Return entities for specific domainfetchEntityDetails(refId)
- Return full details for one entityfetchFunctionalAreasByDomain(domainRefId)
- Return middle tier categoriesTQ SAYS (about the above):
File: /server/data-model/data-mapping/mappers/ceds-entity-lookup.js
Define SQL queries:
getAllEntities
: SELECT refId, label, entityType, domainRefId, functionalAreaRefIdgetByDomain
: Filter by domainRefIdgetFunctionalAreas
: SELECT DISTINCT functionalAreaRefId with countsgetEntityDetails
: Full row by refIdTQ SAYS (about the above):
File: /server/endpoints-dot-d/endpoints.d/ceds/cedsEntityLookup.js
Endpoints:
GET /api/ceds/fetchEntityLookup
- All entities or by domainGET /api/ceds/fetchEntityDetails/:refId
- Single entity detailsGET /api/ceds/fetchFunctionalAreas/:domainId
- Middle tier for domainTQ SAYS (about the above):
// Test in browser console or with Node.js script
// 1. Test full entity lookup (should include all types)
const response1 = await fetch('/api/ceds/fetchEntityLookup');
const allEntities = await response1.json();
console.log('Total entities:', allEntities.length);
console.log('Entity types:', [...new Set(allEntities.map(e => e.entityType))]);
// Expected: ~2100+ entities, types: ['domain', 'functionalArea', 'class', 'property']
// 2. Test domain filtering
const response2 = await fetch('/api/ceds/fetchEntityLookup?domain=DOM_Organizations___Institutions');
const domainEntities = await response2.json();
const classCount = domainEntities.filter(e => e.entityType === 'class').length;
console.log('Classes in Organizations domain:', classCount);
// Expected: 100-200 classes for this domain
// 3. Test functional areas with counts
const response3 = await fetch('/api/ceds/fetchFunctionalAreas/DOM_Organizations___Institutions');
const functionalAreas = await response3.json();
console.log('Functional areas:', functionalAreas.map(fa => `${fa.label}: ${fa.count}`));
// Expected: Array like [{label: 'Organization', count: 42}, {label: 'Student', count: 35}...]
// Verify: Total of counts should equal classCount from test 2
// 4. Test entity details for a class
const response4 = await fetch('/api/ceds/fetchEntityDetails/C200015');
const classDetails = await response4.json();
console.log('Class has required fields:', {
hasLabel: !!classDetails.label,
hasType: !!classDetails.entityType,
hasOptionSetFlag: classDetails.isOptionSet !== undefined,
hasCrossDomains: !!classDetails.crossDomainList
});
// Expected: All should be true
// 5. Test entity details for a property
const propResponse = await fetch('/api/ceds/fetchEntityDetails/P000748');
const propDetails = await propResponse.json();
console.log('Property details:', propDetails);
// Expected: entityType = 'property', has label, has uri
// 6. Performance test - measure full lookup time
console.time('FullEntityLookup');
const perfResponse = await fetch('/api/ceds/fetchEntityLookup');
const perfData = await perfResponse.json();
console.timeEnd('FullEntityLookup');
// Expected: < 500ms for ~2100 entities
// 7. Test non-existent entity
const response7 = await fetch('/api/ceds/fetchEntityDetails/FAKE_ID_12345');
const notFound = await response7.json();
console.log('Non-existent entity returns:', notFound);
// Expected: null or empty object, not an error
Success Criteria:
Goal: Update Pinia store to manage entity lookup data and three-tier navigation state
TQ SAYS (about the above):
File: /html/stores/ontologyStore.js
New State Properties:
const entityLookup = ref(new Map()); // refId -> entity info
const functionalAreas = ref([]); // Current domain's middle tier
const selectedFunctionalArea = ref(null); // Current middle tier selection
const entitiesInArea = ref([]); // Classes in selected functional area
New Actions:
// Load entity lookup on app init
async loadEntityLookup() {
const response = await fetch('/api/ceds/fetchEntityLookup', {
headers: authHeader
});
const entities = await response.json();
// Build Map for O(1) lookups
entities.forEach(entity => {
entityLookup.value.set(entity.refId, entity);
});
}
// Get entity name by refId (replaces inline lookups)
getEntityName(refId) {
const entity = entityLookup.value.get(refId);
return entity?.label || refId;
}
// Load functional areas when domain changes
async loadFunctionalAreas(domainRefId) {
const response = await fetch(`/api/ceds/fetchFunctionalAreas/${domainRefId}`, {
headers: authHeader
});
functionalAreas.value = await response.json();
}
// Select functional area and load its classes
selectFunctionalArea(areaId) {
selectedFunctionalArea.value = areaId;
// Filter loaded classes by functional area
entitiesInArea.value = classes.value.filter(c => {
const entity = entityLookup.value.get(c.refId);
return entity?.functionalAreaRefId === areaId;
});
}
TQ SAYS (about the above):
Modify selectDomain()
to also load functional areas:
async selectDomain(domain) {
currentDomain.value = domain;
await Promise.all([
loadClassesByDomain(domain.refId),
loadFunctionalAreas(domain.refId)
]);
// Auto-select first functional area if exists
if (functionalAreas.value.length > 0) {
selectFunctionalArea(functionalAreas.value[0].id);
}
}
TQ SAYS (about the above):
// In browser console after loading app
const store = useOntologyStore();
// 1. Verify entity lookup Map structure
console.log('Entity lookup size:', store.entityLookup.size);
console.log('Sample entity:', store.entityLookup.get('C200015'));
// Expected: ~2100+ entities, each with refId, label, entityType, etc.
// 2. Test name resolution for different entity types
const testIds = {
class: 'C200015',
property: 'P000748',
domain: 'DOM_Organizations___Institutions',
fake: 'FAKE_ID_999'
};
for (const [type, id] of Object.entries(testIds)) {
console.log(`${type}: ${id} -> ${store.getEntityName(id)}`);
}
// Expected: Actual names for real IDs, 'FAKE_ID_999' for non-existent
// 3. Test domain selection loads both classes and functional areas
console.time('DomainSwitch');
await store.selectDomain(store.domains[0]);
console.timeEnd('DomainSwitch');
console.log('Classes loaded:', store.classes.length);
console.log('Functional areas:', store.functionalAreas.map(fa => `${fa.label}(${fa.count})`));
// Expected: Both load in parallel, < 1s total time
// 4. Test functional area selection and filtering
const firstArea = store.functionalAreas[0];
store.selectFunctionalArea(firstArea.id);
console.log('Selected area:', store.selectedFunctionalArea);
console.log('Filtered classes:', store.entitiesInArea.length);
// Expected: entitiesInArea.length matches firstArea.count
// 5. Verify option sets are identifiable
const optionSets = store.entitiesInArea.filter(e => {
const entity = store.entityLookup.get(e.refId);
return entity?.isOptionSet;
});
console.log('Option sets in area:', optionSets.map(os => os.label));
// Expected: Some classes should be marked as option sets
// 6. Test cross-domain information availability
const crossDomainClass = [...store.entityLookup.values()].find(e =>
e.crossDomainList && e.crossDomainList.includes(',')
);
console.log('Cross-domain class:', {
label: crossDomainClass.label,
domains: crossDomainClass.crossDomainList
});
// Expected: Should find classes that appear in multiple domains
// 7. Memory usage check
if (performance.memory) {
console.log('Memory used by entity lookup:',
`${(performance.memory.usedJSHeapSize / 1024 / 1024).toFixed(2)} MB`);
}
// Expected: Reasonable memory usage for ~2100 entities (< 10MB)
Success Criteria:
Goal: Update components to display three-tier navigation
TQ SAYS (about the above):
File: /html/components/tools/ontology/ClassOutline.vue
Changes:
Add toolbar with expand/collapse controls
Add functional area accordion/expansion panels
Structure:
[Expand All] [Collapse All]
─────────────────────────────
Domain Tab (selected)
└── Functional Areas (collapsible sections)
├── Organization (42 classes)
│ ├── OrganizationIdentifier
│ ├── OrganizationIndicator [Option Set]
│ └── ...
├── Student (38 classes)
│ ├── StudentAcademicRecord
│ └── ...
Visual distinctions:
TQ SAYS (about the above): Please add 'Collapse All' and 'Expand All' buttons at the top of the list.
ANSWER: Added toolbar with these buttons in step 1 above.
Also, what does Option Set mean in this: OrganizationIndicator [Option Set]? I still think option sets are subordinate to classes. Am I wrong?
ANSWER: Option sets ARE classes themselves (marked with ConceptScheme type). They appear at the same level as regular classes in the navigation. The [Option Set] label just helps users distinguish "this class contains predefined values" from "this class stores data". Classes reference option sets through their properties' range definitions.
File: /html/components/tools/ontology/ClassDetails.vue
Changes:
Replace inline getClassName()
with store method:
const getClassName = (refId) => {
return ontologyStore.getEntityName(refId);
};
Add breadcrumb showing position:
Organizations & Institutions > Student > StudentAcademicRecord
For cross-domain classes, show badges for other domains
NEW: Enhanced property display showing option set ranges:
// In properties section, show which option sets are used
Properties:
- stateCode → RefStateCodeSet [Option Set] (click to view values)
- organizationType → RefOrganizationType [Option Set]
- identifier → String (primitive type)
- startDate → Date (primitive type)
Implementation:
TQ SAYS (about the above): Good idea. I want the breadcrumb.
File: /html/components/tools/ontology/FunctionalAreaPanel.vue
New component for middle tier section with:
TQ SAYS (about the above): Is this talking about the display when a collapsible item is opened?
ANSWER: Yes, exactly. This component renders the content that appears when you expand a functional area (middle tier category). It shows the sorted list of classes and option sets within that category, with appropriate icons and click handlers.
Detailed UI Testing Checklist:
// Browser console tests during manual interaction
// 1. Three-tier navigation structure
console.log('Domain tabs visible:', document.querySelectorAll('.domain-tab').length === 9);
console.log('Functional areas visible:', document.querySelectorAll('.functional-area').length);
// Expected: 9 domain tabs, 10-50 functional areas per domain
// 2. Expand/Collapse All functionality
document.querySelector('[data-test="expand-all"]').click();
console.log('All expanded:', document.querySelectorAll('.functional-area.expanded').length);
document.querySelector('[data-test="collapse-all"]').click();
console.log('All collapsed:', document.querySelectorAll('.functional-area.expanded').length);
// Expected: All areas expand/collapse together
// 3. Option set visual indicators
const optionSetIcons = document.querySelectorAll('.mdi-format-list-bulleted-square');
console.log('Option sets with special icon:', optionSetIcons.length);
// Expected: ~200-300 option sets with different icon
// 4. Breadcrumb accuracy
// Click: Organizations domain → Student functional area → StudentAcademicRecord class
const breadcrumb = document.querySelector('.breadcrumb').textContent;
console.log('Breadcrumb:', breadcrumb);
// Expected: "Organizations & Institutions > Student > StudentAcademicRecord"
// 5. Property range display
const propertyRows = document.querySelectorAll('.property-row');
const withOptionSets = [...propertyRows].filter(row =>
row.textContent.includes('[Option Set]')
);
console.log(`Properties with option sets: ${withOptionSets.length}/${propertyRows.length}`);
// Expected: Many properties should show their option set ranges
// 6. No IDs visible test
const allText = document.body.innerText;
const hasClassIds = /C\d{6}/.test(allText); // Pattern for class IDs
const hasPropertyIds = /P\d{6}/.test(allText); // Pattern for property IDs
console.log('Class IDs visible:', hasClassIds);
console.log('Property IDs visible:', hasPropertyIds);
// Expected: Both should be false (all resolved to names)
// 7. Cross-domain navigation
// Click a class that exists in multiple domains
const crossDomainBadges = document.querySelectorAll('.cross-domain-badge');
console.log('Cross-domain badges visible:', crossDomainBadges.length);
crossDomainBadges[0]?.click();
// Expected: Should switch to different domain and select the class
Performance Metrics:
// Measure UI responsiveness
console.time('ExpandFunctionalArea');
document.querySelector('.functional-area-header').click();
console.timeEnd('ExpandFunctionalArea');
// Expected: < 100ms to expand and show classes
console.time('SelectClass');
document.querySelector('.class-item').click();
console.timeEnd('SelectClass');
// Expected: < 200ms to load and display class details
console.time('NavigateToLinkedClass');
document.querySelector('.linked-class').click();
console.timeEnd('NavigateToLinkedClass');
// Expected: < 500ms even if switching domains
Accessibility Checks:
Success Criteria:
Goal: Ensure system works end-to-end and performs well
TQ SAYS (about the above):
Scenario 1: Complete Navigation Flow
// Start at root, navigate to specific class via three tiers
1. Load app fresh (no deep link)
2. Click "Organizations & Institutions" domain tab
3. Wait for functional areas to load
4. Expand "Organization" functional area
5. Click "OrganizationIdentifier" class
6. Verify breadcrumb: "Organizations & Institutions > Organization > OrganizationIdentifier"
7. In properties, click linked option set "RefOrganizationIdentifierType"
8. Verify navigation to option set (should have [Option Set] indicator)
9. Check that option set values are displayed
// Expected: Each step < 500ms, total flow < 3s
Scenario 2: Cross-Domain Navigation via RDF Links
1. Navigate to a class with cross-domain relationships
2. In RDF relationships table, identify class from different domain
3. Click the linked class name
4. Verify:
- Domain tab switches automatically
- Functional area expands to show the class
- Class is selected and highlighted
- Breadcrumb updates correctly
// Expected: Seamless transition even across domains
Scenario 3: Deep Link Restoration
// Test URL: /ontology/DOM_Organizations___Institutions/C200015
1. Load app with deep link URL
2. Verify:
- Correct domain tab is selected
- Entity lookup is loaded (no IDs showing)
- Functional area containing C200015 is expanded
- C200015 class is selected
- Details panel shows full information
// Expected: Direct navigation works, < 2s to fully load
Scenario 4: Edge Cases
// Test unusual data conditions
// A. Single-item functional area
1. Find functional area with only 1 class
2. Expand it
3. Verify it still works correctly
// B. Very large functional area (>100 items)
1. Find largest functional area
2. Expand it
3. Measure performance
// Expected: Still responsive, consider pagination if >100
// C. Class with no properties
1. Find class with no properties
2. Verify RDF relationships still show
3. Verify metadata relationships visible
// D. Property with no range
1. Find property without option set range
2. Verify shows primitive type or "unspecified"
Scenario 5: Search Integration
1. Open search (magnifying glass icon)
2. Search for "Student"
3. Verify results show:
- Functional area "Student"
- All classes starting with "Student"
- Domain context for each result
4. Click a search result
5. Verify:
- Navigates to correct domain
- Expands correct functional area
- Selects the class
- Search overlay closes
// Expected: Search-to-selection < 1s
TQ SAYS (about the above):
// Comprehensive performance benchmarks
// 1. Initial app load (cold start)
performance.mark('AppLoadStart');
// ... app initialization ...
performance.mark('AppLoadEnd');
performance.measure('AppLoad', 'AppLoadStart', 'AppLoadEnd');
const appLoad = performance.getEntriesByName('AppLoad')[0];
console.log(`App load: ${appLoad.duration}ms`);
// Target: < 2000ms including entity lookup
// 2. Stress test functional area expansion
const allAreas = document.querySelectorAll('.functional-area-header');
console.time('ExpandAll');
allAreas.forEach(area => area.click());
console.timeEnd('ExpandAll');
// Target: < 1000ms for all areas
// 3. Rapid domain switching
console.time('Switch5Domains');
for (let i = 0; i < 5; i++) {
await store.selectDomain(store.domains[i]);
}
console.timeEnd('Switch5Domains');
// Target: < 3000ms total (600ms average per switch)
// 4. Name resolution stress test
const allRefIds = [...store.entityLookup.keys()];
console.time('ResolveAllNames');
allRefIds.forEach(refId => store.getEntityName(refId));
console.timeEnd('ResolveAllNames');
console.log(`Resolved ${allRefIds.length} names`);
// Target: < 200ms for ~2100 names
// 5. Memory profiling
const beforeExpand = performance.memory?.usedJSHeapSize || 0;
document.querySelector('[data-test="expand-all"]').click();
const afterExpand = performance.memory?.usedJSHeapSize || 0;
console.log(`Memory increase: ${((afterExpand - beforeExpand) / 1024 / 1024).toFixed(2)} MB`);
// Target: < 5MB increase
// 6. Search performance
console.time('SearchStudent');
await searchStore.search('Student');
console.timeEnd('SearchStudent');
// Target: < 100ms for search results
TQ SAYS (about the above):
-- 1. Verify no orphaned classes (all have functional areas)
SELECT COUNT(*) as orphaned_classes
FROM CEDS_RDF_UI_SUPPORT_INDEX
WHERE entityType = 'class'
AND (functionalAreaRefId IS NULL OR functionalAreaRefId = '');
-- Expected: 0
-- 2. Check functional area consistency
SELECT fa.functionalAreaRefId,
COUNT(DISTINCT fa.domainRefId) as domain_count
FROM CEDS_RDF_UI_SUPPORT_INDEX fa
WHERE fa.entityType = 'class'
GROUP BY fa.functionalAreaRefId
HAVING domain_count > 1;
-- Expected: Some functional areas span domains (like "Student")
-- 3. Verify all original classes are indexed
SELECT 'Missing classes:', COUNT(*)
FROM CEDS_Classes c
WHERE NOT EXISTS (
SELECT 1 FROM CEDS_RDF_UI_SUPPORT_INDEX e
WHERE e.refId = c.refId
);
-- Expected: 0
-- 4. Verify all original properties are indexed
SELECT 'Missing properties:', COUNT(*)
FROM CEDS_Properties p
WHERE NOT EXISTS (
SELECT 1 FROM CEDS_RDF_UI_SUPPORT_INDEX e
WHERE e.refId = p.refId
);
-- Expected: 0
-- 5. Check option set marking accuracy
SELECT 'Mismarked option sets:', COUNT(*)
FROM CEDS_RDF_UI_SUPPORT_INDEX
WHERE entityType = 'class'
AND ((isOptionSet = 1 AND jsonString NOT LIKE '%ConceptScheme%')
OR (isOptionSet = 0 AND jsonString LIKE '%ConceptScheme%'));
-- Expected: 0
-- 6. Validate cross-domain lists
SELECT refId, label,
LENGTH(crossDomainList) - LENGTH(REPLACE(crossDomainList, ',', '')) + 1 as list_count,
(SELECT COUNT(DISTINCT domainRefId)
FROM CEDS_ClassToDomain
WHERE classRefId = e.refId) as actual_count
FROM CEDS_RDF_UI_SUPPORT_INDEX e
WHERE entityType = 'class' AND crossDomainList IS NOT NULL
AND list_count != actual_count;
-- Expected: 0 (all counts match)
-- 7. Performance check on indexed columns
EXPLAIN QUERY PLAN
SELECT * FROM CEDS_RDF_UI_SUPPORT_INDEX
WHERE domainRefId = 'DOM_Organizations___Institutions'
AND functionalAreaRefId = 'Organization';
-- Expected: Uses indexes, not table scan
If issues arise at any phase:
Once the three-tier system is working:
Total: 9-14 hours
The implementation is complete when: