Class: Diametric::Query
- Inherits:
-
Object
- Object
- Diametric::Query
- Includes:
- Enumerable
- Defined in:
- lib/diametric/query.rb
Overview
Query
objects are used to generate Datomic queries, whether to send via an external client or via the persistence API. The two methods used to generate a query are .where
and .filter
, both of which are chainable. To get the query data and arguments for a Query
, use the data
method.
If you are using a persistence API, you can ask Query
to get the results of a Datomic query. Diametric::Query
is an Enumerable
. To get the results of a query, use Enumerable
methods such as .each
or .first
. Query
also provides a .all
method to run the query and get the results.
Instance Attribute Summary collapse
-
#conditions ⇒ Object
readonly
Returns the value of attribute conditions.
-
#connection ⇒ Object
readonly
Returns the value of attribute connection.
-
#filter_attrs ⇒ Object
readonly
Returns the value of attribute filter_attrs.
-
#filter_values ⇒ Object
readonly
Returns the value of attribute filter_values.
-
#filters ⇒ Object
readonly
Returns the value of attribute filters.
-
#model ⇒ Object
readonly
Returns the value of attribute model.
-
#resolve ⇒ Object
readonly
Returns the value of attribute resolve.
Instance Method Summary collapse
-
#all(conn_or_db = @conn_or_db) ⇒ Array<Entity>
Return all query results.
-
#data ⇒ Array(Array, Array)
Create a Datomic query from the conditions and filters passed to this
Query
object. -
#each {|Entity| ... } ⇒ Object
Loop through the query results.
-
#filter(*filter) ⇒ Query
Add a filter to your Datomic query.
-
#initialize(model, connection_or_database = nil, resolve = false) ⇒ Query
constructor
Create a new Datomic query.
- #peer_data ⇒ Object
- #peer_filter(*filter) ⇒ Object
-
#where(conditions) ⇒ Query
Add conditions to your Datomic query.
Constructor Details
#initialize(model, connection_or_database = nil, resolve = false) ⇒ Query
Create a new Datomic query.
25 26 27 28 29 30 31 32 33 |
# File 'lib/diametric/query.rb', line 25 def initialize(model, connection_or_database=nil, resolve=false) @model = model @conditions = {} @filters = [] @filter_attrs = [] @filter_values = [] @conn_or_db = connection_or_database @resolve = resolve end |
Instance Attribute Details
#conditions ⇒ Object
Returns the value of attribute conditions.
19 20 21 |
# File 'lib/diametric/query.rb', line 19 def conditions @conditions end |
#connection ⇒ Object (readonly)
Returns the value of attribute connection.
19 20 21 |
# File 'lib/diametric/query.rb', line 19 def connection @connection end |
#filter_attrs ⇒ Object
Returns the value of attribute filter_attrs.
19 20 21 |
# File 'lib/diametric/query.rb', line 19 def filter_attrs @filter_attrs end |
#filter_values ⇒ Object
Returns the value of attribute filter_values.
19 20 21 |
# File 'lib/diametric/query.rb', line 19 def filter_values @filter_values end |
#filters ⇒ Object
Returns the value of attribute filters.
19 20 21 |
# File 'lib/diametric/query.rb', line 19 def filters @filters end |
#model ⇒ Object (readonly)
Returns the value of attribute model.
19 20 21 |
# File 'lib/diametric/query.rb', line 19 def model @model end |
#resolve ⇒ Object (readonly)
Returns the value of attribute resolve.
19 20 21 |
# File 'lib/diametric/query.rb', line 19 def resolve @resolve end |
Instance Method Details
#all(conn_or_db = @conn_or_db) ⇒ Array<Entity>
Return all query results.
135 136 137 138 139 140 141 |
# File 'lib/diametric/query.rb', line 135 def all(conn_or_db=@conn_or_db) if self.model.instance_variable_get("@peer") && !@resolve model.q(*data, conn_or_db) else map { |x| x } end end |
#data ⇒ Array(Array, Array)
Create a Datomic query from the conditions and filters passed to this Query
object.
149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/diametric/query.rb', line 149 def data return peer_data if self.model.instance_variable_get("@peer") vars = model.attribute_names.map { |attribute| ~"?#{attribute}" } from = conditions.map { |k, _| ~"?#{k}" } clauses = model.attribute_names.map { |attribute| [~"?e", model.namespace(model.prefix, attribute), ~"?#{attribute}"] } clauses += filters args = conditions.map { |_, v| v } query = [ :find, ~"?e", *vars, :in, ~"\$", *from, :where, *clauses ] [query, args] end |
#each {|Entity| ... } ⇒ Object
Loop through the query results. In order to use each
, your model must include a persistence API. At a minimum, it must have a .q
method that returns an Enumerable
object.
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/diametric/query.rb', line 114 def each # TODO check to see if the model has a `.q` method and give # an appropriate error if not. res = model.q(*data, @conn_or_db) collapse_results(res).each do |entity| if @resolve yield model.reify(entity.first, @conn_or_db, @resolve) elsif self.model.instance_variable_get("@peer") yield entity # The map is for compatibility with Java peer persistence. # TODO remove if possible else yield model.from_query(entity.map { |x| x }, @conn_or_db, @resolve) end end end |
#filter(*filter) ⇒ Query
Add a filter to your Datomic query. Filters are known as expression clause predicates in the Datomic query documentation.
A filter can be in one of two forms. In the first, you pass a series of arguments. Any Ruby symbol given in this form will be converted to a EDN symbol. If the symbol is the same as one of the queried model’s attributes or as a key passed to where
, it will be prefixed with a ? so that it becomes a Datalog variable. In the second form, you pass EDN representing a Datomic predicate to filter
. No conversion is done on this filter and it must be an EDN list.
76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/diametric/query.rb', line 76 def filter(*filter) return peer_filter(*filter) if self.model.instance_variable_get("@peer") query = self.dup if filter.first.is_a?(EDN::Type::List) filter = filter.first else filter = filter.map { |e| convert_filter_element(e) } filter = EDN::Type::List.new(*filter) end query.filters += [[filter]] query end |
#peer_data ⇒ Object
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/diametric/query.rb', line 172 def peer_data if conditions.empty? && filters.empty? args = [model.prefix] query = <<-EOQ [:find ?e :in $ [?include-ns ...] :where [?e ?aid ?v] [?aid :db/ident ?a] [(namespace ?a) ?ns] [(= ?ns ?include-ns)]] EOQ else from = conditions.map do |k, v| if v.kind_of? Array [~"?#{k}", Diametric::Persistence::Utils.read_string("...")] else ~"?#{k}" end end keys = conditions.keys unless filter_attrs.empty? from += filter_attrs.inject([]) { |memo, key| memo << ~"?#{key}value"; memo } keys += filter_attrs end keys.uniq! clauses = keys.map { |attribute| [~"?e", model.namespace(model.prefix, attribute), ~"?#{attribute}"] } clauses += filters args = conditions.map { |_, v| v } args += filter_values query = [ :find, ~"?e", :in, ~"\$", from.flatten(1), :where, *clauses ] end [query, args] end |
#peer_filter(*filter) ⇒ Object
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/diametric/query.rb', line 90 def peer_filter(*filter) query = self.dup query.filter_attrs += (self.model.attribute_names & filter) filter = filter.map do |e| if e.is_a? Symbol convert_filter_element(e) elsif e.is_a? String e else query.filter_values << e ~"?#{query.filter_attrs.last.to_s}value" end end filter = EDN::Type::List.new(*filter) query.filters << [Diametric::Persistence::Utils.read_string(filter.to_edn)] query end |
#where(conditions) ⇒ Query
Add conditions to your Datomic query. Conditions check for equality against entity attributes. In addition, you can add conditions for use as variables in filters.
44 45 46 47 48 |
# File 'lib/diametric/query.rb', line 44 def where(conditions) query = self.dup query.conditions = query.conditions.merge(conditions) query end |