querybuilder 0.9.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,8 @@
1
+ == 0.9.1 2010-09-22
2
+
3
+ * 1 Major enhancement
4
+ * Key based add_table (used to import key/value based fields).
5
+
1
6
  == 0.9.0 2010-09-14
2
7
 
3
8
  * 1 Major enhancements
@@ -1,3 +1,3 @@
1
1
  module QueryBuilder
2
- VERSION = '0.9.0'
2
+ VERSION = '0.9.1'
3
3
  end
@@ -138,7 +138,8 @@ module QueryBuilder
138
138
  def initialize(source, opts = {})
139
139
  @default = opts.delete(:default) || {}
140
140
  @opts = opts
141
- @rubyless_helper = @opts[:rubyless_helper]
141
+ @rubyless_helper = @opts[:rubyless_helper]
142
+
142
143
  if source.kind_of?(Processor)
143
144
  # experimental: class change
144
145
  @context = source.context
@@ -739,6 +740,7 @@ module QueryBuilder
739
740
  end
740
741
  end
741
742
 
743
+ # Add a new table and apply scoping when needed
742
744
  def add_table(use_name, table_name = nil)
743
745
  if use_name == main_table && first?
744
746
  # we are now using final table
@@ -773,6 +775,12 @@ module QueryBuilder
773
775
  end
774
776
  end
775
777
 
778
+ # Add a table to 'import' a key/value based field. This method ensures that
779
+ # a given field is only included once for each context.
780
+ def add_key_value_table(use_name, index_table, key = :any, &block)
781
+ @query.add_key_value_table(use_name, index_table, key, &block)
782
+ end
783
+
776
784
  # Hook to use dummy scopes (such as 'in site')
777
785
  def need_join_scope?(scope_name)
778
786
  true
@@ -4,7 +4,7 @@ module QueryBuilder
4
4
  class Query
5
5
  attr_accessor :processor_class, :distinct, :select, :tables, :table_alias, :where,
6
6
  :limit, :offset, :page_size, :order, :group, :error, :attributes_alias,
7
- :pagination_key, :main_class, :context
7
+ :pagination_key, :main_class, :context, :key_value_tables
8
8
 
9
9
  class << self
10
10
  def adapter
@@ -19,6 +19,7 @@ module QueryBuilder
19
19
  @join_tables = {}
20
20
  @needed_join_tables = {}
21
21
  @attributes_alias = {}
22
+ @key_value_tables = {}
22
23
  @where = []
23
24
  end
24
25
 
@@ -119,6 +120,23 @@ module QueryBuilder
119
120
  add_alias_to_tables(table_name || use_name, alias_name)
120
121
  end
121
122
 
123
+ # Add a table to 'import' a key/value based field. This method ensures that
124
+ # a given field is only included once for each context.
125
+ def add_key_value_table(use_name, index_table, key, &block)
126
+ key_tables = (@key_value_tables[table] ||= {})
127
+ key_table = (key_tables[use_name] ||= {})
128
+ if alias_table = key_table[key]
129
+ # done, the index_table has been used for the given key in the current context
130
+ else
131
+ # insert the new table
132
+ add_table(use_name, index_table, false)
133
+ alias_table = key_table[key] = table(use_name)
134
+ # Let caller configure the filter (join).
135
+ block.call(alias_table)
136
+ end
137
+ alias_table
138
+ end
139
+
122
140
  def add_select(clause)
123
141
  @select ||= []
124
142
  @select << clause
@@ -130,7 +148,8 @@ module QueryBuilder
130
148
  end
131
149
 
132
150
  # Use this method to add a join to another table (added only once for each join name).
133
- # versions LEFT JOIN dyn_attributes ON ...
151
+ # nodes JOIN idx_nodes_string AS id1 ON ...
152
+ # FIXME: can we remove this ? It seems buggy (JOIN in or clauses)
134
153
  def needs_join_table(table_name1, type, table_name2, clause, join_name = nil)
135
154
  join_name ||= "#{table_name1}=#{type}=#{table_name2}"
136
155
  @needed_join_tables[join_name] ||= {}
@@ -149,10 +168,10 @@ module QueryBuilder
149
168
  end
150
169
  end
151
170
 
152
- # Duplicate query, avoiding mutual
171
+ # Duplicate query, avoiding sharing some arrays and hash
153
172
  def dup
154
173
  other = super
155
- %w{tables table_alias where}.each do |k|
174
+ %w{tables table_alias where tables key_value_tables}.each do |k|
156
175
  other.send("#{k}=", other.send(k).dup)
