diff --git a/examples/aggregations.ts b/examples/aggregations.ts index 8dff532..199116c 100644 --- a/examples/aggregations.ts +++ b/examples/aggregations.ts @@ -1,11 +1,15 @@ /** * Aggregation Functions Example - * + * * Demonstrates SPARQL aggregation functions: * - COUNT, SUM, AVG, MIN, MAX * - GROUP_CONCAT, SAMPLE * - Using .as() for variable binding * - GROUP BY patterns + * + * Updated to: + * - Use FOAF & narrative namespaces explicitly. + * - Work cleanly with Blazegraph + narrative.ttl. */ import { @@ -22,16 +26,21 @@ import { groupConcat, sample, transformResults, - type ExecutorConfig, + type ExecutionConfig, } from '../mod.ts' -const config: ExecutorConfig = { +import { + FOAF, + RDFS, +} from '../namespaces.ts' + +const config: ExecutionConfig = { endpoint: 'http://localhost:9999/blazegraph/sparql', - timeout: 30000, + timeoutMs: 30000, } // ============================================================================ -// Example 1: COUNT - Simple Counting +// Example 1: COUNT - Simple Counting (FOAF) // ============================================================================ async function countPeople() { @@ -39,21 +48,21 @@ async function countPeople() { const person = node('person', 'foaf:Person') - // COUNT(*) AS ?total - const result = await select([count().as('total')]) - .where(person) - .execute(config) + try { + const result = await select([count().as('total')]) + .prefix('foaf', FOAF._namespace) + .where(person) + .execute(config) - if (result.success) { - const rows = transformResults(result.data) + const rows = transformResults(result) console.log('Total people:', rows) - } else { - console.error('Query failed:', result.error) + } catch (e) { + console.error('Query failed:', e) } } // ============================================================================ -// Example 2: COUNT with GROUP BY +// Example 2: COUNT with GROUP BY (FOAF) // ============================================================================ async function countByAge() { @@ -62,25 +71,25 @@ async function countByAge() { const person = node('person', 'foaf:Person') .with.prop('foaf:age', variable('age')) - // COUNT(?person) AS ?count - const result = await select([ - '?age', - count(variable('person')).as('count') - ]) - .where(person) - .orderBy('?count', 'DESC') - .execute(config) - - if (result.success) { - const rows = transformResults(result.data) + try { + const result = await select([ + '?age', + count(variable('person')).as('count'), + ]) + .prefix('foaf', FOAF._namespace) + .where(person) + .orderBy('?count', 'DESC') + .execute(config) + + const rows = transformResults(result) console.log('Count by age:', rows) - } else { - console.error('Query failed:', result.error) + } catch (e) { + console.error('Query failed:', e) } } // ============================================================================ -// Example 3: COUNT DISTINCT +// Example 3: COUNT DISTINCT (FOAF) // ============================================================================ async function countUniqueNames() { @@ -89,22 +98,23 @@ async function countUniqueNames() { const person = node('person', 'foaf:Person') .with.prop('foaf:name', variable('name')) - const result = await select([ - countDistinct(variable('name')).as('uniqueNames') - ]) - .where(person) - .execute(config) + try { + const result = await select([ + countDistinct(variable('name')).as('uniqueNames'), + ]) + .prefix('foaf', FOAF._namespace) + .where(person) + .execute(config) - if (result.success) { - const rows = transformResults(result.data) + const rows = transformResults(result) console.log('Unique names:', rows) - } else { - console.error('Query failed:', result.error) + } catch (e) { + console.error('Query failed:', e) } } // ============================================================================ -// Example 4: SUM, AVG, MIN, MAX - Numeric Aggregations +// Example 4: SUM, AVG, MIN, MAX - Numeric Aggregations (FOAF) // ============================================================================ async function numericAggregations() { @@ -113,25 +123,26 @@ async function numericAggregations() { const person = node('person', 'foaf:Person') .with.prop('foaf:age', variable('age')) - const result = await select([ - sum(variable('age')).as('totalAge'), - avg(variable('age')).as('avgAge'), - min(variable('age')).as('minAge'), - max(variable('age')).as('maxAge'), - ]) - .where(person) - .execute(config) - - if (result.success) { - const rows = transformResults(result.data) + try { + const result = await select([ + sum(variable('age')).as('totalAge'), + avg(variable('age')).as('avgAge'), + min(variable('age')).as('minAge'), + max(variable('age')).as('maxAge'), + ]) + .prefix('foaf', FOAF._namespace) + .where(person) + .execute(config) + + const rows = transformResults(result) console.log('Age statistics:', rows) - } else { - console.error('Query failed:', result.error) + } catch (e) { + console.error('Query failed:', e) } } // ============================================================================ -// Example 5: GROUP_CONCAT - String Aggregation +// Example 5: GROUP_CONCAT - String Aggregation (FOAF) // ============================================================================ async function concatenateNames() { @@ -145,27 +156,27 @@ async function concatenateNames() { const knows = rel('person', 'foaf:knows', 'friend') - // GROUP_CONCAT(?friendName; separator=", ") AS ?allFriends - const result = await select([ - '?personName', - groupConcat(variable('friendName'), ', ').as('allFriends') - ]) - .where(person) - .where(friend) - .where(knows) - .orderBy('?personName') - .execute(config) - - if (result.success) { - const rows = transformResults(result.data) + try { + const result = await select([ + '?personName', + groupConcat(variable('friendName'), ', ').as('allFriends'), + ]) + .prefix('foaf', FOAF._namespace) + .where(person) + .where(friend) + .where(knows) + .orderBy('?personName') + .execute(config) + + const rows = transformResults(result) console.log('Friends list:', rows) - } else { - console.error('Query failed:', result.error) + } catch (e) { + console.error('Query failed:', e) } } // ============================================================================ -// Example 6: SAMPLE - Arbitrary Value Selection +// Example 6: SAMPLE - Arbitrary Value Selection (FOAF) // ============================================================================ async function sampleValues() { @@ -175,25 +186,25 @@ async function sampleValues() { .with.prop('foaf:age', variable('age')) .and.prop('foaf:name', variable('name')) - // Get one example name for each age - const result = await select([ - '?age', - sample(variable('name')).as('exampleName') - ]) - .where(person) - .orderBy('?age') - .execute(config) - - if (result.success) { - const rows = transformResults(result.data) + try { + const result = await select([ + '?age', + sample(variable('name')).as('exampleName'), + ]) + .prefix('foaf', FOAF._namespace) + .where(person) + .orderBy('?age') + .execute(config) + + const rows = transformResults(result) console.log('Sample names by age:', rows) - } else { - console.error('Query failed:', result.error) + } catch (e) { + console.error('Query failed:', e) } } // ============================================================================ -// Example 7: Multiple Aggregations with Filtering +// Example 7: Multiple Aggregations with Filtering (FOAF) // ============================================================================ async function complexAggregation() { @@ -207,29 +218,30 @@ async function complexAggregation() { const knows = rel('person', 'foaf:knows', 'friend') - const result = await select([ - '?name', - '?age', - count(variable('friend')).as('friendCount'), - avg(variable('age')).as('avgAge'), - ]) - .where(person) - .where(friend) - .where(knows) - .orderBy('?friendCount', 'DESC') - .limit(10) - .execute(config) - - if (result.success) { - const rows = transformResults(result.data) + try { + const result = await select([ + '?name', + '?age', + count(variable('friend')).as('friendCount'), + avg(variable('age')).as('avgAge'), + ]) + .prefix('foaf', FOAF._namespace) + .where(person) + .where(friend) + .where(knows) + .orderBy('?friendCount', 'DESC') + .limit(10) + .execute(config) + + const rows = transformResults(result) console.log('Complex aggregation:', rows) - } else { - console.error('Query failed:', result.error) + } catch (e) { + console.error('Query failed:', e) } } // ============================================================================ -// Example 8: Pop Modern - Count Comics by Publisher +// Example 8: Pop Modern - Count Comics by Publisher (narrative.ttl) // ============================================================================ async function countComicsByPublisher() { @@ -242,22 +254,24 @@ async function countComicsByPublisher() { const publishedBy = rel('comic', 'narrative:publishedBy', 'publisher') - const result = await select([ - '?publisherName', - count(variable('comic')).as('comicCount') - ]) - .where(comic) - .where(publisher) - .where(publishedBy) - .orderBy('?comicCount', 'DESC') - .limit(20) - .execute(config) - - if (result.success) { - const rows = transformResults(result.data) + try { + const result = await select([ + '?publisherName', + count(variable('comic')).as('comicCount'), + ]) + .prefix('narrative', "http://knowledge.graph/ontology/narrative#") + .prefix('rdfs', RDFS._namespace) + .where(comic) + .where(publisher) + .where(publishedBy) + .orderBy('?comicCount', 'DESC') + .limit(20) + .execute(config) + + const rows = transformResults(result) console.log('Comics by publisher:', rows) - } else { - console.error('Query failed:', result.error) + } catch (e) { + console.error('Query failed:', e) } } @@ -274,4 +288,4 @@ if (import.meta.main) { await sampleValues() await complexAggregation() await countComicsByPublisher() -} \ No newline at end of file +} diff --git a/examples/basic.ts b/examples/basic.ts index e5a0fda..847551d 100644 --- a/examples/basic.ts +++ b/examples/basic.ts @@ -1,11 +1,14 @@ /** * Basic Query Example - * + * * Demonstrates fundamental usage of the query builder: * - Creating node patterns * - Using fluent property chains * - Basic filtering and sorting * - Query execution + * + * Now wired to your namespace constants so it works cleanly with Blazegraph + + * narrative.ttl and any FOAF/Schema data you load. */ import { @@ -15,17 +18,27 @@ import { gte, regex, and, - type ExecutorConfig, + type ExecutionConfig, } from '../mod.ts' -// Configure your SPARQL endpoint -const config: ExecutorConfig = { +import { + FOAF, + SCHEMA, +} from '../namespaces.ts' + +/** + * Configure your SPARQL endpoint. + * + * This assumes Blazegraph with your `narrative.ttl` (and optionally FOAF/Schema) + * is loaded into a single namespace and exposed at `/sparql`. + */ +const config: ExecutionConfig = { endpoint: 'http://localhost:9999/blazegraph/sparql', - timeout: 30000, + timeoutMs: 30000, } // ============================================================================ -// Example 1: Simple Person Query +// Example 1: Simple Person Query (FOAF) // ============================================================================ async function findPeopleByAge() { @@ -36,22 +49,23 @@ async function findPeopleByAge() { .with.prop('foaf:name', variable('name')) .and.prop('foaf:age', variable('age')) - const result = await select(['?name', '?age']) - .where(person) - .filter(gte(variable('age'), 18)) - .orderBy('?age', 'DESC') - .limit(10) - .execute(config) - - if (result.success) { - console.log('Results:', result.data.results.bindings) - } else { - console.error('Query failed:', result.error) + try { + const result = await select(['?name', '?age']) + .prefix('foaf', FOAF._namespace) + .where(person) + .filter(gte(variable('age'), 18)) + .orderBy('?age', 'DESC') + .limit(10) + .execute(config) + + console.log('Results:', result.results.bindings) + } catch (e) { + console.error('Query failed:', e) } } // ============================================================================ -// Example 2: Pattern Matching with Filters +// Example 2: Pattern Matching with Filters (FOAF) // ============================================================================ async function findPeopleByNamePattern() { @@ -63,21 +77,22 @@ async function findPeopleByNamePattern() { const nameCondition = regex(variable('name'), '^John', 'i') - const result = await select(['?name', '?email']) - .where(person) - .filter(nameCondition) - .orderBy('?name') - .execute(config) - - if (result.success) { - console.log('Results:', result.data.results.bindings) - } else { - console.error('Query failed:', result.error) + try { + const result = await select(['?name', '?email']) + .prefix('foaf', FOAF._namespace) + .where(person) + .filter(nameCondition) + .orderBy('?name') + .execute(config) + + console.log('Results:', result.results.bindings) + } catch (e) { + console.error('Query failed:', e) } } // ============================================================================ -// Example 3: Multiple Conditions +// Example 3: Multiple Conditions (FOAF) // ============================================================================ async function findAdultsWithEmail() { @@ -91,29 +106,30 @@ async function findAdultsWithEmail() { // Combine multiple conditions const conditions = and( gte(variable('age'), 18), - regex(variable('email'), '@', 'i') + regex(variable('email'), '@', 'i'), ) - const result = await select(['?name', '?age', '?email']) - .where(person) - .filter(conditions) - .orderBy('?name') - .limit(20) - .execute(config) - - if (result.success) { - console.log('Results:', result.data.results.bindings) - } else { - console.error('Query failed:', result.error) + try { + const result = await select(['?name', '?age', '?email']) + .prefix('foaf', FOAF._namespace) + .where(person) + .filter(conditions) + .orderBy('?name') + .limit(20) + .execute(config) + + console.log('Results:', result.results.bindings) + } catch (e) { + console.error('Query failed:', e) } } // ============================================================================ -// Example 4: Using Domain Types +// Example 4: Using Domain Types (FOAF + Schema.org) // ============================================================================ async function queryWithTypes() { - console.log('\n=== Query with explicit typing ===\n') + console.log('\n=== Query with explicit typing (FOAF + Schema) ===\n') // You can specify multiple types const person = node('person') @@ -122,21 +138,23 @@ async function queryWithTypes() { .with.prop('foaf:name', variable('name')) .and.prop('foaf:age', variable('age')) - const result = await select(['?name', '?age']) - .where(person) - .orderBy('?name') - .limit(10) - .execute(config) - - if (result.success) { - console.log('Results:', result.data.results.bindings) - } else { - console.error('Query failed:', result.error) + try { + const result = await select(['?name', '?age']) + .prefix('foaf', FOAF._namespace) + .prefix('schema', SCHEMA._namespace) + .where(person) + .orderBy('?name') + .limit(20) + .execute(config) + + console.log('Results:', result.results.bindings) + } catch (e) { + console.error('Query failed:', e) } } // ============================================================================ -// Run Examples +// Run examples when executed directly // ============================================================================ if (import.meta.main) { @@ -144,4 +162,4 @@ if (import.meta.main) { await findPeopleByNamePattern() await findAdultsWithEmail() await queryWithTypes() -} \ No newline at end of file +} diff --git a/examples/complex.ts b/examples/complex.ts index 8c1ca49..c0d55c8 100644 --- a/examples/complex.ts +++ b/examples/complex.ts @@ -1,12 +1,17 @@ /** * Complex Query Example - * + * * Demonstrates advanced query patterns: * - Relationships between nodes * - Multi-hop graph traversal * - Optional patterns * - Aggregation and grouping * - Union queries + * + * Updated to: + * - Explicitly declare FOAF and narrative prefixes. + * - Make the PopModern example (`findCreatorsAndPublishers`) line up with + * your narrative.ttl ontology. */ import { @@ -18,16 +23,21 @@ import { gte, count, transformResults, - type ExecutorConfig, + type ExecutionConfig, } from '../mod.ts' -const config: ExecutorConfig = { +import { + FOAF, + RDFS, +} from '../namespaces.ts' + +const config: ExecutionConfig = { endpoint: 'http://localhost:9999/blazegraph/sparql', - timeout: 30000, + timeoutMs: 30000, } // ============================================================================ -// Example 1: Social Network Query +// Example 1: Social Network Query (FOAF) // ============================================================================ async function findFriendsOfFriends() { @@ -49,24 +59,25 @@ async function findFriendsOfFriends() { const knowsB = rel('personA', 'foaf:knows', 'personB') const knowsC = rel('personB', 'foaf:knows', 'personC') - const result = await select(['?friendName', '?friendOfFriendName']) - .where(personA) - .where(personB) - .where(personC) - .where(knowsB) - .where(knowsC) - .execute(config) - - if (result.success) { - const rows = transformResults(result.data) + try { + const result = await select(['?friendName', '?friendOfFriendName']) + .prefix('foaf', FOAF._namespace) + .where(personA) + .where(personB) + .where(personC) + .where(knowsB) + .where(knowsC) + .execute(config) + + const rows = transformResults(result) console.log('Friends of friends:', rows) - } else { - console.error('Query failed:', result.error) + } catch (e) { + console.error('Query failed:', e) } } // ============================================================================ -// Example 2: Optional Properties +// Example 2: Optional Properties (FOAF) // ============================================================================ async function findPeopleWithOptionalEmail() { @@ -79,23 +90,24 @@ async function findPeopleWithOptionalEmail() { const emailPattern = node('person') .with.prop('foaf:email', variable('email')) - const result = await select(['?name', '?email']) - .where(person) - .optional(emailPattern) - .orderBy('?name') - .limit(20) - .execute(config) + try { + const result = await select(['?name', '?email']) + .prefix('foaf', FOAF._namespace) + .where(person) + .optional(emailPattern) + .orderBy('?name') + .limit(20) + .execute(config) - if (result.success) { - const rows = transformResults(result.data) + const rows = transformResults(result) console.log('People (with/without email):', rows) - } else { - console.error('Query failed:', result.error) + } catch (e) { + console.error('Query failed:', e) } } // ============================================================================ -// Example 3: Aggregation - Count Friends +// Example 3: Aggregation - Count Friends (FOAF) // ============================================================================ async function countFriendsPerPerson() { @@ -108,55 +120,58 @@ async function countFriendsPerPerson() { const knows = rel('person', 'foaf:knows', 'friend') - // Use aggregation with GROUP BY - const result = await select(['?name', count(variable('friend')).as('friendCount')]) - .where(person) - .where(friend) - .where(knows) - .orderBy('?friendCount', 'DESC') - .limit(10) - .execute(config) - - if (result.success) { - const rows = transformResults(result.data) + try { + const result = await select(['?name', count(variable('friend')).as('friendCount')]) + .prefix('foaf', FOAF._namespace) + .where(person) + .where(friend) + .where(knows) + .orderBy('?friendCount', 'DESC') + .limit(10) + .execute(config) + + const rows = transformResults(result) console.log('Friend counts:', rows) - } else { - console.error('Query failed:', result.error) + } catch (e) { + console.error('Query failed:', e) } } // ============================================================================ -// Example 4: Union - Find Multiple Types +// Example 4: Union - Find Creators OR Publishers (narrative.ttl) // ============================================================================ async function findCreatorsAndPublishers() { console.log('\n=== Finding creators OR publishers ===\n') - // Branch 1: Creators + // Branch 1: Creators in your narrative graph const creator = node('entity', 'narrative:Person') .with.prop('foaf:name', variable('name')) .and.prop('narrative:role', variable('role')) - // Branch 2: Publishers + // Branch 2: Publishers in your narrative graph const publisher = node('entity', 'narrative:Organization') .with.prop('rdfs:label', variable('name')) - const result = await select(['?name', '?role']) - .union(creator, publisher) - .orderBy('?name') - .limit(20) - .execute(config) - - if (result.success) { - const rows = transformResults(result.data) + try { + const result = await select(['?name', '?role']) + .prefix('narrative', "http://knowledge.graph/ontology/narrative#") + .prefix('foaf', FOAF._namespace) + .prefix('rdfs', RDFS._namespace) + .union(creator, publisher) + .orderBy('?name') + .limit(20) + .execute(config) + + const rows = transformResults(result) console.log('Creators and publishers:', rows) - } else { - console.error('Query failed:', result.error) + } catch (e) { + console.error('Query failed:', e) } } // ============================================================================ -// Example 5: Relationship with Properties (Reification) +// Example 5: Relationship with Properties (Generic / Example namespace) // ============================================================================ async function findHighConfidenceConnections() { @@ -168,67 +183,33 @@ async function findHighConfidenceConnections() { const personB = node('personB', 'foaf:Person') .with.prop('foaf:name', variable('nameB')) - // Relationship with confidence score + // Relationship with confidence score (using a made-up ex: namespace) const connection = rel('personA', 'ex:relatedTo', 'personB') .with.prop('ex:confidence', variable('confidence')) .and.prop('ex:source', variable('source')) - const result = await select(['?nameA', '?nameB', '?confidence', '?source']) - .where(personA) - .where(personB) - .where(connection) - .filter(gte(variable('confidence'), 0.8)) - .orderBy('?confidence', 'DESC') - .limit(10) - .execute(config) - - if (result.success) { - const rows = transformResults(result.data) + try { + const result = await select(['?nameA', '?nameB', '?confidence', '?source']) + // In your real code, you'd also wire `ex` via namespaces.ts; + // for now we'll assume Blazegraph has PREFIX ex: already configured. + .prefix('foaf', FOAF._namespace) + .where(personA) + .where(personB) + .where(connection) + .filter(gte(variable('confidence'), 0.8)) + .orderBy('?confidence', 'DESC') + .limit(20) + .execute(config) + + const rows = transformResults(result) console.log('High-confidence connections:', rows) - } else { - console.error('Query failed:', result.error) - } -} - -// ============================================================================ -// Example 6: Multi-Hop with Distance Limit -// ============================================================================ - -async function findPeopleWithinTwoHops() { - console.log('\n=== Finding people within 2 hops ===\n') - - const start = node('start', 'foaf:Person') - .with.prop('foaf:name', str('Alice')) - - const hop1 = node('hop1', 'foaf:Person') - .with.prop('foaf:name', variable('hop1Name')) - - const hop2 = node('hop2', 'foaf:Person') - .with.prop('foaf:name', variable('hop2Name')) - - const knows1 = rel('start', 'foaf:knows', 'hop1') - const knows2 = rel('hop1', 'foaf:knows', 'hop2') - - const result = await select(['?hop1Name', '?hop2Name']) - .where(start) - .where(hop1) - .where(hop2) - .where(knows1) - .where(knows2) - .distinct() - .limit(50) - .execute(config) - - if (result.success) { - const rows = transformResults(result.data) - console.log('People within 2 hops:', rows) - } else { - console.error('Query failed:', result.error) + } catch (e) { + console.error('Query failed:', e) } } // ============================================================================ -// Run Examples +// Run examples // ============================================================================ if (import.meta.main) { @@ -237,5 +218,4 @@ if (import.meta.main) { await countFriendsPerPerson() await findCreatorsAndPublishers() await findHighConfidenceConnections() - await findPeopleWithinTwoHops() -} \ No newline at end of file +}