This guide helps you migrate from the old semantic query system to the new enhanced version with property-path queries, entity matching, and radial layout.
All existing imports continue to work. New APIs are additive.
The orbit resolver now works correctly without the require is not defined error.
No code changes needed - it just works now.
Before:
import { findRelatedConcepts } from './services/semanticWebQuery.js';
const results = await findRelatedConcepts("Mario", { limit: 20 });
// Results: Array of entities without clear relationships
results.forEach(item => {
console.log(item.itemLabel?.value || item.label?.value);
// No clear relationship information
});After:
import { discoverConnections } from './services/semanticDiscovery.js';
const results = await discoverConnections("Mario", { limit: 20 });
// Results: Clear relationships with labels
results.connections.forEach(conn => {
console.log(`${conn.source} → ${conn.relation} → ${conn.target}`);
// Example: "Mario → developer → Nintendo"
// Example: "Mario → genre → Platform game"
});Benefits:
- 10-50x faster
- Clear relationship labels
- Confidence scores
- Organized by provider
Before:
const wikidataResults = await queryWikidata("Mario");
const dbpediaResults = await queryDBpedia("Mario");
// Manual deduplication
const combined = [...wikidataResults, ...dbpediaResults];
// Likely contains duplicatesAfter:
import { deduplicateEntities } from './services/entityMatching.js';
const wikidataResults = await queryWikidata("Mario");
const dbpediaResults = await queryDBpedia("Mario");
const combined = [...wikidataResults, ...dbpediaResults];
const deduplicated = deduplicateEntities(combined, {
autoMergeThreshold: 0.85
});
// Duplicates automatically merged
console.log(`${combined.length} → ${deduplicated.length} entities`);Benefits:
- No duplicate entities
- Merged data from multiple sources
- Confidence-based matching
Before:
// Manual workflow
const results = await findRelatedConcepts(entityName);
// Manual filtering
const filtered = results.filter(r => /* some logic */);
// Manual layout calculation
const positions = calculatePositions(filtered);After:
import { exploreEntity } from './services/semanticIntegration.js';
const result = await exploreEntity(entityName, {
maxDepth: 2,
minConfidence: 0.65,
generateLayout: true
});
// Everything done for you:
// - Discovery
// - Deduplication
// - Organization into orbits
// - Layout generation
console.log(result.entities); // Deduplicated entities
console.log(result.connections); // Clear relationships
console.log(result.layout); // Ready-to-render layoutBenefits:
- One function call
- Automatic workflow
- Consistent results
Before:
// In OrbitOverlay.jsx or similar
const candidates = await fetchOrbitCandidatesForPrototype(prototype);
// Manually position nodes
const positions = candidates.inner.map((candidate, idx) => {
const angle = (idx / candidates.inner.length) * 2 * Math.PI;
return {
x: RADIUS * Math.cos(angle),
y: RADIUS * Math.sin(angle),
candidate
};
});After:
import { exploreEntity } from './services/semanticIntegration.js';
import { layoutRadialGraph } from './services/radialLayout.js';
// Get data with layout
const result = await exploreEntity(prototype.name, {
maxDepth: 1,
maxConnectionsPerLevel: 16,
generateLayout: true
});
// Use pre-calculated positions
result.layout.nodes.forEach(node => {
renderNode(node.x, node.y, node.node, {
dimensions: node.dimensions,
zIndex: node.zIndex,
opacity: node.opacity
});
});
// Render connections with curves
result.layout.connections.forEach(conn => {
renderConnection(conn.path, conn.relation);
});Benefits:
- No overlaps
- Dimension-aware layout
- Smart overflow handling
- Labeled connections
Add new imports alongside existing code:
// Existing imports (keep these)
import { findRelatedConcepts } from './services/semanticWebQuery.js';
// New imports (add these)
import { discoverConnections } from './services/semanticDiscovery.js';
import { exploreEntity } from './services/semanticIntegration.js';
// Test side-by-side
async function testComparison(entityName) {
// Old way
const oldResults = await findRelatedConcepts(entityName);
// New way
const newResults = await discoverConnections(entityName);
console.log('Old:', oldResults.length, 'results');
console.log('New:', newResults.connections.length, 'connections');
console.log('Speedup:', /* compare times */);
}Replace orbit candidate fetching:
// In orbitResolver.js or component
import { quickDiscover } from './services/semanticIntegration.js';
async function fetchOrbitCandidatesForPrototype(prototype, options = {}) {
try {
const result = await quickDiscover(prototype.name, {
maxConnectionsPerLevel: 16,
minConfidence: 0.6
});
// Convert to existing format for compatibility
const candidates = result.connections.map(conn => ({
name: conn.target,
uri: conn.targetUri,
predicate: conn.relation,
source: conn.provider,
score: conn.confidence,
tier: conn.confidence > 0.8 ? 'A' : 'B'
}));
return {
inner: candidates.filter(c => c.tier === 'A').slice(0, 8),
outer: candidates.filter(c => c.tier !== 'A').slice(0, 16),
all: candidates
};
} catch (error) {
console.error('Orbit fetch failed:', error);
return { inner: [], outer: [], all: [] };
}
}If you have custom layout code:
import { layoutRadialGraph } from './services/radialLayout.js';
function layoutOrbitNodes(centralNode, orbitCandidates) {
// Convert candidates to orbits
const orbits = [
{
level: 1,
entities: orbitCandidates.inner.map(c => ({ name: c.name }))
},
{
level: 2,
entities: orbitCandidates.outer.map(c => ({ name: c.name }))
}
];
// Generate layout
const layout = layoutRadialGraph(
centralNode,
orbits,
[], // connections (optional)
{
baseRadius: 180,
orbitSpacing: 140,
minNodeMargin: 28
}
);
return layout;
}Show relationship labels on connections:
function renderConnection(connection, layout) {
const { path, relation, confidence } = connection;
// Draw the path
if (path.type === 'curved') {
ctx.beginPath();
ctx.moveTo(path.x1, path.y1);
ctx.quadraticCurveTo(path.cx, path.cy, path.x2, path.y2);
ctx.strokeStyle = `rgba(100, 100, 100, ${confidence * 0.6})`;
ctx.lineWidth = 1 + confidence;
ctx.stroke();
// Add label at midpoint
const midX = (path.x1 + path.x2) / 2;
const midY = (path.y1 + path.y2) / 2;
ctx.font = '11px sans-serif';
ctx.fillStyle = '#666';
ctx.fillText(relation, midX, midY);
}
}async function testPerformance(entityName) {
console.time('Old Query');
const oldResults = await findRelatedConcepts(entityName);
console.timeEnd('Old Query');
console.time('New Query');
const newResults = await discoverConnections(entityName);
console.timeEnd('New Query');
console.log({
oldCount: oldResults.length,
newCount: newResults.connections.length,
improvement: '10-50x faster'
});
}import { deduplicateEntities } from './services/entityMatching.js';
async function testDeduplication(entityName) {
const [wikidata, dbpedia] = await Promise.all([
queryWikidata(entityName),
queryDBpedia(entityName)
]);
const combined = [...wikidata, ...dbpedia];
const deduplicated = deduplicateEntities(combined);
console.log({
before: combined.length,
after: deduplicated.length,
reduction: `${Math.round((1 - deduplicated.length / combined.length) * 100)}%`
});
}import { layoutRadialGraph } from './services/radialLayout.js';
function testLayout(centralNode, orbitData) {
const layout = layoutRadialGraph(centralNode, orbitData);
// Check for overlaps
let overlapCount = 0;
for (let i = 0; i < layout.nodes.length; i++) {
for (let j = i + 1; j < layout.nodes.length; j++) {
const dist = Math.sqrt(
(layout.nodes[i].x - layout.nodes[j].x) ** 2 +
(layout.nodes[i].y - layout.nodes[j].y) ** 2
);
if (dist < 60) overlapCount++;
}
}
console.log({
totalNodes: layout.nodes.length,
overlaps: overlapCount,
quality: overlapCount === 0 ? 'Perfect' : 'Needs adjustment'
});
}Problem: New API returns different shape
Solution: Use transformation functions
function convertToLegacyFormat(modernResult) {
return {
inner: modernResult.connections
.filter(c => c.confidence > 0.8)
.slice(0, 8)
.map(c => ({ name: c.target, ...c })),
outer: modernResult.connections
.filter(c => c.confidence <= 0.8)
.slice(0, 16)
.map(c => ({ name: c.target, ...c }))
};
}Problem: Queries taking too long
Solution: Adjust timeout parameters
const result = await discoverConnections(entityName, {
timeout: 10000, // Reduce for faster failure
limit: 15, // Fewer results = faster
minConfidence: 0.70 // Higher threshold = fewer queries
});Problem: Nodes positioned off-screen
Solution: Adjust radius and scaling
const layout = layoutRadialGraph(central, orbits, [], {
baseRadius: 120, // Smaller initial radius
orbitSpacing: 100, // Tighter spacing
minNodeMargin: 20 // Smaller margins
});If you need to rollback:
- Remove new imports (all in
src/services/semantic*.js) - Keep existing code (no changes to existing files except
orbitResolver.js) - For orbitResolver.js: Revert the
require→importchange if needed
All new functionality is in separate files, so rollback is simple.
For issues or questions:
- Check
SEMANTIC_DISCOVERY_GUIDE.mdfor usage examples - See
SEMANTIC_IMPROVEMENTS_SUMMARY.mdfor technical details - Review test file:
src/services/__tests__/semanticSystem.test.js
- Monitor performance - Log query times
- Tune parameters - Adjust confidence thresholds
- Add visualizations - Use layout data for UI
- Extend mappings - Add domain-specific properties
- Enable caching - Cache frequent queries