157
176
  end
158
177
  other
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{querybuilder}
8
- s.version = "0.9.0"
8
+ s.version = "0.9.1"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Gaspard Bucher"]
12
- s.date = %q{2010-09-14}
12
+ s.date = %q{2010-09-22}
13
13
  s.description = %q{QueryBuilder is an interpreter for the "pseudo sql" language. This language
14
14
  can be used for two purposes:
15
15
 
@@ -27,23 +27,27 @@ class DummyProcessor < QueryBuilder::Processor
27
27
  end
28
28
 
29
29
  # Overwrite this and take care to check for valid fields.
30
- def process_field(fld_name)
31
- if ['id', 'parent_id', 'project_id', 'section_id', 'kpath', 'name', 'event_at', 'custom_a'].include?(fld_name)
32
- "#{table}.#{fld_name}"
33
- elsif fld_name == 'REF_DATE'
30
+ def process_field(field_name)
31
+ if ['id', 'parent_id', 'project_id', 'section_id', 'kpath', 'name', 'event_at', 'custom_a'].include?(field_name)
32
+ "#{table}.#{field_name}"
33
+ elsif field_name == 'REF_DATE'
34
34
  context[:ref_date] ? insert_bind(context[:ref_date]) : 'now()'
35
- elsif fld_name == 'index'
36
- add_table('index')
37
- add_filter "#{table('index')}.node_id = #{field_or_attr('id', table(self.class.main_table))}"
38
- "#{table('index')}.value"
35
+ elsif %w{age size}.include?(field_name)
36
+ tbl = add_key_value_table('idx', 'idx_nodes', field_name) do |tbl_name|
37
+ # This block is only executed once
38
+ add_filter "#{tbl_name}.node_id = #{table}.id"
39
+ add_filter "#{tbl_name}.key = #{quote(field_name)}"
40
+ end
41
+
42
+ "#{tbl}.value"
39
43
  else
40
44
  super # raises an error
41
45
  end
42
46
  end
43
47
 
44
- def resolve_missing_table(query, table_alias, table_name)
48
+ def resolve_missing_table(query, table_name, table_alias)
45
49
  case table_name
46
- when 'index'
50
+ when 'idx_nodes'
47
51
  query.where.insert 0, "#{table_alias}.id = 0"
48
52
  when 'links'
49
53
  query.where.insert 0, "#{table_alias}.id = 0"
@@ -59,4 +59,10 @@ it_should_paginate_the_entries:
59
59
  context:
60
60
  custom_query_group: test
61
61
  src: "abc limit 4 paginate foo"
62
- res: "[%Q{SELECT a,34 AS number,c FROM test WHERE 3 AND 2 AND 1 ORDER BY a ASC LIMIT 4 OFFSET ?}, ((foo.to_i > 0 ? foo.to_i : 1)-1)*4]"
62
+ res: "[%Q{SELECT a,34 AS number,c FROM test WHERE 3 AND 2 AND 1 ORDER BY a ASC LIMIT 4 OFFSET ?}, ((foo.to_i > 0 ? foo.to_i : 1)-1)*4]"
63
+
64
+ it_should_use_alias_in_filter:
65
+ context:
66
+ custom_query_group: test
67
+ src: "abc where number > 10"
68
+ res: "%Q{SELECT a,34 AS number,c FROM test WHERE (34) > 10 AND 3 AND 2 AND 1 ORDER BY a ASC}"
@@ -86,12 +86,33 @@ equation_and_or_par:
86
86
  res: "[%Q{SELECT objects.* FROM objects WHERE (objects.event_at > '2006-04-01' OR objects.name LIKE 'foo%') AND objects.parent_id = ? GROUP BY id}, id]"
87
87
 
88
88
  or_with_same_tables:
89
- src: "objects where index = 5 or index = 7"
90
- res: "[%Q{SELECT objects.* FROM index,objects WHERE ((index.value = 5 AND index.node_id = objects.id) OR (index.value = 7 AND index.node_id = objects.id)) AND objects.parent_id = ? GROUP BY objects.id}, id]"
89
+ src: "objects where age = 5 or age = 7"
90
+ res: "[%Q{SELECT objects.* FROM idx_nodes AS id1,objects WHERE ((id1.value = 5 AND id1.key = 'age' AND id1.node_id = objects.id) OR (id1.value = 7 AND id1.key = 'age' AND id1.node_id = objects.id)) AND objects.parent_id = ? GROUP BY objects.id}, id]"
91
91
 
92
92
  or_with_missing_table:
93
- src: "objects where index = 5 or 7"
94
- res: "[%Q{SELECT objects.* FROM index,objects WHERE ((index.value = 5 AND index.node_id = objects.id) OR (7 AND index.id = 0)) AND objects.parent_id = ? GROUP BY objects.id}, id]"
93
+ src: "objects where age = 5 or 7"
94
+ res: "[%Q{SELECT objects.* FROM idx_nodes AS id1,objects WHERE ((id1.value = 5 AND id1.key = 'age' AND id1.node_id = objects.id) OR (7 AND id1.id = 0)) AND objects.parent_id = ? GROUP BY objects.id}, id]"
95
+
96
+ or_different_table:
97
+ src: "objects where age = 5 or size = 15"
98
+ res: "[%Q{SELECT objects.* FROM idx_nodes AS id1,objects WHERE ((id1.value = 5 AND id1.key = 'age' AND id1.node_id = objects.id) OR (id1.value = 15 AND id1.key = 'size' AND id1.node_id = objects.id)) AND objects.parent_id = ? GROUP BY objects.id}, id]"
99
+
100
+ and_with_same_tables:
101
+ src: "objects where age > 5 and age < 7"
102
+ res: "[%Q{SELECT objects.* FROM idx_nodes AS id1,objects WHERE id1.value > 5 AND id1.value < 7 AND id1.key = 'age' AND id1.node_id = objects.id AND objects.parent_id = ?}, id]"
103
+
104
+ and_with_same_tables_different_key:
105
+ src: "objects where age > 5 and size = 10"
106
+ res: "[%Q{SELECT objects.* FROM idx_nodes AS id1,idx_nodes AS id2,objects WHERE id1.value > 5 AND id2.value = 10 AND id2.key = 'size' AND id2.node_id = objects.id AND id1.key = 'age' AND id1.node_id = objects.id AND objects.parent_id = ?}, id]"
107
+
108
+
109
+ filter_and_order_index:
110
+ src: "objects where age > 5 order by age asc"
111
+ res: "[%Q{SELECT objects.* FROM idx_nodes AS id1,objects WHERE id1.value > 5 AND id1.key = 'age' AND id1.node_id = objects.id AND objects.parent_id = ? ORDER BY id1.value ASC}, id]"
112
+
113
+ order_without_idx_filter:
114
+ src: "objects order by age asc"
115
+ res: "[%Q{SELECT objects.* FROM idx_nodes AS id1,objects WHERE id1.key = 'age' AND id1.node_id = objects.id AND objects.parent_id = ? ORDER BY id1.value ASC}, id]"
95
116
 
96
117
  equation_par:
97
118
  src: "objects where (1 > 2 or 2 > 3) and 4 = 5 "
@@ -18,7 +18,7 @@ parent_from_parent:
18
18
 
19
19
  children_from_objects_in_project:
20
20
  res: "[%Q{SELECT objects.* FROM objects,objects AS ob1 WHERE objects.parent_id = ob1.id AND ob1.project_id = ? GROUP BY objects.id}, project_id]"
21
-
21
+
22
22
  tags:
23
23
  sxp: '[:query, [:relation, "tags"]]'
24
24
  res: "%Q{SELECT objects.* FROM objects INNER JOIN tags ON objects.id = tags.node_id}"
@@ -31,7 +31,7 @@ complex_from_with_scopes_and_typed_scope:
31
31
  # instead of 'project', we give it a class with 'jobs:project'
32
32
  src: "letters where name = 'foo' in jobs:project from letters in section"
33
33
  sxp: '[:query, [:from, [:scope, [:filter, [:relation, "letters"], [:"=", [:field, "name"], [:string, "foo"]]], "jobs:project"], [:scope, [:relation, "letters"], "section"]]]'
34
-
34
+
35
35
  letters_in_project_from_letters:
36
36
  sxp: '[:query, [:from, [:scope, [:relation, "letters"], "project"], [:relation, "letters"]]]'
37
37
  res: "[%Q{SELECT objects.* FROM objects,objects AS ob1 WHERE objects.kpath LIKE 'NNL%' AND objects.project_id = ob1.id AND ob1.kpath LIKE 'NNL%' AND ob1.parent_id = ? GROUP BY objects.id}, id]"
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 9
8
- - 0
9
- version: 0.9.0
8
+ - 1
9
+ version: 0.9.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - Gaspard Bucher
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-09-14 00:00:00 +02:00
17
+ date: 2010-09-22 00:00:00 +02:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency