Class: FactDb::Temporal::Query

Inherits:
Object
  • Object
show all
Defined in:
lib/fact_db/temporal/query.rb

Overview

Executes temporal queries on facts with time-based filtering

Provides methods for querying facts at specific points in time, comparing states between dates, and searching with temporal constraints.

Examples:

Query current facts about an entity

query = Query.new
facts = query.current_facts(entity_id: person.id)

Compare facts at two points in time

diff = query.diff(entity_id: person.id, from_date: Date.parse("2023-01-01"), to_date: Date.today)
puts "Added: #{diff[:added].count}, Removed: #{diff[:removed].count}"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(scope = Models::Fact.all) ⇒ Query

Initializes a new Query with an optional base scope

Parameters:

  • scope (ActiveRecord::Relation) (defaults to: Models::Fact.all)

    base fact scope (defaults to all facts)



25
26
27
# File 'lib/fact_db/temporal/query.rb', line 25

def initialize(scope = Models::Fact.all)
  @scope = scope
end

Instance Attribute Details

#scopeActiveRecord::Relation (readonly)

Returns the base scope for queries.

Returns:

  • (ActiveRecord::Relation)

    the base scope for queries



20
21
22
# File 'lib/fact_db/temporal/query.rb', line 20

def scope
  @scope
end

Instance Method Details

#current_facts(entity_id:) ⇒ ActiveRecord::Relation

Returns currently valid canonical facts about an entity

Parameters:

  • entity_id (Integer)

    the entity to query

Returns:

  • (ActiveRecord::Relation)

    currently valid facts mentioning the entity



65
66
67
# File 'lib/fact_db/temporal/query.rb', line 65

def current_facts(entity_id:)
  execute(entity_id: entity_id, at: nil, status: :canonical)
end

#diff(entity_id:, from_date:, to_date:) ⇒ Hash

Compares facts at two points in time to find changes

Examples:

diff = query.diff(entity_id: 1, from_date: 1.year.ago, to_date: Date.today)
puts "#{diff[:added].count} new facts, #{diff[:removed].count} removed"

Parameters:

  • entity_id (Integer)

    the entity to compare

  • from_date (Date, Time)

    the earlier point in time

  • to_date (Date, Time)

    the later point in time

Returns:

  • (Hash)

    hash with :added, :removed, and :unchanged arrays of facts



140
141
142
143
144
145
146
147
148
149
# File 'lib/fact_db/temporal/query.rb', line 140

def diff(entity_id:, from_date:, to_date:)
  facts_at_from = facts_at(from_date, entity_id: entity_id).to_a
  facts_at_to = facts_at(to_date, entity_id: entity_id).to_a

  {
    added: facts_at_to - facts_at_from,
    removed: facts_at_from - facts_at_to,
    unchanged: facts_at_from & facts_at_to
  }
end

#execute(topic: nil, at: nil, entity_id: nil, status: :canonical, limit: nil) ⇒ ActiveRecord::Relation

Executes a temporal query with multiple filters

Parameters:

  • topic (String, nil) (defaults to: nil)

    text to search for in fact content

  • at (Date, Time, nil) (defaults to: nil)

    point in time (nil for currently valid)

  • entity_id (Integer, nil) (defaults to: nil)

    filter by entity

  • status (Symbol) (defaults to: :canonical)

    fact status filter (:canonical, :superseded, :synthesized, :all)

  • limit (Integer, nil) (defaults to: nil)

    maximum number of results

Returns:

  • (ActiveRecord::Relation)

    matching facts ordered by valid_at desc



37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/fact_db/temporal/query.rb', line 37

def execute(topic: nil, at: nil, entity_id: nil, status: :canonical, limit: nil)
  result = @scope

  # Status filtering
  result = apply_status_filter(result, status)

  # Temporal filtering
  result = apply_temporal_filter(result, at)

  # Entity filtering
  result = apply_entity_filter(result, entity_id)

  # Topic search
  result = apply_topic_search(result, topic)

  # Ordering - most recently valid first
  result = result.order(valid_at: :desc)

  # Limit results
  result = result.limit(limit) if limit

  result
