like_query 0.0.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.
- checksums.yaml +7 -0
- data/README.md +130 -0
- data/lib/like_query/collect.rb +95 -0
- data/lib/like_query/model_extensions.rb +70 -0
- data/lib/like_query/railtie.rb +7 -0
- data/lib/like_query/version.rb +3 -0
- data/lib/like_query.rb +5 -0
- metadata +66 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 16e59dc644a7dc678761df657971741c56ac76b3a0dcd773cb51b9e7ed241ecf
|
4
|
+
data.tar.gz: 7abd672371cb6ec62799ed15d882f41ed2d88a66f5828f25541eaa96cf1d49ce
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: adb0afaf4ef8a6bc4a5c1c5571958702c751405a55a9f50954f71c63e2ac6283e3f1b62488778ba0ecf3d144dd98dbad62c1585e7ed86e8c6c65ea40b6a11a6d
|
7
|
+
data.tar.gz: 3fb0bd08e5e6c3853f1ab63ae306080a17354d277c7ac0ede1310404c9e2f68777edf6df0770161bc13bc4247efffd5d5c9b7229d1442ffbe4df9f7897d53eaf
|
data/README.md
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
# like_query
|
2
|
+
|
3
|
+
For my customers apps, newly built with turbo, search queries are mostly for two purposes:
|
4
|
+
|
5
|
+
Index view callable by a url like `/customers?find=müller screw` or a javascript dropdown on the front, in our case built with svelte, which receives a json and renders a table in a dropdown.
|
6
|
+
|
7
|
+
This query generator is built for this two purposes.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
add
|
12
|
+
|
13
|
+
`gem "like_query"`
|
14
|
+
|
15
|
+
to Gemfile
|
16
|
+
|
17
|
+
this adds the methods `#like` and `#like_result` to all models.
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
**.like**
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
customer = Customer.create(name: 'Ambühl')
|
25
|
+
art1 = Article.create(name: 'first', number: '01', customer: customer)
|
26
|
+
|
27
|
+
Article.like('fir', :name, :number)
|
28
|
+
# => would find art1
|
29
|
+
# => searches by: "where name like '%fir%' or number like '%fir%'"
|
30
|
+
# => queries are built with Article.arel_table[:name].matches('%fir%')
|
31
|
+
|
32
|
+
Article.like(['fir', 'ambühl'], :name, :number, customer: :name)
|
33
|
+
# => :customer is the name of the association
|
34
|
+
# => search-tag as array adds a ".and(where( ... like '%ambühl%'))" for all scoped columns
|
35
|
+
# => would also find art1
|
36
|
+
```
|
37
|
+
|
38
|
+
**.like_result**
|
39
|
+
|
40
|
+
returns a hash that can easily be transformed by `#to_json` for a javascript-frontend, by example
|
41
|
+
|
42
|
+
can only be chained behind `#like`.
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
customer = Customer.create(name: 'Ambühl')
|
46
|
+
art1 = Article.create(name: 'first', number: '01', customer: customer)
|
47
|
+
|
48
|
+
Article.like('fir', :name).like_result(limit: 10)
|
49
|
+
# returns:
|
50
|
+
# {
|
51
|
+
# :data=>[
|
52
|
+
# {
|
53
|
+
# :attributes=>[[:name, "first"]],
|
54
|
+
# :id=>1}],
|
55
|
+
# :length=>1,
|
56
|
+
# :overflow=>false,
|
57
|
+
# :columns_count=>1,
|
58
|
+
# :sub_records_columns_count=>0
|
59
|
+
# }
|
60
|
+
|
61
|
+
Article.like('fir', :name).like_result( :number, limit: 10)
|
62
|
+
# would query like the above example: Search scope is only :name
|
63
|
+
# but would return article-number instead of article-name inside the data block
|
64
|
+
```
|
65
|
+
|
66
|
+
**Class LikeQuery::Collect**
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
cust = Customer.create(name: 'Müller')
|
70
|
+
20.times {Article.create(name: 'any-article', customer: cust)}
|
71
|
+
|
72
|
+
c = LikeQuery::Collect.new(4)
|
73
|
+
# => 4 is the limit
|
74
|
+
|
75
|
+
c.receive { Article.like('any-art', :name) }
|
76
|
+
# => would add 4 articles to the result hash because of limit
|
77
|
+
|
78
|
+
c.receive { Customer.like('any-art', :name, articles: :name) }
|
79
|
+
# => limit is already exhausted: does nothing
|
80
|
+
# => otherwise it would add Customers to the result hash
|
81
|
+
|
82
|
+
c.result
|
83
|
+
# => would return anything like:
|
84
|
+
{
|
85
|
+
:data=>[
|
86
|
+
{:attributes=>[["customer.name", "Müller"]], :id=>1},
|
87
|
+
{:attributes=>[["customer.name", "Müller"]], :id=>2},
|
88
|
+
{
|
89
|
+
:attributes=>[[:name, "second"], [:number, "01"]], :id=>3,
|
90
|
+
:associations=>{:customer=>[{:attributes=>[[:name, "Ambühl"]], :id=>2}]}}],
|
91
|
+
:length=>4,
|
92
|
+
:overflow=>true,
|
93
|
+
:columns_count=>2,
|
94
|
+
:sub_records_columns_count=>1
|
95
|
+
}
|
96
|
+
```
|
97
|
+
|
98
|
+
## Tests
|
99
|
+
|
100
|
+
Tests for this gem, by rspec, are included not inside this gem, they can be found in a [test project](https://gitlab.com/sedl/like_query_project)
|
101
|
+
|
102
|
+
- [ ] [Set up project integrations](https://gitlab.com/sedl/like_query/-/settings/integrations)
|
103
|
+
|
104
|
+
## Collaborate with your team
|
105
|
+
|
106
|
+
- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
|
107
|
+
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
|
108
|
+
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
|
109
|
+
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
|
110
|
+
- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
|
111
|
+
|
112
|
+
## Test and Deploy
|
113
|
+
|
114
|
+
Use the built-in continuous integration in GitLab.
|
115
|
+
|
116
|
+
- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
|
117
|
+
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
|
118
|
+
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
|
119
|
+
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
|
120
|
+
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
|
121
|
+
|
122
|
+
***
|
123
|
+
|
124
|
+
## Hope this helps
|
125
|
+
|
126
|
+
I created this only for one of my customers. If it may help others, i am happy.
|
127
|
+
|
128
|
+
## License
|
129
|
+
|
130
|
+
[MIT](https://mit-license.org/)
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module LikeQuery
|
2
|
+
class Collect
|
3
|
+
def initialize(limit = 50)
|
4
|
+
@limit = limit
|
5
|
+
@length = 0
|
6
|
+
@data = []
|
7
|
+
@overflow = false
|
8
|
+
@columns_count = 0
|
9
|
+
@sub_records_columns_count = 0
|
10
|
+
end
|
11
|
+
|
12
|
+
def receive(*result_pattern, image: nil, &block)
|
13
|
+
|
14
|
+
return false if @length >= @limit
|
15
|
+
|
16
|
+
recs = yield
|
17
|
+
|
18
|
+
pattern = (result_pattern.present? ? result_pattern : recs.like_query_pattern)
|
19
|
+
|
20
|
+
associations = []
|
21
|
+
pattern.each do |p|
|
22
|
+
if p.is_a?(Hash)
|
23
|
+
associations += p.keys
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
recs.includes(associations).each do |r|
|
28
|
+
rec_attr = {}
|
29
|
+
pattern.each do |p|
|
30
|
+
if p.is_a?(Hash)
|
31
|
+
|
32
|
+
# ASSOCIATED RECORDS
|
33
|
+
|
34
|
+
p.each do |assoc, cols|
|
35
|
+
rec_attr[:associations] ||= {}
|
36
|
+
rec_attr[:associations][assoc] ||= []
|
37
|
+
sub = r.send(assoc)
|
38
|
+
_cols = (cols.is_a?(Enumerable) ? cols : [cols])
|
39
|
+
(sub.is_a?(Enumerable) ? sub : [sub]).each do |_sub|
|
40
|
+
sub_attr = []
|
41
|
+
_cols.each do |c|
|
42
|
+
sub_attr.push([c, _sub.send(c)])
|
43
|
+
end
|
44
|
+
|
45
|
+
if @length >= @limit
|
46
|
+
@overflow = true
|
47
|
+
break
|
48
|
+
elsif @length < @limit
|
49
|
+
c = sub_attr.length
|
50
|
+
@sub_records_columns_count = c if c > @sub_records_columns_count
|
51
|
+
rec_attr[:associations][assoc] ||= []
|
52
|
+
rec_attr[:associations][assoc].push({ attributes: sub_attr, id: _sub.id })
|
53
|
+
@length += 1
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
58
|
+
elsif p.is_a?(Symbol) || p.is_a?(String)
|
59
|
+
|
60
|
+
# MAIN RECORD
|
61
|
+
|
62
|
+
v = nil
|
63
|
+
p.to_s.split('.').each { |_p| v = (v ? v : r).send(_p) }
|
64
|
+
rec_attr[:attributes] ||= []
|
65
|
+
rec_attr[:attributes].push([p, v])
|
66
|
+
rec_attr[:id] = r.id
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
70
|
+
if @length >= @limit
|
71
|
+
@overflow = true
|
72
|
+
break
|
73
|
+
elsif @length < @limit
|
74
|
+
c = (image ? 1 : 0) + rec_attr[:attributes].length
|
75
|
+
@columns_count = c if c > @columns_count
|
76
|
+
@data.push(rec_attr)
|
77
|
+
@length += 1
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
true
|
82
|
+
end
|
83
|
+
|
84
|
+
def result
|
85
|
+
{
|
86
|
+
data: @data,
|
87
|
+
length: @length,
|
88
|
+
overflow: @overflow,
|
89
|
+
columns_count: @columns_count,
|
90
|
+
sub_records_columns_count: @sub_records_columns_count
|
91
|
+
}
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module LikeQuery
|
2
|
+
module ModelExtensions
|
3
|
+
|
4
|
+
def like(search_string, *pattern)
|
5
|
+
|
6
|
+
raise 'like can only be called from a model' if self == ApplicationRecord
|
7
|
+
|
8
|
+
queries = nil
|
9
|
+
associations = []
|
10
|
+
@like_query_pattern = pattern
|
11
|
+
|
12
|
+
(search_string.is_a?(String) ? search_string.split(' ') : search_string).each do |s|
|
13
|
+
str = "%#{s}%"
|
14
|
+
q = nil
|
15
|
+
if pattern.first.is_a?(Array) && pattern.length >= 2
|
16
|
+
raise "only one array can be given: Either pattern as one array or as multiple args, not as array"
|
17
|
+
end
|
18
|
+
(pattern.first.is_a?(Array) ? pattern.first : pattern).each do |p|
|
19
|
+
if p.is_a?(Symbol) || p.is_a?(String)
|
20
|
+
_q = arel_table[p].matches(str)
|
21
|
+
q = (q ? q.or(_q) : _q)
|
22
|
+
elsif p.is_a?(Hash)
|
23
|
+
p.each do |k, v|
|
24
|
+
assoc = reflect_on_association(k)
|
25
|
+
raise "Unknown association: :#{k}" unless assoc.present?
|
26
|
+
associations.push(k.to_sym) unless associations.include?(k.to_sym)
|
27
|
+
if v.is_a?(Symbol) || v.is_a?(String)
|
28
|
+
_q = assoc.klass.arel_table[v].matches(str)
|
29
|
+
q = (q ? q.or(_q) : _q)
|
30
|
+
elsif v.is_a?(Array)
|
31
|
+
v.each do |_v|
|
32
|
+
_q = assoc.klass.arel_table[_v].matches(str)
|
33
|
+
q = (q ? q.or(_q) : _q)
|
34
|
+
end
|
35
|
+
else
|
36
|
+
raise "unknown element: #{p}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
queries = (queries ? queries.and(q) : q)
|
43
|
+
end
|
44
|
+
|
45
|
+
@query = if associations.present?
|
46
|
+
left_outer_joins(associations).where(
|
47
|
+
queries
|
48
|
+
)
|
49
|
+
else
|
50
|
+
where(
|
51
|
+
queries
|
52
|
+
)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def like_result(*pattern, limit: 50, image: nil)
|
57
|
+
|
58
|
+
|
59
|
+
raise 'has to be called behind #like' unless @like_query_pattern.is_a?(Array)
|
60
|
+
c = LikeQuery::Collect.new(limit)
|
61
|
+
c.receive(*pattern){@query}
|
62
|
+
c.result
|
63
|
+
end
|
64
|
+
|
65
|
+
def like_query_pattern
|
66
|
+
@like_query_pattern
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
data/lib/like_query.rb
ADDED
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: like_query
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- christian
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-08-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 7.0.3
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 7.0.3
|
27
|
+
description: helper for building active record calls.
|
28
|
+
email:
|
29
|
+
- christian@sedlmair.ch
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- README.md
|
35
|
+
- lib/like_query.rb
|
36
|
+
- lib/like_query/collect.rb
|
37
|
+
- lib/like_query/model_extensions.rb
|
38
|
+
- lib/like_query/railtie.rb
|
39
|
+
- lib/like_query/version.rb
|
40
|
+
homepage: https://gitlab.com/sedl/like_query
|
41
|
+
licenses:
|
42
|
+
- MIT
|
43
|
+
metadata:
|
44
|
+
homepage_uri: https://gitlab.com/sedl/like_query
|
45
|
+
source_code_uri: https://gitlab.com/sedl/like_query
|
46
|
+
changelog_uri: https://gitlab.com/sedl/like_query
|
47
|
+
post_install_message:
|
48
|
+
rdoc_options: []
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
requirements: []
|
62
|
+
rubygems_version: 3.4.10
|
63
|
+
signing_key:
|
64
|
+
specification_version: 4
|
65
|
+
summary: helper for building active record calls.
|
66
|
+
test_files: []
|