querybuilder 0.9.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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