Skip to content
Scott Williams edited this page Jun 13, 2014 · 2 revisions

How Querying Works

The Query Builder let's you build robust queries to send to the server. Let's dive in to the internals to see what's going on.

class Widget
  include Arkenstone::Document
  include Arkenstone::Queryable
  
  url 'http://example.com/widgets'
  
  attributes :name, :size, :sku
end

Arkenstone::Queryable

This module is kept in lib/arkenstone/querybale.rb. When it is included the included method is called in class << self. This maps the Arkenstone::Queryable::ClassMethods of the module onto the class that included it, Widget in this case.

This allows you to perform a Widget.where() call and provide a search query.

search_results = Widget.where do
  name: 'ABC'
end

search_results = Widget.where({name: 'ABC'})

search_results = Widget.where('{"name": "ABC"}')

where(query = nil, &block)

where takes an optional query and a block.

First, check_for_url is called to make sure that a url has been declared for the class. Our example uses http://example.com/widgets.

Next build_where_query creates the actual query body.

build_where_body(query = nil, &block)

build_where_query builds a JSON string and stores it in the body variable. body is ultimately returned back to where.

If the query is provided, where prefers to use that. If neither is provided, nil is returned.

If the query is a string, it is simply copied to the body.

If the query is a hash, it is converted to JSON then copied to the body. If the query is not a hash or a string, and no block is provided, nil is returned.

If there is no query and a block is provided. a new Arkenstone::QueryBuilder is created and the block is passed to that.

Arkenstone::QueryBuilder

The QueryBuilder is in lib/arkenstone/query_builder.rb. Its function is to create a JSON query string. It does this by building up a hash and then converting it to JSON.

build(&block)

build takes a block, then evaluates it. Each method in the block is evaluating within the context of the querybuilder. So a _gt function in the block calls the _gt method in the builder.

DSL Mapping

Each of the simple evaluators returns a hash with the appropriate json commands to be sent to a server. These include _in, _gt, _gte, _lt, and _lte.

Complex queries (Boolean, _limit, _include) make use of a @cache instance variable. This itself is a separate hash. Each type of query has its own bucket in the @cache. This means that you can't have two AND queries that aren't nested:

  _and(
    { name: 'ABC'},
    { sku: '1000' }
  )
  _and(
    { name: 'DEF'},
    { sku: '55' }
  )

Logically this doesn't make sense either, and could just be combined in a single AND query. If this query is submitted, the second AND would overwrite the first.

Once the block has been evaluated, the cache is flushed (if it exists) and reset and its value is merged with the results of the simple queries. This is then converted to JSON and returned.

build_where_body (continued)

The result from the QueryBuilder is bubbled back up to where.

where(continued)

If the body is nil (for any reason) the method short-ciruits and just returns nil. Otherwise, the body is sent to send_request as a POST. See the network documentation for more information.

If the response was successful, the response body is parsed and converted into a list of objects.

Clone this wiki locally