rails-simple-search 1.1.10 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +69 -3
  3. data/lib/sql_handler.rb +36 -15
  4. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7bbf2eecbbb512c647a434d6f6d1f928652cfeafa00c8c52226a88c1ec6bc23c
4
- data.tar.gz: 8b32419b7e0daa8baa581dfd96e04b08513960bbc1a628bda321c12493de6a08
3
+ metadata.gz: 163e11eed3a64c95ca420d49bd6ade5d62bfeaeaff693b3b9586a387f89b7ae1
4
+ data.tar.gz: adb762be25ea2e2672d400667764728ca051df3dba97604610f4a5fe43f7d64b
5
5
  SHA512:
6
- metadata.gz: 9032b02b2027ee62b51680ed8310ccf7579a8eef33494f6536dfc91f414f853bff2496ee53fb875dccfd52f84e60dacea7d3221eba27d51bc5ceafc73873be63
7
- data.tar.gz: 796835f0e60438a36e52783eac52e0fdecf9e4ff3fd9789aa53f95396cdae797d1567c9477dea2fb3e773893950bc8aea54b3ed1f7f0c54b4c164deab0c1c28f
6
+ metadata.gz: d5f332112cbb55e5d966c4d0bba3044a19f56b0923ccc7f256b59fdcdee2d0e83d3708db5ffc718910b7fbe241c3f6480e5503be02ceb967603443ae5942dff7
7
+ data.tar.gz: 765ce655040b6488cb7137fa56afdc9fa3abcee70ca718f6972e0a9f8317ccb794cef9834b071638fc5a96689a22a217fd09c894787ba7d970b6df9a4a0b7ad1
data/README.md CHANGED
@@ -1,10 +1,30 @@
1
+ [![Gem Version](https://badge.fury.io/rb/rails-simple-search.svg)](https://badge.fury.io/rb/rails-simple-search)
2
+
3
+ ### Table of Contents
4
+
5
+ * [What is rails-simple-search?](#what-is-rails-simple-search)
6
+
7
+ * [Why rails-simple-search?](#why-rails-simple-search)
8
+
9
+ * [Installation](#installation)
10
+
11
+ * [Usage](#usage)
12
+
13
+ * [How to construct field names](#how-to-construct-field-names)
14
+
15
+ * [Demo projects](#demo-projects)
16
+
17
+ * [Version history](#version-history)
18
+
19
+ * [License](#license)
20
+
1
21
  ## What is rails-simple-search?
2
22
  rails-simple-search is a Ruby gem. It can help Ruby on Rails developers **quickly**
3
23
  implement searching/filtering function against database. If you're not looking
4
24
  for a full-text searching solution, this plugin will most probably **satisfy all**
5
25
  your searching requirement.
6
26
 
7
- ## Why?
27
+ ## Why rails-simple-search?
8
28
  From time to time, I need to build pages to show a list of narrowed down records
9
29
  from a database table by giving some searching criteria on some columns of the
10
30
  table and/or of some referencing tables. Before I implemented this plugin, I usually
@@ -67,6 +87,8 @@ The following is how we implement this searching function with rails-simple-sear
67
87
  ```
68
88
 
69
89
  4. Code in views:
90
+ **Pay attention to the name of the form fields**.
91
+
70
92
  ```
71
93
  <% form_for @search, url => "/xxxxxx", data: {turbo: false} do |f| %>
72
94
 
@@ -89,7 +111,7 @@ The following is how we implement this searching function with rails-simple-sear
89
111
  <%=f.text_field "address.city" %> <!-- address is an association of model User -->
90
112
 
91
113
  <%=f.label "name of any one who commented to my posts" %>
92
- <%=f.text_field "posts.comments.author.first_name_or_posts.comments.author.last_name" %>
114
+ <%=f.text_field "posts.comments.user.first_name_or_posts.comments.user.last_name" %>
93
115
  <!-- the associations could go even deeper, isn't it POWERFUL? -->
94
116
 
95
117
  <%=f.submit %>
@@ -105,6 +127,50 @@ The following is how we implement this searching function with rails-simple-sear
105
127
  post "/xxxxxx" => "yyyyyyy#zzzzzz"
106
128
  ```
107
129
 
130
+ ## How to construct field names
131
+ 1. Let's call the model we're search for is the base model. If you just want to search
132
+ against any direct fields of the base model, we just use the table field name as the html
133
+ input field name. For example
134
+ ```
135
+ "email"
136
+ ```
137
+
138
+ 2. If you need to search against fields of the base model's association, you can just
139
+ use the association name and the table field name with a dot "." connecting them. Like this
140
+ ```
141
+ "address.city"
142
+ ```
143
+
144
+ 3. You can chain the associations more than 2 layers. For example, we're going to
145
+ find out the users whose posts have been commented by someone whose first name we happen to know.
146
+ ```
147
+ "posts.comments.user.first_name"
148
+ ```
149
+
150
+ 4. Sometimes we need to find out something according to a range of time, or a range of numbers,
151
+ we can attach "_greater_than", "_greater_than_equal_to", "_less_than", or "_less_than_equal_to".
152
+ For example, we need to find out the users who birth date is between a range, the field names
153
+ can be like this
154
+ ```
155
+ birth_date_greater_than
156
+ ```
157
+ and
158
+ ```
159
+ birth_date_greater_than
160
+ ```
161
+
162
+ 5. Sometimes we need to express the idea of "or", for example, I know roughly a user's name, but
163
+ not sure if it's her first name or last name, we can do it like this
164
+ ```
165
+ first_name_or_last_name
166
+ ```
167
+
168
+ 6. We can even use the "or" relation with association fields. For example
169
+ ```
170
+ posts.comments.user.first_name_or_posts.comments.user.last_name
171
+ ```
172
+
173
+
108
174
  ## Demo projects
109
175
  There are 2 demo projects for this gem, one for [Rails 5](https://github.com/yzhanginwa/demo_app_for_rails_simple_search)
110
176
  and one for [Rails 7](https://github.com/yzhanginwa/rails_simple_search_demo). You are encouraged to clone them to your local and
@@ -122,7 +188,7 @@ From version 1.1.0 on, we started to support the "or" relation, e.g., we can use
122
188
 
123
189
  From version 1.1.3 on, we started to support Rails 5.
124
190
 
125
- For Rails 7, please use version 1.1.9.
191
+ For Rails 7, please use version 1.2.0.
126
192
 
127
193
  ## License
128
194
 
data/lib/sql_handler.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  module RailsSimpleSearch
2
2
  module SqlHandler
3
3
  def init
4
- @table_name = @model_class.table_name
4
+ @model_table_name = @model_class.table_name
5
5
  @joins = {}
6
6
  end
7
7
 
@@ -36,7 +36,7 @@ module RailsSimpleSearch
36
36
  joins.each do |j|
37
37
  table = j[1]
38
38
  constrain = j[2]
39
- @joins_str << " inner join #{table} on #{constrain}"
39
+ @joins_str << format(" inner join #{table} AS A%02d on #{constrain}", j[0])
40
40
  end
41
41
  end
42
42
 
@@ -45,16 +45,17 @@ module RailsSimpleSearch
45
45
  @condition_group.set_relation(:and)
46
46
 
47
47
  @criteria.each do |key, value|
48
- @condition_group.add(parse_attribute(key, value))
48
+ @condition_group.add(parse_search_parameters(key, value))
49
49
  end
50
50
 
51
51
  make_joins
52
52
  end
53
53
 
54
- def insert_condition(base_class, field, value)
54
+ def build_single_condition(base_class, association_alias, field, value)
55
55
  field, operator = parse_field_name(field)
56
56
  table = base_class.table_name
57
57
  key = "#{table}.#{field}"
58
+ final_key = "#{association_alias}.#{field}"
58
59
 
59
60
  column = base_class.columns_hash[field.to_s]
60
61
  return nil unless column
@@ -65,17 +66,22 @@ module RailsSimpleSearch
65
66
  verb = 'in'
66
67
  elsif operator
67
68
  verb = operator
68
- elsif text_column?(column) && ! @config[:exact_match].include?((@table_name == table) ? field : key)
69
+ elsif text_column?(column) && ! @config[:exact_match].include?((@model_table_name == table) ? field : key)
69
70
  verb = 'like'
70
71
  value = "%#{value}%"
71
72
  else
72
73
  verb = '='
73
74
  end
74
75
 
75
- ConditionGroup.new(ConditionItem.new(key, verb, value))
76
+ ConditionGroup.new(ConditionItem.new(final_key, verb, value))
76
77
  end
77
78
 
78
- def insert_join(base_class, asso_ref)
79
+ def table_name_to_alias(table_name)
80
+ format('A%02d', @joins[table_name][0])
81
+ end
82
+
83
+ def insert_join(base_class, asso_ref, new_asso_chain)
84
+ debugger
79
85
  base_table = base_class.table_name
80
86
  asso_table = asso_ref.klass.table_name
81
87
 
@@ -84,45 +90,57 @@ module RailsSimpleSearch
84
90
  return unless @joins[asso_table].nil?
85
91
 
86
92
  @join_count += 1
93
+ base_table_alias = new_asso_chain ? base_table : table_name_to_alias(base_table)
94
+ asso_table_alias = format('A%02d', @join_count)
95
+
87
96
  if asso_ref.belongs_to?
88
- @joins[asso_table] =[@join_count, asso_table, "#{base_table}.#{asso_ref.foreign_key} = #{asso_table}.#{asso_ref.klass.primary_key}"]
97
+ @joins[asso_table] =[@join_count, asso_table, "#{base_table_alias}.#{asso_ref.foreign_key} = #{asso_table_alias}.#{asso_ref.klass.primary_key}"]
89
98
  else
90
- join_cond = "#{base_table}.#{base_class.primary_key} = #{asso_table}.#{asso_ref.foreign_key}"
91
- join_cond = "#{asso_table}.#{asso_ref.type} = '#{base_class.name}' and #{join_cond}" if asso_ref.type
99
+ join_cond = "#{base_table_alias}.#{base_class.primary_key} = #{asso_table_alias}.#{asso_ref.foreign_key}"
100
+ join_cond = "#{asso_table_alias}.#{asso_ref.type} = '#{base_class.name}' and #{join_cond}" if asso_ref.type
92
101
  @joins[asso_table] = [@join_count, asso_table, join_cond]
93
102
  end
94
103
  end
95
104
 
96
- def parse_attribute(attribute, value)
105
+ # This method parse a search parameter and its value
106
+ # then produce a ConditionGroup
107
+ def parse_search_parameters(attribute, value)
108
+ # handle _or_ parameters
97
109
  attributes = attribute.split(@config[:or_separator])
98
110
  if attributes.size > 1
99
111
  cg = ConditionGroup.new
100
112
  cg.set_relation(:or)
101
113
  attributes.each do |a|
102
- cg.add(parse_attribute(a, value))
114
+ cg.add(parse_search_parameters(a, value))
103
115
  end
104
116
  return cg
105
117
  end
106
118
 
119
+ # handle direct fields
107
120
  unless attribute =~ /\./
108
- condition = insert_condition(@model_class, attribute, value)
121
+ condition = build_single_condition(@model_class, @model_class.table_name, attribute, value)
109
122
  return condition
110
123
  end
111
124
 
125
+ # handle association fields
112
126
  association_fields = attribute.split(/\./)
113
127
  field = association_fields.pop
114
128
 
115
129
  base_class = @model_class
130
+ new_asso_chain = true
116
131
  until association_fields.empty?
117
132
  association_fields[0] = base_class.reflect_on_association(association_fields[0].to_sym)
118
- insert_join(base_class, association_fields[0])
133
+ insert_join(base_class, association_fields[0], new_asso_chain)
134
+ new_asso_chain = false
119
135
  base_class = association_fields.shift.klass
120
136
  end
121
137
 
122
- insert_condition(base_class, field, value)
138
+ association_alias = table_name_to_alias(base_class.table_name)
139
+ build_single_condition(base_class, association_alias, field, value)
123
140
  end
124
141
  end
125
142
 
143
+ # This class holds a single condition
126
144
  class ConditionItem
127
145
  attr_reader :field, :verb, :value
128
146
 
@@ -133,6 +151,9 @@ module RailsSimpleSearch
133
151
  end
134
152
  end
135
153
 
154
+ # This class holds a ConditionGroup
155
+ # One ConditionGroup can hold one or more
156
+ # conditions
136
157
  class ConditionGroup
137
158
  def initialize(item = nil)
138
159
  if item
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-simple-search
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.10
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yi Zhang
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-12-26 00:00:00.000000000 Z
11
+ date: 2023-12-27 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: rails-simple-search is a light and easy to use gem. It could help developers
14
14
  quickly build a search page.
@@ -32,7 +32,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
32
32
  requirements:
33
33
  - - ">="
34
34
  - !ruby/object:Gem::Version
35
- version: '0'
35
+ version: 2.7.0
36
36
  required_rubygems_version: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="