Class: OpenObserve::Search

Inherits:
Resource show all
Defined in:
lib/openobserve/search.rb

Overview

Log search and exploration.

The raw API makes a search verbose out of proportion to the question asked: the SQL has to be wrapped in a SearchQuery inside a SearchSQLRequest, the window expressed in microseconds, the organization repeated, and the rows dug out of a hits envelope. This domain reduces all of that to "SQL plus a window".

Why this domain keeps using the typed sub-client

Every other domain issues raw connection.call(..., type: nil) requests, because the generated sub-clients pin a model that both breaks Record#[] and drops undeclared fields (see CLAUDE.md, "Hard rule"). Search is the documented exception: the socle already passes type: nil for _search, _around and _values, so no model is ever applied and the payload arrives intact. Going raw here would buy nothing and would mean hand-serializing the SearchSQLRequest/SearchQuery body.

This holds only as long as those three operations stay untyped upstream. Nothing checks it statically — the e2e canary is what would catch a regression.

Examples:

oo.search.all(sql: "SELECT * FROM canary WHERE level = 'error'", last: 3600)

Constant Summary collapse

DEFAULT_PAGE_SIZE =

Default page size for #each / #all.

100

Constants inherited from Resource

Resource::AUTH

Instance Method Summary collapse

Methods inherited from Resource

#initialize

Constructor Details

This class inherits a constructor from OpenObserve::Resource

Instance Method Details

#allArray<OpenObserve::Hit>

Returns every matching row, materialised.

Returns:



64
65
66
# File 'lib/openobserve/search.rb', line 64

def all(...)
  each(...).to_a
end

#around(stream:, key:, size: 10, type: nil) ⇒ OpenObserve::SearchResult

The rows surrounding a given record — the "show me the context" view.

Parameters:

  • stream (String)

    stream name

  • key (String)

    the record's _timestamp

  • size (Integer) (defaults to: 10)

    rows on each side

Returns:



74
75
76
77
78
79
80
# File 'lib/openobserve/search.rb', line 74

def around(stream:, key:, size: 10, type: nil)
  envelope = call do
    api._around(org_id: organization, stream_name: stream, key: key.to_s, size: size, type: type).data
  end

  SearchResult.from(envelope)
end

#each(sql:, from: nil, to: nil, last: nil, page_size: DEFAULT_PAGE_SIZE, now: Time.now, &block) ⇒ Enumerator<OpenObserve::Hit>

Auto-paginating iteration over every matching row.

Parameters:

  • page_size (Integer) (defaults to: DEFAULT_PAGE_SIZE)

    rows per request

Returns:



55
56
57
58
59
60
61
# File 'lib/openobserve/search.rb', line 55

def each(sql:, from: nil, to: nil, last: nil, page_size: DEFAULT_PAGE_SIZE, now: Time.now, &block)
  enum = paginate(size: page_size) do |offset, size|
    run(sql: sql, from: from, to: to, last: last, size: size, offset: offset, now: now).hits
  end

  block ? enum.each(&block) : enum
end

#run(sql:, from: nil, to: nil, last: nil, size: DEFAULT_PAGE_SIZE, offset: 0, now: Time.now) ⇒ OpenObserve::SearchResult

Run one search.

Parameters:

  • sql (String)

    the SQL to run

  • from (Time, Date, String, Numeric, nil) (defaults to: nil)

    window lower bound

  • to (Time, Date, String, Numeric, nil) (defaults to: nil)

    window upper bound; defaults to now

  • last (Numeric, nil) (defaults to: nil)

    trailing window in seconds, e.g. 3600

  • size (Integer) (defaults to: DEFAULT_PAGE_SIZE)

    maximum rows

  • offset (Integer) (defaults to: 0)

    rows to skip (from in the raw API — renamed here because from: already means the window's lower bound)

  • now (Time) (defaults to: Time.now)

    clock reference, injectable for tests

Returns:

Raises:



41
42
43
44
45
46
47
48
49
# File 'lib/openobserve/search.rb', line 41

def run(sql:, from: nil, to: nil, last: nil, size: DEFAULT_PAGE_SIZE, offset: 0, now: Time.now)
  start_time, end_time = Coerce.window(from: from, to: to, last: last, now: now)

  envelope = call do
    api._search(org_id: organization, search_sql_request: request(sql, start_time, end_time, size, offset)).data
  end

  SearchResult.from(envelope)
end

#values(stream:, fields:, from: nil, to: nil, last: nil, size: 10, now: Time.now) ⇒ OpenObserve::SearchResult

Distinct values taken by one or more fields over a window — what populates a filter dropdown without scanning the rows yourself.

Parameters:

  • fields (String, Array<String>)

    field name(s)

Returns:



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/openobserve/search.rb', line 87

def values(stream:, fields:, from: nil, to: nil, last: nil, size: 10, now: Time.now)
  start_time, end_time = Coerce.window(from: from, to: to, last: last, now: now)

  envelope = call do
    api._values(
      org_id:      organization,
      stream_name: stream,
      fields:      Array(fields).join(','),
      size:        size,
      from:        0,
      start_time:  start_time,
      end_time:    end_time
    ).data
  end

  SearchResult.from(envelope)
end