end

#facts_at(date, entity_id: nil) ⇒ ActiveRecord::Relation

Returns facts valid at a specific point in time

Parameters:

  • date (Date, Time)

    the point in time to query

  • entity_id (Integer, nil) (defaults to: nil)

    optional entity filter

Returns:

  • (ActiveRecord::Relation)

    facts valid at the given date



74
75
76
# File 'lib/fact_db/temporal/query.rb', line 74

def facts_at(date, entity_id: nil)
  execute(at: date, entity_id: entity_id, status: :canonical)
end

#facts_created_between(from:, to:, entity_id: nil) ⇒ ActiveRecord::Relation

Returns facts that became valid within a date range

Parameters:

  • from (Date, Time)

    start of range (inclusive)

  • to (Date, Time)

    end of range (inclusive)

  • entity_id (Integer, nil) (defaults to: nil)

    optional entity filter

Returns:

  • (ActiveRecord::Relation)

    facts created in the range, ordered by valid_at asc



84
85
86
87
88
# File 'lib/fact_db/temporal/query.rb', line 84

def facts_created_between(from:, to:, entity_id: nil)
  result = @scope.canonical.became_valid_between(from, to)
  result = result.mentioning_entity(entity_id) if entity_id
  result.order(valid_at: :asc)
end

#facts_invalidated_between(from:, to:, entity_id: nil) ⇒ ActiveRecord::Relation

Returns facts that became invalid within a date range

Parameters:

  • from (Date, Time)

    start of range (inclusive)

  • to (Date, Time)

    end of range (inclusive)

  • entity_id (Integer, nil) (defaults to: nil)

    optional entity filter

Returns:

  • (ActiveRecord::Relation)

    facts invalidated in the range, ordered by invalid_at asc



96
97
98
99
100
# File 'lib/fact_db/temporal/query.rb', line 96

def facts_invalidated_between(from:, to:, entity_id: nil)
  result = @scope.became_invalid_between(from, to)
  result = result.mentioning_entity(entity_id) if entity_id
  result.order(invalid_at: :asc)
end

#facts_with_entity_role(entity_id:, role:, at: nil) ⇒ ActiveRecord::Relation

Returns facts where an entity has a specific mention role

Parameters:

  • entity_id (Integer)

    the entity to query

  • role (String, Symbol)

    the mention role (e.g., :subject, :object)

  • at (Date, Time, nil) (defaults to: nil)

    point in time (nil for currently valid)

Returns:

  • (ActiveRecord::Relation)

    facts with the entity in the specified role



124
125
126
127
128
# File 'lib/fact_db/temporal/query.rb', line 124

def facts_with_entity_role(entity_id:, role:, at: nil)
  result = @scope.canonical.with_role(entity_id, role)
  result = apply_temporal_filter(result, at)
  result.order(valid_at: :desc)
end

#semantic_search(query:, at: nil, entity_id: nil, limit: 20) ⇒ ActiveRecord::Relation

Searches facts by text with temporal filtering

Uses PostgreSQL full-text search with optional point-in-time filtering.

Parameters:

  • query (String)

    text to search for

  • at (Date, Time, nil) (defaults to: nil)

    point in time (nil for currently valid)

  • entity_id (Integer, nil) (defaults to: nil)

    optional entity filter

  • limit (Integer) (defaults to: 20)

    maximum number of results (default: 20)

Returns:

  • (ActiveRecord::Relation)

    matching facts



111
112
113
114
115
116
# File 'lib/fact_db/temporal/query.rb', line 111

def semantic_search(query:, at: nil, entity_id: nil, limit: 20)
  result = @scope.canonical.search_text(query)
  result = apply_temporal_filter(result, at)
  result = result.mentioning_entity(entity_id) if entity_id
  result.limit(limit)
end