Query Entity Relationships in Knowledge Graph

User Intent

"How do I query relationships between entities? Show me how to find all people at an organization, products by a company, or events at a location."

Operation

SDK Methods: queryObservables() with relationship filters, queryContents() with entity filters GraphQL Query: queryObservables, queryContents Entity: Observable relationships and co-occurrence patterns

Prerequisites

  • Graphlit project with extracted entities (knowledge graph built)

  • Understanding of Observable/Observation model

  • Content with entity extraction completed


Complete Code Example (TypeScript)

import { Graphlit } from 'graphlit-client';
import { ObservableTypes, EntityState } from 'graphlit-client/dist/generated/graphql-types';

const graphlit = new Graphlit();

console.log('=== Querying Entity Relationships ===\n');

// Example 1: Find all people at Graphlit
console.log('Example 1: Person → Organization\n');

// First, find the Graphlit organization entity
const graphlitOrg = await graphlit.queryObservables({
  search: "Graphlit",
  filter: {
    types: [ObservableTypes.Organization],
    states: [EntityState.Enabled]
  }
});

if (graphlitOrg.observables.results.length > 0) {
  const orgId = graphlitOrg.observables.results[0].observable.id;
  console.log(`Found organization: ${graphlitOrg.observables.results[0].observable.name}\n`);
  
  // Find all content mentioning Graphlit
  const graphlitContent = await graphlit.queryContents({
    filter: {
      observations: [{
        type: ObservableTypes.Organization,
        observable: { id: orgId }
      }]
    }
  });
  
  // Extract all people mentioned in that content
  const peopleAtGraphlit = new Map<string, { id: string; name: string }>();
  
  graphlitContent.contents.results.forEach(content => {
    content.observations
      ?.filter(obs => obs.type === ObservableTypes.Person)
      .forEach(obs => {
        peopleAtGraphlit.set(obs.observable.id, {
          id: obs.observable.id,
          name: obs.observable.name
        });
      });
  });
  
  console.log(`People at Graphlit: ${peopleAtGraphlit.size}`);
  Array.from(peopleAtGraphlit.values()).slice(0, 5).forEach(person => {
    console.log(`  - ${person.name}`);
  });
  if (peopleAtGraphlit.size > 5) {
    console.log(`  ... and ${peopleAtGraphlit.size - 5} more`);
  }
}

console.log('\n---\n');

// Example 2: Find all events at a location
console.log('Example 2: Event → Place\n');

// Find a place entity
const seattle = await graphlit.queryObservables({
  search: "Seattle",
  filter: {
    types: [ObservableTypes.Place],
    states: [EntityState.Enabled]
  }
});

if (seattle.observables.results.length > 0) {
  const placeId = seattle.observables.results[0].observable.id;
  console.log(`Found place: ${seattle.observables.results[0].observable.name}\n`);
  
  // Find content mentioning both events and this place
  const contentWithPlace = await graphlit.queryContents({
    filter: {
      observations: [{
        type: ObservableTypes.Place,
        observable: { id: placeId }
      }]
    }
  });
  
  // Extract events from that content
  const eventsAtPlace = new Set<string>();
  
  contentWithPlace.contents.results.forEach(content => {
    content.observations
      ?.filter(obs => obs.type === ObservableTypes.Event)
      .forEach(obs => {
        eventsAtPlace.add(obs.observable.name);
      });
  });
  
  console.log(`Events in Seattle: ${eventsAtPlace.size}`);
  Array.from(eventsAtPlace).slice(0, 5).forEach(event => {
    console.log(`  - ${event}`);
  });
}

console.log('\n---\n');

// Example 3: Find products by organization
console.log('Example 3: Product → Organization\n');

// Find Microsoft
const microsoft = await graphlit.queryObservables({
  search: "Microsoft",
  filter: {
    types: [ObservableTypes.Organization],
    states: [EntityState.Enabled]
  }
});

if (microsoft.observables.results.length > 0) {
  const msftId = microsoft.observables.results[0].observable.id;
  console.log(`Found organization: ${microsoft.observables.results[0].observable.name}\n`);
  
  // Find content mentioning Microsoft
  const msftContent = await graphlit.queryContents({
    filter: {
      observations: [{
        type: ObservableTypes.Organization,
        observable: { id: msftId }
      }]
    }
  });
  
  // Extract products
  const msftProducts = new Map<string, number>();
  
  msftContent.contents.results.forEach(content => {
    content.observations
      ?.filter(obs => obs.type === ObservableTypes.Product)
      .forEach(obs => {
        msftProducts.set(
          obs.observable.name,
          (msftProducts.get(obs.observable.name) || 0) + 1
        );
      });
  });
  
  console.log(`Products by Microsoft: ${msftProducts.size}`);
  Array.from(msftProducts.entries())
    .sort((a, b) => b[1] - a[1])
    .slice(0, 5)
    .forEach(([product, count]) => {
      console.log(`  - ${product} (mentioned ${count} times)`);
    });
}

