lucene_query 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.
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Jeremy Voorhis
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,9 @@
1
+ require 'rake'
2
+ require 'spec/rake/spectask'
3
+
4
+ desc "Run all examples"
5
+ Spec::Rake::SpecTask.new('examples') do |t|
6
+ t.spec_files = FileList['examples/**/*.rb']
7
+ end
8
+
9
+ task :default => :examples
@@ -0,0 +1,96 @@
1
+ require File.dirname(__FILE__) + '/../lib/lucene_query'
2
+
3
+ describe LuceneQuery do
4
+
5
+ it "should passthru most primitives" do
6
+ lambda { :example }.should generate_query("example")
7
+ lambda { 42 }.should generate_query("42")
8
+ lambda { 3.14159 }.should generate_query("3.14159")
9
+ lambda { true }.should generate_query("true")
10
+ lambda { false }.should generate_query("false")
11
+ end
12
+
13
+ it "should cope with empty Arrays" do
14
+ lambda { Array.new }.should generate_query("")
15
+ end
16
+
17
+ it "should cope with empty Hashes" do
18
+ lambda { Hash.new }.should generate_query("")
19
+ end
20
+
21
+ it "should quote Strings" do
22
+ lambda { "example" }.should generate_query("'example'")
23
+ end
24
+
25
+ it "should escape Strings" do
26
+ lambda { "this || that" }.should generate_query("'this \\|| that'")
27
+ lambda { "this && that" }.should generate_query("'this \\&& that'")
28
+ lambda { "Query builder for the Lucene (and Solr) search engine." }.should generate_query("'Query builder for the Lucene \\(and Solr\\) search engine.'")
29
+ lambda { "~jvoorhis" }.should generate_query("'\\~jvoorhis'")
30
+ lambda { "-spam" }.should generate_query("'\\-spam'")
31
+ lambda { "+ham" }.should generate_query("'\\+ham'")
32
+ lambda { '\d{10}' }.should generate_query("'\\\\d\\{10\\}'")
33
+ end
34
+
35
+ it "should group Arrays" do
36
+ lambda { [:red, :green, :blue] }.should generate_query("(red green blue)")
37
+ end
38
+
39
+ it "should join terms with AND" do
40
+ lambda { And(:symbol, 42, "string") }.should generate_query("(symbol AND 42 AND 'string')")
41
+ end
42
+
43
+ it "should join terms with OR" do
44
+ lambda { Or(:symbol, 42, "string") }.should generate_query("(symbol OR 42 OR 'string')")
45
+ end
46
+
47
+ it "should support fields" do
48
+ lambda { Field(:city, "Portland") }.should generate_query("city:'Portland'")
49
+ lambda { Field("city", "Portland") }.should generate_query("'city':'Portland'")
50
+ end
51
+
52
+ it "should AND together Hash terms" do
53
+ lambda { { :city => "Portland", :state => "Oregon" } }.should generate_query("(state:'Oregon' AND city:'Portland')")
54
+ end
55
+
56
+ it "should OR together IN terms" do
57
+ lambda { In(:id, [110, 220, 330]) }.should generate_query("(id:110 OR id:220 OR id:330)")
58
+ end
59
+
60
+ it "should require terms" do
61
+ lambda { Required("lucene") }.should generate_query("+'lucene'")
62
+ lambda { { :marine_life => [Required("fish"), Required("dolphins")] } }.should generate_query("(marine_life:(+'fish' +'dolphins'))")
63
+ end
64
+
65
+ it "should prohibit terms" do
66
+ lambda { Prohibit("bugs") }.should generate_query("-'bugs'")
67
+ lambda { { :marine_life => [Required("fish"), Prohibit("eels")] } }.should generate_query("(marine_life:(+'fish' -'eels'))")
68
+ end
69
+
70
+ it "should produce fuzzy terms" do
71
+ lambda { Fuzzy("term") }.should generate_query("term~")
72
+ lambda { Fuzzy("multiple terms") }.should generate_query("multiple~ terms~")
73
+ lambda { Fuzzy("term", 0.7) }.should generate_query("term~0.7")
74
+ lambda { Fuzzy("*") }.should generate_query("\\*~")
75
+ end
76
+ end
77
+
78
+ class QueryMatcher
79
+ def initialize(expected)
80
+ @expected = expected
81
+ end
82
+
83
+ def matches?(target)
84
+ @target = target
85
+ @actual = LuceneQuery.new(&@target).to_s
86
+ @expected == @actual
87
+ end
88
+
89
+ def failure_message
90
+ "\tExpected\n#@expected\n\tbut received\n#@actual"
91
+ end
92
+ end
93
+
94
+ def generate_query(query)
95
+ QueryMatcher.new(query)
96
+ end
@@ -0,0 +1,152 @@
1
+ class LuceneQuery
2
+
3
+ VERSION = '0.1'
4
+
5
+ ## Syntax Nodes
6
+ ::String.class_eval do
7
+ def to_lucene; "'#{escape_lucene}'" end
8
+
9
+ def parens
10
+ if self =~ /^\s*$/
11
+ self
12
+ else
13
+ "(#{self})"
14
+ end
15
+ end
16
+
17
+ # The Lucene documentation declares special characters to be:
18
+ # + - && || ! ( ) { } [ ] ^ " ~ * ? : \
19
+ RE_ESCAPE_LUCENE = /
20
+ ( [-+!\(\)\{\}\[\]^"~*?:\\] # A special character
21
+ | && # Boolean &&
22
+ | \|\| # Boolean ||
23
+ )
24
+ /x unless defined?(RE_ESCAPE_LUCENE)
25
+
26
+ def escape_lucene
27
+ gsub(RE_ESCAPE_LUCENE) { |m| "\\#{m}" }
28
+ end
29
+ end
30
+
31
+ ::Symbol.class_eval do
32
+ def to_lucene; to_s end
33
+ end
34
+
35
+ ::Array.class_eval do
36
+ def to_lucene
37
+ map { |t| t.to_lucene }.join(" ").parens
38
+ end
39
+ end
40
+
41
+ ::Hash.class_eval do
42
+ def to_lucene
43
+ inner = map { |k,v| Field.new(k, v) }
44
+ LuceneQuery::And.new(*inner).to_lucene
45
+ end
46
+ end
47
+
48
+ ::Numeric.module_eval do
49
+ def to_lucene; to_s end
50
+ end
51
+
52
+ ::TrueClass.class_eval do
53
+ def to_lucene; to_s end
54
+ end
55
+
56
+ ::FalseClass.class_eval do
57
+ def to_lucene; to_s end
58
+ end
59
+
60
+ class Field
61
+ def initialize(key, val)
62
+ @key, @val = key, val
63
+ end
64
+
65
+ def to_lucene
66
+ @key.to_lucene + ":" + @val.to_lucene
67
+ end
68
+ end
69
+
70
+ class InfixOperator
71
+ def initialize(*terms) @terms = terms end
72
+
73
+ def to_lucene
74
+ @terms.map { |t| t.to_lucene }.join(" #{operator} ").parens
75
+ end
76
+ end
77
+
78
+ class And < InfixOperator
79
+ def operator; "AND" end
80
+ end
81
+
82
+ class Or < InfixOperator
83
+ def operator; "OR" end
84
+ end
85
+
86
+ class Not
87
+ def initialize(term) @term = term end
88
+
89
+ def to_lucene
90
+ "NOT #{@term.to_lucene}"
91
+ end
92
+ end
93
+
94
+ class Required
95
+ def initialize(term) @term = term end
96
+
97
+ def to_lucene
98
+ "+" + @term.to_lucene
99
+ end
100
+ end
101
+
102
+ class Prohibit
103
+ def initialize(term) @term = term end
104
+
105
+ def to_lucene
106
+ "-" + @term.to_lucene
107
+ end
108
+ end
109
+
110
+ class Fuzzy
111
+ def initialize(term, boost=nil)
112
+ @term, @boost = term, boost
113
+ end
114
+
115
+ def to_lucene
116
+ @term.split(/\s+/).map { |t|
117
+ @boost ? "%s~%1.1f" % [t.escape_lucene, @boost] : "%s~" % t.escape_lucene
118
+ } * " "
119
+ end
120
+ end
121
+
122
+ ## DSL Helpers
123
+ class QueryBuilder
124
+ def self.generate(*args, &block)
125
+ new.generate(*args, &block)
126
+ end
127
+
128
+ def generate(&block)
129
+ instance_eval(&block)
130
+ end
131
+
132
+ def Field(key, val) Field.new(key, val) end
133
+ def And(*terms) And.new(*terms) end
134
+ def Or(*terms) Or.new(*terms) end
135
+ def In(field, terms)
136
+ Or.new(*terms.map { |term| Field.new(field, term) })
137
+ end
138
+ def Not(term) Not.new(term) end
139
+ def Required(term) Required.new(term) end
140
+ def Prohibit(term) Prohibit.new(term) end
141
+ def Fuzzy(*args) Fuzzy.new(*args) end
142
+ end
143
+
144
+ def initialize(&block)
145
+ @term = QueryBuilder.generate(&block)
146
+ end
147
+
148
+ def to_s; @term.to_lucene end
149
+ alias :to_str :to_s
150
+ end
151
+
152
+ SolrQuery = LuceneQuery unless defined?(SolrQuery)
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lucene_query
3
+ version: !ruby/object:Gem::Version
4
+ hash: 9
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ version: "0.1"
10
+ platform: ruby
11
+ authors:
12
+ - Jeremy Voorhis
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-05-09 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description:
22
+ email: jvoorhis@elevatedrails.com
23
+ executables: []
24
+
25
+ extensions: []
26
+
27
+ extra_rdoc_files: []
28
+
29
+ files:
30
+ - MIT-LICENSE
31
+ - Rakefile
32
+ - lib/lucene_query.rb
33
+ - examples/lucene_query.rb
34
+ has_rdoc: true
35
+ homepage: http://www.elevatedrails.com/
36
+ licenses: []
37
+
38
+ post_install_message:
39
+ rdoc_options: []
40
+
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ none: false
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ hash: 3
49
+ segments:
50
+ - 0
51
+ version: "0"
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ requirements: []
62
+
63
+ rubyforge_project:
64
+ rubygems_version: 1.4.2
65
+ signing_key:
66
+ specification_version: 3
67
+ summary: Query builder for the Lucene (and Solr) search engine.
68
+ test_files: []
69
+