Ruby Developer Guide
Other documentationsDemoCommunityGitHub
  • Forest Admin
  • Getting started
    • How it works
    • Quick start
      • Ruby on Rails
    • Create your agent
    • Troubleshooting
    • Migrating legacy agents
      • Pre-requisites
      • Recommendations
      • Migration steps
      • Code transformations
        • API Charts
        • Live Queries
        • Smart Charts
        • Route overrides
        • Smart Actions
        • Smart Fields
        • Smart Relationships
        • Smart Segments
  • Data Sources
    • Getting Started
      • Collection selection
      • Naming conflicts
      • Query interface and Native Queries
        • Fields and projections
        • Filters
        • Aggregations
    • Provided data sources
      • ActiveRecord
        • Polymorphic relationships
      • Mongoid
    • Write your own
      • Translation strategy
        • Structure declaration
        • Capabilities declaration
        • Read implementation
        • Write implementation
        • Intra-data source Relationships
      • Contribute
  • Agent customization
    • Getting Started
    • Actions
      • Scope and context
      • Result builder
      • Static Forms
      • Widgets in Forms
      • Dynamic Forms
      • Form layout customization
      • Related data invalidation
    • Charts
      • Value
      • Objective
      • Percentage
      • Distribution
      • Leaderboard
      • Time-based
    • Fields
      • Add fields
      • Move, rename and remove fields
      • Override binary field mode
      • Override writing behavior
      • Override filtering behavior
      • Override sorting behavior
      • Validation
    • Hooks
      • Collection hook
      • Collection override
    • Pagination
    • Plugins
      • Write your own
    • Relationships
      • To a single record
      • To multiple records
      • Computed foreign keys
      • Under the hood
    • Search
    • Segments
  • Frontend customization
    • Smart Charts
      • Create a table chart
      • Create a bar chart
      • Create a cohort chart
      • Create a density map
    • Smart Views
      • Create a Map view
      • Create a Calendar view
      • Create a Shipping view
      • Create a Gallery view
      • Create a custom tinder-like validation view
      • Create a custom moderation view
  • Deploying to production
    • Environments
      • Deploy on AWS
      • Deploy on Heroku
      • Deploy on GCP
      • Deploy on Ubuntu
    • Development workflow
    • Using branches
    • Deploying your changes
    • Forest Admin CLI commands
      • init
      • login
      • branch
      • switch
      • set-origin
      • push
      • environments:create
      • environments:reset
      • deploy
  • Under the hood
    • .forestadmin-schema.json
    • Data Model
      • Typing
      • Relationships
    • Security & Privacy
Powered by GitBook
On this page
  • Choosing how to query your data
  • In practice
  • Querying with the native driver
  • Querying with the Forest Admin Query Interface

Was this helpful?

  1. Data Sources
  2. Getting Started

Query interface and Native Queries

PreviousNaming conflictsNextFields and projections

Last updated 12 months ago

Was this helpful?

This is the official documentation of the agent_ruby Ruby agent.

Forest Admin can connect to any data source, as long as it can be represented as a collection of records that have a common structure.

To achieve that, Forest Admin needs to abstract away data source differences: each connector "speaks" the language of a given API on one side and exposes the Forest Admin Query Interface on the other.

This interface is called the Forest Admin Query Interface, it is not a full-featured ORM: its objective is to be "just enough" to fuel Forest Admin.

Choosing how to query your data

The Forest Admin Query Interface is used to implement all native features of the admin panel, however, when writing custom code (, , ...), you can either access your data using the Forest Admin Query Interface or using the native driver.

The choice is yours, and you will probably use both depending on the situation.

-
Forest Admin Query Interface
Native Driver

Code consistency

👍 Use the same query interface for all data sources

👎 Different API for each database / SaaS

Customizations can be queried (computed field, relationships, ...)

👍 Yes

👎 No

Features

👎 Common denominator is exposed

👍 All features of the underlying API

In-app deployments

👎 Difficult to reuse your existing code

👍 Re-use your existing code

Learning curve

👎 The interface is Forest Admin specific

👍 You already know how to write SQL

Native support for filters from the UI

👍 Yes

👎 No

Total

3 x 👍 + 3 x 👎

3 x 👍 + 3 x 👎

In practice

Querying with the native driver

As the name implies, native drivers have different interfaces for each data source.

module ForestAdminRails
  class CreateAgent
    def self.customize
      @create_agent.customize_collection('customer') do |collection|
        collection.add_segment('highPrice') do |context|
          rows = ActiveRecord::Base.connection.execute(
            'SELECT p.id as product_id, COUNT(o.id) as nb
             FROM orders o
             INNER JOIN products p ON o.product_id = p.id
             GROUP BY p.id
             ORDER BY nb DESC
             LIMIT 10;'
          )

          {
            field: 'id',
            operator: 'In',
            value: rows.map { |r| r['product_id'] }
          }
        end
      end
    end
  end
end

Querying with the Forest Admin Query Interface

Queries can be executed directly, by calling the methods exposed by context.dataSource and context.collection.

include ForestAdminDatasourceToolkit::Components::Query
include ForestAdminDatasourceToolkit::Components::Query::ConditionTree

module ForestAdminRails
  class CreateAgent
    def self.customize
      @create_agent.customize_collection('order') do |collection|
        collection.add_segment('mySegment') do |context|
          rows = context.datasource.get_collection('order').aggregate(
            Filter.new,
            Aggregation.new(operation: 'Count', groups: [{ field: 'product_id' }]),
            10
          )

          {
            field: 'id',
            operator: Operators::IN,
            value: rows.map { |r| r['product_id'] }
          }
        end
      end
    end
  end
end
Data Source Interface
class DatasourceContract
    def collections
      raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
    end

    def charts
      raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
    end

    def get_collection(name)
      raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
    end

    def add_collection(collection)
      raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
    end

    def render_chart(caller, name)
      raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
    end
end
Collection Interface

Parameters are explained in depth on the following pages:

class CollectionContract
    def datasource
      raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
    end

    def schema
      raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
    end

    def name
      raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
    end

    def execute(caller, name, data, filter = nil)
      raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
    end

    def get_form(caller, name, data = nil, filter = nil, metas = {})
      raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
    end

    def create(caller, data)
      raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
    end

    def list(caller, filter, projection)
      raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
    end

    def update(caller, filter, data)
      raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
    end

    def delete(caller, filter)
      raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
    end

    def aggregate(caller, filter, aggregation, limit = nil)
      raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
    end

    def render_chart
      raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
    end
end

creating new actions
fields
Fields and projections
Filters
Aggregations