console.log('\n---\n');

// Example 4: Multi-hop relationship (Person → Organization → Event)
console.log('Example 4: Multi-hop Relationship (Person → Org → Event)\n');

// Find a person
const person = await graphlit.queryObservables({
  search: "Kirk Marple",
  filter: {
    types: [ObservableTypes.Person],
    states: [EntityState.Enabled]
  }
});

if (person.observables.results.length > 0) {
  const personId = person.observables.results[0].observable.id;
  console.log(`Person: ${person.observables.results[0].observable.name}\n`);
  
  // Step 1: Find organizations this person is associated with
  const personContent = await graphlit.queryContents({
    filter: {
      observations: [{
        type: ObservableTypes.Person,
        observable: { id: personId }
      }]
    }
  });
  
  const relatedOrgs = new Set<string>();
  personContent.contents.results.forEach(content => {
    content.observations
      ?.filter(obs => obs.type === ObservableTypes.Organization)
      .forEach(obs => {
        relatedOrgs.add(obs.observable.id);
      });
  });
  
  console.log(`Associated organizations: ${relatedOrgs.size}`);
  
  // Step 2: For each organization, find events
  const allEvents = new Set<string>();
  
  for (const orgId of Array.from(relatedOrgs).slice(0, 3)) {  // Limit for demo
    const orgEvents = await graphlit.queryContents({
      filter: {
        observations: [{
          type: ObservableTypes.Organization,
          observable: { id: orgId }
        }]
      }
    });
    
    orgEvents.contents.results.forEach(content => {
      content.observations
        ?.filter(obs => obs.type === ObservableTypes.Event)
        .forEach(obs => {
          allEvents.add(obs.observable.name);
        });
    });
  }
  
  console.log(`Events related to person's organizations: ${allEvents.size}`);
  Array.from(allEvents).slice(0, 5).forEach(event => {
    console.log(`  - ${event}`);
  });
}

console.log('\n✓ Relationship queries complete!');

Step-by-Step Explanation

Step 1: Understanding Relationship Types

Direct Relationships (inferred from co-occurrence):

  • Person → Organization: People work at organizations

  • Event → Place: Events happen at locations

  • Product → Organization: Companies make products

  • Person → Event: People attend/organize events

  • Software → Organization: Companies develop software

Implicit Relationships (from content context):

  • Entities mentioned together in same document

  • Entities on same page (PDF documents)

  • Entities in same conversation thread

  • Entities in same time window (audio/video)

Pattern: Entity A → Entity B

Step 3: Query Pattern 2 - Co-Occurrence Filtering

Pattern: Find content with BOTH entities

Step 4: Query Pattern 3 - Multi-Hop Relationships

Pattern: Entity A → Entity B → Entity C

Step 5: Relationship Strength (Frequency)

Pattern: Count co-occurrences to measure relationship strength


Configuration Options

Filtering by Content Type

Time-Based Relationship Queries

Confidence-Based Filtering


Variations

Variation 1: Build Network Graph

Create network visualization data:

Variation 2: Relationship Timeline

Track when relationships formed:

Variation 3: Entity Influence Score

Rank entities by relationship count:

Variation 4: Relationship Path Finding

Find shortest path between two entities:

Variation 5: Relationship Export for Analysis

Export relationship data for external analysis:


Common Issues & Solutions

Issue: Too Many API Calls for Large Graphs

Problem: Multi-hop queries make hundreds of API calls.

Solution: Batch queries and cache results:

Issue: No Relationships Found

Problem: Query returns no related entities.

Causes:

  1. Entities from different content sources

  2. Entities never co-occur

  3. Filters too restrictive

Solution: Broaden query:

Issue: Duplicate Relationships

Problem: Same relationship counted multiple times.

Solution: Deduplicate by entity ID:

Issue: Slow Multi-Hop Queries

Problem: Deep relationship traversal very slow.

Solution: Limit depth and parallelize:


Developer Hints

Relationship Query Performance

  • Direct relationships (1-hop): Fast (<1s)

  • 2-hop relationships: Moderate (1-5s)

  • 3+ hop relationships: Slow (5-30s)

  • Cache aggressively for large graphs

Relationship Types by Content

  • Emails: Strong Person-Person, Person-Organization

  • Slack: Person-Person, Organization-Product

  • Documents: All types, especially Product-Organization

  • GitHub: Person-Repo, Software-Organization

Co-Occurrence Reliability

  • Same page (PDF): Very strong relationship indicator

  • Same document: Strong relationship

  • Same thread (Slack/email): Strong relationship

  • Different documents: Weaker relationship

Graph Traversal Strategies

  • Breadth-first: Find shortest paths

  • Depth-first: Explore deep relationships

  • Limited depth: Prevent explosion (max 3 hops)

  • Type-specific: Only traverse certain entity types


Last updated

Was this helpful?