mongo-search 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.
- data/.gitignore +22 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +33 -0
- data/Guardfile +19 -0
- data/Rakefile +6 -0
- data/lib/mongo-search/builder.rb +31 -0
- data/lib/mongo-search/converters.rb +20 -0
- data/lib/mongo-search/definition.rb +29 -0
- data/lib/mongo-search/matchers/exact_matcher.rb +15 -0
- data/lib/mongo-search/matchers/greater_than_matcher.rb +19 -0
- data/lib/mongo-search/matchers/intersect_matcher.rb +20 -0
- data/lib/mongo-search/matchers/less_than_matcher.rb +19 -0
- data/lib/mongo-search/matchers/partial_matcher.rb +15 -0
- data/lib/mongo-search/sorter.rb +25 -0
- data/lib/mongo-search/version.rb +5 -0
- data/lib/mongo-search.rb +10 -0
- data/mongo-search.gemspec +25 -0
- data/spec/search_spec.rb +70 -0
- data/spec/spec_helper.rb +4 -0
- metadata +101 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
mongo-search (0.0.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: http://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.1.3)
|
10
|
+
ffi (1.0.11)
|
11
|
+
guard (1.0.3)
|
12
|
+
ffi (>= 0.5.0)
|
13
|
+
thor (>= 0.14.6)
|
14
|
+
guard-rspec (0.7.0)
|
15
|
+
guard (>= 0.10.0)
|
16
|
+
rspec (2.10.0)
|
17
|
+
rspec-core (~> 2.10.0)
|
18
|
+
rspec-expectations (~> 2.10.0)
|
19
|
+
rspec-mocks (~> 2.10.0)
|
20
|
+
rspec-core (2.10.1)
|
21
|
+
rspec-expectations (2.10.0)
|
22
|
+
diff-lcs (~> 1.1.3)
|
23
|
+
rspec-mocks (2.10.1)
|
24
|
+
thor (0.14.6)
|
25
|
+
|
26
|
+
PLATFORMS
|
27
|
+
ruby
|
28
|
+
|
29
|
+
DEPENDENCIES
|
30
|
+
guard
|
31
|
+
guard-rspec
|
32
|
+
mongo-search!
|
33
|
+
rspec
|
data/Guardfile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard 'rspec', :version => 2 do
|
5
|
+
watch(%r{^spec/.+_spec\.rb$})
|
6
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
7
|
+
watch('spec/spec_helper.rb') { "spec" }
|
8
|
+
|
9
|
+
# Rails example
|
10
|
+
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
11
|
+
watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
|
12
|
+
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
|
13
|
+
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
14
|
+
watch('config/routes.rb') { "spec/routing" }
|
15
|
+
watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
16
|
+
# Capybara request specs
|
17
|
+
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
|
18
|
+
end
|
19
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module MongoSearch
|
2
|
+
class Builder
|
3
|
+
def initialize(search)
|
4
|
+
@search = search
|
5
|
+
end
|
6
|
+
|
7
|
+
def match(attr)
|
8
|
+
@search << Matchers::PartialMatcher.new(attr)
|
9
|
+
end
|
10
|
+
|
11
|
+
def exact(attr)
|
12
|
+
@search << Matchers::ExactMatcher.new(attr)
|
13
|
+
end
|
14
|
+
|
15
|
+
def intersect(attr)
|
16
|
+
@search << Matchers::IntersectMatcher.new(attr)
|
17
|
+
end
|
18
|
+
|
19
|
+
def greater_than(attr, opts = nil)
|
20
|
+
@search << Matchers::GreaterThanMatcher.new(attr, opts)
|
21
|
+
end
|
22
|
+
|
23
|
+
def less_than(attr, opts = nil)
|
24
|
+
@search << Matchers::LessThanMatcher.new(attr, opts)
|
25
|
+
end
|
26
|
+
|
27
|
+
def sort_with(attr, opts = nil)
|
28
|
+
@search.sorter = Sorter.new(attr, opts)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module MongoSearch
|
2
|
+
# Hash of proc converter objects, defaults to identity func
|
3
|
+
Converters = Hash.new { |h,k| lambda { |i| i } }
|
4
|
+
|
5
|
+
Converters[:time] = lambda do |v|
|
6
|
+
if v.is_a?(Time)
|
7
|
+
v
|
8
|
+
else
|
9
|
+
Time.parse(v.to_s)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
Converters[:array] = lambda do |v|
|
14
|
+
if v.is_a?(String)
|
15
|
+
v.split(/\s*,\s*/)
|
16
|
+
else
|
17
|
+
Array(v)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module MongoSearch
|
2
|
+
class Definition
|
3
|
+
def initialize
|
4
|
+
@filters = []
|
5
|
+
@sorter = nil
|
6
|
+
yield Builder.new(self) if block_given?
|
7
|
+
end
|
8
|
+
|
9
|
+
def <<(filter)
|
10
|
+
@filters << filter
|
11
|
+
end
|
12
|
+
|
13
|
+
def sorter=(sorter)
|
14
|
+
@sorter = sorter
|
15
|
+
end
|
16
|
+
|
17
|
+
def criteria_for(params)
|
18
|
+
conditions = {}
|
19
|
+
|
20
|
+
@filters.each do |filter|
|
21
|
+
conditions.merge! filter.call(params)
|
22
|
+
end
|
23
|
+
|
24
|
+
sort = @sorter ? @sorter.call(params) : []
|
25
|
+
|
26
|
+
[conditions, sort]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module MongoSearch
|
2
|
+
module Matchers
|
3
|
+
class GreaterThanMatcher
|
4
|
+
def initialize(attr, opts = nil)
|
5
|
+
opts ||= {}
|
6
|
+
@attr = attr
|
7
|
+
@field = opts[:field] || attr
|
8
|
+
@operator = opts[:equal] ? :$gte : :$gt
|
9
|
+
@conv = Converters[opts[:type]]
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(params)
|
13
|
+
filters = {}
|
14
|
+
filters[@field] = {@operator => @conv.call(params[@attr])} if params[@attr]
|
15
|
+
filters
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module MongoSearch
|
2
|
+
module Matchers
|
3
|
+
class IntersectMatcher
|
4
|
+
def initialize(attr)
|
5
|
+
@attr = attr
|
6
|
+
end
|
7
|
+
|
8
|
+
def call(params)
|
9
|
+
filters = {}
|
10
|
+
|
11
|
+
if params[@attr]
|
12
|
+
value = params[@attr]
|
13
|
+
filters[@attr] = {:$all => Converters[:array].call(value)}
|
14
|
+
end
|
15
|
+
|
16
|
+
filters
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module MongoSearch
|
2
|
+
module Matchers
|
3
|
+
class LessThanMatcher
|
4
|
+
def initialize(attr, opts = nil)
|
5
|
+
opts ||= {}
|
6
|
+
@attr = attr
|
7
|
+
@field = opts[:field] || attr
|
8
|
+
@operator = opts[:equal] ? :$lte : :$lt
|
9
|
+
@conv = Converters[opts[:type]]
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(params)
|
13
|
+
filters = {}
|
14
|
+
filters[@field] = {@operator => @conv.call(params[@attr])} if params[@attr]
|
15
|
+
filters
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module MongoSearch
|
2
|
+
class Sorter
|
3
|
+
def initialize(attr, opts = nil)
|
4
|
+
@attr, @opts = attr, opts || {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def call(params)
|
8
|
+
sort = []
|
9
|
+
|
10
|
+
if order_by = params[@attr] || @opts[:default]
|
11
|
+
order_by.split(/\s*,\s*/).each do |attr_order|
|
12
|
+
if attr_order =~ /([^\s]+)(?:\s*(asc|desc)?)$/
|
13
|
+
attr_name, dir = $1, ($2 || :asc)
|
14
|
+
sort << [:"#{attr_name}_ordenacao", :"#{dir}"]
|
15
|
+
sort << [:"#{attr_name}", :"#{dir}"]
|
16
|
+
else
|
17
|
+
raise ArgumentError, "invalid sorting parameters: #{order_by}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
sort
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/mongo-search.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require "time"
|
2
|
+
require "mongo-search/sorter"
|
3
|
+
require "mongo-search/converters"
|
4
|
+
require "mongo-search/matchers/exact_matcher"
|
5
|
+
require "mongo-search/matchers/partial_matcher"
|
6
|
+
require "mongo-search/matchers/intersect_matcher"
|
7
|
+
require "mongo-search/matchers/greater_than_matcher"
|
8
|
+
require "mongo-search/matchers/less_than_matcher"
|
9
|
+
require "mongo-search/definition"
|
10
|
+
require "mongo-search/builder"
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "mongo-search/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "mongo-search"
|
7
|
+
s.version = Mongo::Search::VERSION
|
8
|
+
s.authors = ["Rodrigo Kochenburger <divoxx@gmail.com>", "Tomas Mattia <tomas.mattia@gmail.com>"]
|
9
|
+
s.email = ["abril_vejasp_dev@thoughtworks.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Easy search for mongodb}
|
12
|
+
|
13
|
+
s.rubyforge_project = "mongo-search"
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
# specify any dependencies here; for example:
|
21
|
+
s.add_development_dependency "rspec"
|
22
|
+
s.add_development_dependency "guard"
|
23
|
+
s.add_development_dependency "guard-rspec"
|
24
|
+
# s.add_runtime_dependency "rest-client"
|
25
|
+
end
|
data/spec/search_spec.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Search integration" do
|
4
|
+
let :klass do
|
5
|
+
mock(:collection => collection)
|
6
|
+
end
|
7
|
+
|
8
|
+
let :collection do
|
9
|
+
mock(:collection)
|
10
|
+
end
|
11
|
+
|
12
|
+
subject do
|
13
|
+
MongoSearch::Definition.new do |c|
|
14
|
+
c.match :name
|
15
|
+
c.exact :status
|
16
|
+
c.intersect :tags
|
17
|
+
c.less_than :updated_before, :field => :updated_at, :type => :time, :equal => true
|
18
|
+
c.greater_than :updated_since, :field => :updated_at, :type => :time, :equal => true
|
19
|
+
c.sort_with :order, :default => "name asc"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it "matches partial string with case-insensitive regexp" do
|
24
|
+
c, _ = subject.criteria_for :name => "foo"
|
25
|
+
c.should == {:name => /foo/i}
|
26
|
+
end
|
27
|
+
|
28
|
+
it "matches exact string" do
|
29
|
+
c, _ = subject.criteria_for :status => 'published'
|
30
|
+
c.should == {:status => 'published'}
|
31
|
+
end
|
32
|
+
|
33
|
+
it "matches intersection of list items" do
|
34
|
+
c, _ = subject.criteria_for :tags => ['foo', 'bar']
|
35
|
+
c.should == {:tags => {:$all => ['foo', 'bar']}}
|
36
|
+
end
|
37
|
+
|
38
|
+
it "matches intersection of comma separate string" do
|
39
|
+
c, _ = subject.criteria_for :tags => "foo,bar"
|
40
|
+
c.should == {:tags => {:$all => ['foo', 'bar']}}
|
41
|
+
end
|
42
|
+
|
43
|
+
it "matches a parameter against a field using greater than op" do
|
44
|
+
time = Time.parse("2012-01-10T22:10:01Z")
|
45
|
+
c, _ = subject.criteria_for :updated_since => time.strftime("%Y-%m-%dT%H:%M:%SZ")
|
46
|
+
c.should == {:updated_at => {:$gte => time}}
|
47
|
+
end
|
48
|
+
|
49
|
+
it "matches a parameter against a field using less than op" do
|
50
|
+
time = Time.parse("2012-01-10T22:10:01Z")
|
51
|
+
c, _ = subject.criteria_for :updated_before => time.strftime("%Y-%m-%dT%H:%M:%SZ")
|
52
|
+
c.should == {:updated_at => {:$lte => time}}
|
53
|
+
end
|
54
|
+
|
55
|
+
it "uses specified sorting and includes a _ordenacao prefix also" do
|
56
|
+
_, s = subject.criteria_for :order => "titulo desc"
|
57
|
+
s.should == [[:titulo_ordenacao, :desc], [:titulo, :desc]]
|
58
|
+
end
|
59
|
+
|
60
|
+
it "defaults sorting direction to asc if not specified" do
|
61
|
+
_, s = subject.criteria_for :order => "titulo, categoria"
|
62
|
+
s.should include([:titulo, :asc])
|
63
|
+
s.should include([:categoria, :asc])
|
64
|
+
end
|
65
|
+
|
66
|
+
it "uses default sorting if no specified" do
|
67
|
+
_, s = subject.criteria_for({})
|
68
|
+
s.should == [[:name_ordenacao, :asc], [:name, :asc]]
|
69
|
+
end
|
70
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mongo-search
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Rodrigo Kochenburger <divoxx@gmail.com>
|
9
|
+
- Tomas Mattia <tomas.mattia@gmail.com>
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2012-05-22 00:00:00.000000000Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rspec
|
17
|
+
requirement: &70097252840100 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *70097252840100
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: guard
|
28
|
+
requirement: &70097252839420 !ruby/object:Gem::Requirement
|
29
|
+
none: false
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: *70097252839420
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: guard-rspec
|
39
|
+
requirement: &70097252838860 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ! '>='
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: '0'
|
45
|
+
type: :development
|
46
|
+
prerelease: false
|
47
|
+
version_requirements: *70097252838860
|
48
|
+
description:
|
49
|
+
email:
|
50
|
+
- abril_vejasp_dev@thoughtworks.com
|
51
|
+
executables: []
|
52
|
+
extensions: []
|
53
|
+
extra_rdoc_files: []
|
54
|
+
files:
|
55
|
+
- .gitignore
|
56
|
+
- Gemfile
|
57
|
+
- Gemfile.lock
|
58
|
+
- Guardfile
|
59
|
+
- Rakefile
|
60
|
+
- lib/mongo-search.rb
|
61
|
+
- lib/mongo-search/builder.rb
|
62
|
+
- lib/mongo-search/converters.rb
|
63
|
+
- lib/mongo-search/definition.rb
|
64
|
+
- lib/mongo-search/matchers/exact_matcher.rb
|
65
|
+
- lib/mongo-search/matchers/greater_than_matcher.rb
|
66
|
+
- lib/mongo-search/matchers/intersect_matcher.rb
|
67
|
+
- lib/mongo-search/matchers/less_than_matcher.rb
|
68
|
+
- lib/mongo-search/matchers/partial_matcher.rb
|
69
|
+
- lib/mongo-search/sorter.rb
|
70
|
+
- lib/mongo-search/version.rb
|
71
|
+
- mongo-search.gemspec
|
72
|
+
- spec/search_spec.rb
|
73
|
+
- spec/spec_helper.rb
|
74
|
+
homepage: ''
|
75
|
+
licenses: []
|
76
|
+
post_install_message:
|
77
|
+
rdoc_options: []
|
78
|
+
require_paths:
|
79
|
+
- lib
|
80
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ! '>='
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
requirements: []
|
93
|
+
rubyforge_project: mongo-search
|
94
|
+
rubygems_version: 1.8.10
|
95
|
+
signing_key:
|
96
|
+
specification_version: 3
|
97
|
+
summary: Easy search for mongodb
|
98
|
+
test_files:
|
99
|
+
- spec/search_spec.rb
|
100
|
+
- spec/spec_helper.rb
|
101
|
+
has_rdoc:
|