groonga 0.0.5 → 0.0.6

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.
Files changed (51) hide show
  1. data/NEWS.ja.rdoc +13 -0
  2. data/NEWS.rdoc +13 -0
  3. data/README.ja.rdoc +2 -1
  4. data/README.rdoc +2 -1
  5. data/Rakefile +0 -1
  6. data/TUTORIAL.ja.rdoc +49 -4
  7. data/benchmark/read-write-many-small-items.rb +5 -4
  8. data/benchmark/write-many-small-items.rb +5 -4
  9. data/example/bookmark.rb +30 -1
  10. data/example/index-html.rb +79 -0
  11. data/example/search/config.ru +182 -0
  12. data/example/search/public/css/groonga.css +122 -0
  13. data/ext/rb-grn-array.c +12 -9
  14. data/ext/rb-grn-column.c +13 -8
  15. data/ext/rb-grn-context.c +34 -16
  16. data/ext/rb-grn-database.c +3 -2
  17. data/ext/rb-grn-expression-builder.c +8 -2
  18. data/ext/rb-grn-expression.c +3 -3
  19. data/ext/rb-grn-hash.c +13 -10
  20. data/ext/rb-grn-object.c +127 -19
  21. data/ext/rb-grn-patricia-trie.c +8 -7
  22. data/ext/rb-grn-table-cursor.c +2 -40
  23. data/ext/rb-grn-table.c +154 -42
  24. data/ext/rb-grn-type.c +18 -10
  25. data/ext/rb-grn-utils.c +22 -20
  26. data/ext/rb-grn.h +9 -12
  27. data/extconf.rb +1 -1
  28. data/html/developer.html +1 -1
  29. data/html/index.html +2 -2
  30. data/lib/groonga/expression-builder.rb +133 -63
  31. data/lib/groonga/schema.rb +229 -37
  32. data/test/groonga-test-utils.rb +1 -1
  33. data/test/test-array.rb +1 -0
  34. data/test/test-context.rb +7 -1
  35. data/test/test-database.rb +11 -3
  36. data/test/test-expression-builder.rb +26 -2
  37. data/test/test-fix-size-column.rb +2 -1
  38. data/test/test-hash.rb +6 -1
  39. data/test/test-record.rb +2 -1
  40. data/test/test-schema.rb +85 -10
  41. data/test/test-table.rb +99 -3
  42. data/test/test-type.rb +3 -2
  43. data/test/test-variable-size-column.rb +2 -1
  44. data/test-unit/Rakefile +6 -1
  45. data/test-unit/lib/test/unit/autorunner.rb +26 -3
  46. data/test-unit/lib/test/unit/priority.rb +21 -1
  47. data/test-unit/lib/test/unit/testcase.rb +101 -36
  48. data/test-unit/lib/test/unit/ui/console/testrunner.rb +7 -4
  49. data/test-unit/test/{test_testcase.rb → test-testcase.rb} +30 -1
  50. data/test-unit/test/test_assertions.rb +1 -1
  51. metadata +9 -6
data/NEWS.ja.rdoc CHANGED
@@ -1,5 +1,18 @@
1
1
  = お知らせ
2
2
 
3
+ == 0.0.6: 2009-07-31
4
+
5
+ * groonga 0.1.1対応
6
+ * ドキュメントの修正 [id:mat_aki]
7
+ * Groonga::Table#selectでのg式対応
8
+ * APIの追加
9
+ * Groonga::Table#union!
10
+ * Groonga::Table#intersect!
11
+ * Groonga::Table#differene!
12
+ * Groonga::Table#merge!
13
+ * tar.gzも提供 [id:m_seki]
14
+ * メモリリークの修正
15
+
3
16
  == 0.0.3: 2009-07-18
4
17
 
5
18
  * [#26145] Groonga::TableKeySupport#has_key?の追加 [Tasuku SUENAGA]
data/NEWS.rdoc CHANGED
@@ -1,5 +1,18 @@
1
1
  = NEWS
2
2
 
3
+ == 0.0.6: 2009-07-31
4
+
5
+ * Supported groonga 0.1.1.
6
+ * Fixed documents [id:mat_aki]
7
+ * Supported groonga expression for searching.
8
+ * Added API
9
+ * Groonga::Table#union!
10
+ * Groonga::Table#intersect!
11
+ * Groonga::Table#differene!
12
+ * Groonga::Table#merge!
13
+ * Provided tar.gz [id:m_seki]
14
+ * Fixed memory leaks
15
+
3
16
  == 0.0.3: 2009-07-18
4
17
 
5
18
  * [#26145] Added Groonga::TableKeySupport#has_key? [Tasuku SUENAGA]
data/README.ja.rdoc CHANGED
@@ -32,7 +32,7 @@ pkg-config.rbはrcairoに付属しているもので、これはRubyライセ
32
32
  == 依存ソフトウェア
33
33
 
34
34
  * Ruby >= 1.8 (1.9.1対応)
35
- * groonga >= 0.0.8
35
+ * groonga >= 0.1.1
36
36
 
37
37
  == インストール
38
38
 
@@ -54,3 +54,4 @@ http://lists.sourceforge.jp/mailman/listinfo/groonga-dev
54
54
  * グニャラくん: バグレポートしてくれました。
55
55
  * にくさん: バグレポートしてくれました。
56
56
  * daraさん: テストを書いてくれました。
57
+ * id:mat_akiさん: チュートリアルのバグを教えてくれました。
data/README.rdoc CHANGED
@@ -33,7 +33,7 @@ license/GPL for details.
33
33
  == Dependencies
34
34
 
35
35
  * Ruby >= 1.8 (including 1.9.1)
36
- * groonga >= 0.0.8
36
+ * groonga >= 0.1.1
37
37
 
38
38
  == Install
39
39
 
@@ -55,3 +55,4 @@ http://rubyforge.org/mailman/listinfo/groonga-users-en
55
55
  * Tasuku SUENAGA: sent bug reports.
56
56
  * niku: sent bug reports.
57
57
  * dara: wrote tests.
58
+ * id:mat_aki: sent bug reports.
data/Rakefile CHANGED
@@ -119,7 +119,6 @@ Hoe.spec('groonga') do |_project|
119
119
  description = cleanup_white_space(entries[entries.index("Description") + 1])
120
120
  project.summary, project.description, = description.split(/\n\n+/, 3)
121
121
 
122
- project.need_tar = false
123
122
  project.remote_rdoc_dir = "groonga"
124
123
  end
125
124
 
data/TUTORIAL.ja.rdoc CHANGED
@@ -187,9 +187,9 @@ http://qwik.jp/senna/senna2.files/rect4605.png
187
187
 
188
188
  次に、<tt><comments></tt>テーブルを追加します。
189
189
 
190
- >> comments = Groonga::Hash.create(:name => "<comments>",
191
- :key_type => "<shorttext>")
192
- => #<Groonga::Hash ...>
190
+ >> comments = Groonga::Array.create(:name => "<comments>",
191
+ :key_type => "<shorttext>")
192
+ => #<Groonga::Array ...>
193
193
  >> comments.define_column("item", items)
194
194
  => #<Groonga::FixSizeColumn ..>
195
195
  >> comments.define_column("author", users)
@@ -347,4 +347,49 @@ http://qwik.jp/senna/senna2.files/rect4605.png
347
347
 
348
348
  == 少し複雑な検索
349
349
 
350
- つづく。。。
350
+ ↓はまだ動かない!!!
351
+
352
+ さらに実用的な検索について考えてみましょう。
353
+
354
+ ブックマークが大量に蓄積されるに従って、より的確に適合度を算
355
+ 出する必要性に迫られます。
356
+
357
+ 今のところ検索対象として利用できるのは<tt><items>.title</tt>
358
+ と<tt><comments>.content</tt>ですが、<tt><items>.title</tt>は
359
+ 元ページから得られるやや信頼できる情報なのに対して、
360
+ <tt><comments>.content</tt>はブックマークユーザが任意に設定で
361
+ きる情報で、やや信憑性に乏しいと言えます。しかし、再現率を確
362
+ 保するためにはユーザのコメントも是非対象に含めたいところです。
363
+
364
+ そこで、以下のようなポリシーで検索を行うことにします。
365
+
366
+ * <tt><items>.title</tt>か<tt><comments>.content</tt>のいずれ
367
+ かにマッチするitemを検索する。
368
+ * ただし、<tt><items>.title</tt>にマッチしたレコードはスコア
369
+ を10倍重み付けする。
370
+ * 同一のitemに対して、キーワードにマッチする<tt>comment</tt>
371
+ が複数存在した場合は、それぞれの<tt>comment</tt>のスコアの
372
+ 和を、該当するitemのスコアとする。
373
+
374
+ 以下のようにして、commentとitemとそれぞれに対する検索結果を求
375
+ めます。
376
+
377
+ >> ruby_comments = @comments.select {|record| record["content"] =~ "Ruby"}
378
+ #<Groonga::Hash ..., size: <2>>
379
+ >> ruby_items = @items.select("*W1:50 title:%Ruby")
380
+ #<Groonga::Hash ..., size: <2>>
381
+
382
+ _ruby_comments_の結果をitem毎にグループ化し、_ruby_items_と
383
+ unionして出力します。
384
+
385
+ >> ruby_items = ruby_comments.group([".item"]).union!(ruby_items)
386
+ #<Groonga::Hash ..., size: <4>>
387
+ >> ruby_items.sort([{:key => ".:score", :order => "descendant"}]).each do |record|
388
+ >> p [record[".:score"], record[".title"]]
389
+ >> end
390
+ [1, "るびま"]
391
+ [1, "オブジェクトスクリプト言語Ruby"]
392
+ [1, "Ruby"]
393
+ [1, "ラングバ"]
394
+
395
+ これで目的の結果が得られました。(FIXME: 得られていない!)
@@ -55,10 +55,11 @@ begin
55
55
 
56
56
  require 'groonga'
57
57
  @database = Groonga::Database.create
58
+ value_type = Groonga::Type.new("Text8", :size => 8)
58
59
 
59
60
  item("groonga: Hash: memory") do
60
61
  @hash = Groonga::Hash.create(:key_type => "<shorttext>",
61
- :value_size => 8)
62
+ :value_type => value_type)
62
63
  values.each do |value|
63
64
  @hash[value] = value
64
65
  @hash[value]
@@ -67,7 +68,7 @@ begin
67
68
 
68
69
  item("groonga: Trie: memory") do
69
70
  @hash = Groonga::PatriciaTrie.create(:key_type => "<shorttext>",
70
- :value_size => 8)
71
+ :value_type => value_type)
71
72
  values.each do |value|
72
73
  @hash[value] = value
73
74
  @hash[value]
@@ -77,7 +78,7 @@ begin
77
78
  @hash_file = Tempfile.new("groonga-hash")
78
79
  item("groonga: Hash: file") do
79
80
  @hash = Groonga::Hash.create(:key_type => "<shorttext>",
80
- :value_size => 8,
81
+ :value_type => value_type,
81
82
  :path => @hash_file.path)
82
83
  values.each do |value|
83
84
  @hash[value] = value
@@ -88,7 +89,7 @@ begin
88
89
  trie_file = Tempfile.new("groonga-trie")
89
90
  item("groonga: Trie: file") do
90
91
  @hash = Groonga::PatriciaTrie.create(:key_type => "<shorttext>",
91
- :value_size => 8,
92
+ :value_type => value_type,
92
93
  :path => trie_file.path)
93
94
  values.each do |value|
94
95
  @hash[value] = value
@@ -54,10 +54,11 @@ begin
54
54
 
55
55
  require 'groonga'
56
56
  @database = Groonga::Database.create
57
+ value_type = Groonga::Type.new("Text8", :size => 8)
57
58
 
58
59
  item("groonga: Hash: memory") do
59
60
  @hash = Groonga::Hash.create(:key_type => "<shorttext>",
60
- :value_size => 8)
61
+ :value_type => value_type)
61
62
  values.each do |value|
62
63
  @hash[value] = value
63
64
  end
@@ -65,7 +66,7 @@ begin
65
66
 
66
67
  item("groonga: Trie: memory") do
67
68
  @hash = Groonga::PatriciaTrie.create(:key_type => "<shorttext>",
68
- :value_size => 8)
69
+ :value_type => value_type)
69
70
  values.each do |value|
70
71
  @hash[value] = value
71
72
  end
@@ -74,7 +75,7 @@ begin
74
75
  @hash_file = Tempfile.new("groonga-hash")
75
76
  item("groonga: Hash: file") do
76
77
  @hash = Groonga::Hash.create(:key_type => "<shorttext>",
77
- :value_size => 8,
78
+ :value_type => value_type,
78
79
  :path => @hash_file.path)
79
80
  values.each do |value|
80
81
  @hash[value] = value
@@ -84,7 +85,7 @@ begin
84
85
  trie_file = Tempfile.new("groonga-trie")
85
86
  item("groonga: Trie: file") do
86
87
  @hash = Groonga::PatriciaTrie.create(:key_type => "<shorttext>",
87
- :value_size => 8,
88
+ :value_type => value_type,
88
89
  :path => trie_file.path)
89
90
  values.each do |value|
90
91
  @hash[value] = value
data/example/bookmark.rb CHANGED
@@ -14,6 +14,12 @@ rescue LoadError
14
14
  require "groonga"
15
15
  end
16
16
 
17
+ require 'time'
18
+
19
+ # Groonga::Logger.register(:level => :debug) do |level, time, title, message, location|
20
+ # p [level, time, title, message, location]
21
+ # end
22
+
17
23
  $KCODE = "UTF-8"
18
24
  Groonga::Context.default_options = {:encoding => :utf8}
19
25
 
@@ -43,10 +49,13 @@ p(terms = Groonga::Hash.create(:name => "<terms>",
43
49
  :default_tokenizer => "<token:bigram>"))
44
50
  p terms.define_index_column("item_title", items,
45
51
  :persistent => persistent,
52
+ :with_weight => true,
53
+ :with_section => true,
54
+ :with_position => true,
46
55
  :source => "<items>.title")
47
56
 
48
57
  p items.find("http://ja.wikipedia.org/wiki/Ruby")["title"] = "Ruby"
49
- p items.find("http://www.ruby-lang.org/")["title"] = "オブジェクトスクリプト言語Ruby"
58
+ p items.find("http://www.ruby-lang.org/")["title"] = "オブジェクト指向スクリプト言語Ruby"
50
59
 
51
60
  p(users = Groonga::Hash.create(:name => "<users>",
52
61
  :key_type => "<shorttext>",
@@ -63,6 +72,9 @@ p comments.define_column("issued", "<time>")
63
72
 
64
73
  p terms.define_index_column("comment_content", comments,
65
74
  :persistent => persistent,
75
+ :with_weight => true,
76
+ :with_section => true,
77
+ :with_position => true,
66
78
  :source => "<comments>.content")
67
79
 
68
80
  p users.add("moritan", :name => "モリタン")
@@ -96,6 +108,15 @@ p add_bookmark("http://d.hatena.ne.jp/higepon/20070815/1187192864",
96
108
  p add_bookmark("http://practical-scheme.net/docs/cont-j.html",
97
109
  "なんでも継続", "taporobo", "トランポリン LISP continuation",
98
110
  1187568692)
111
+ p add_bookmark("http://jp.rubyist.net/managinze",
112
+ "るびま", "moritan", "Ruby ドキュメント",
113
+ Time.now)
114
+ p add_bookmark("http://jp.rubyist.net/managinze",
115
+ "るびま", "taporobo", "Ruby 雑誌",
116
+ Time.now)
117
+ p add_bookmark("http://groonga.rubyforge.org/",
118
+ "ラングバ", "moritan", "Ruby groonga",
119
+ Time.parse("2009-07-19"))
99
120
 
100
121
 
101
122
  records = comments.select do |record|
@@ -128,3 +149,11 @@ records.group([".item"]).each do |record|
128
149
  item.key,
129
150
  item[".title"]]
130
151
  end
152
+
153
+ p ruby_comments = @comments.select {|record| record["content"] =~ "Ruby"}
154
+ p ruby_items = @items.select("*W1:50 title:%Ruby")
155
+
156
+ p ruby_items = ruby_comments.group([".item"]).union!(ruby_items)
157
+ ruby_items.sort([{:key => ".:score", :order => "descending"}]).each do |record|
158
+ p [record[".:score"], record[".title"]]
159
+ end
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if ARGV.size < 2 or ARGV.find {|option| option == "-h" or option == "--help"}
4
+ puts "Usage: #{$0} DATABASE_FILE [FILE_OR_DIRECTORY ...]"
5
+ exit
6
+ end
7
+
8
+ require 'pathname'
9
+
10
+ base_directory = Pathname(__FILE__).dirname + ".."
11
+ $LOAD_PATH.unshift((base_directory + "ext").to_s)
12
+ $LOAD_PATH.unshift((base_directory + "lib").to_s)
13
+
14
+ require 'rubygems'
15
+ require 'groonga'
16
+ require 'nokogiri'
17
+
18
+ database_file, *targets = ARGV
19
+
20
+ database_file = Pathname(database_file)
21
+ database_directory = database_file.dirname
22
+ database_directory.mkpath unless database_directory.exist?
23
+
24
+ if database_file.exist?
25
+ Groonga::Database.open(database_file.to_s)
26
+ else
27
+ Groonga::Database.create(:path => database_file.to_s)
28
+ Groonga::Schema.define do |schema|
29
+ schema.create_table("documents") do |table|
30
+ table.string("title")
31
+ table.text("content")
32
+ table.string("path")
33
+ table.time("last-modified")
34
+ end
35
+
36
+ schema.create_table("terms",
37
+ :type => :patricia_trie,
38
+ :key_normalize => true,
39
+ :default_tokenizer => "TokenBigram") do |table|
40
+ table.index("documents.title")
41
+ table.index("documents.content")
42
+ end
43
+ end
44
+ end
45
+
46
+ documents = Groonga::Context.default["documents"]
47
+
48
+ targets.each do |target|
49
+ target = Pathname(target)
50
+ target.find do |path|
51
+ throw :prune if path.basename.to_s == ".svn"
52
+ if path.file? and path.extname == ".html"
53
+ path.open do |html|
54
+ values = {:path => path.relative_path_from(target).to_s}
55
+ _documents = documents.select do |record|
56
+ record["path"] == values[:path]
57
+ end
58
+ if _documents.size.zero?
59
+ document = documents.add
60
+ else
61
+ document = _documents.to_a[0].key
62
+ end
63
+
64
+ html_document = Nokogiri::HTML(html)
65
+ html_document.css("title").each do |title|
66
+ values[:title] = title.text
67
+ end
68
+ html_document.css("body").each do |body|
69
+ values[:content] = body.text
70
+ end
71
+ values["last-modified"] = path.mtime
72
+
73
+ values.each do |key, value|
74
+ document[key] = value
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,182 @@
1
+ # -*- mode: ruby; coding: utf-8 -*-
2
+
3
+ base_dir = File.join(File.dirname(__FILE__), "..", "..")
4
+ $LOAD_PATH.unshift(File.join(base_dir, "ext"))
5
+ $LOAD_PATH.unshift(File.join(base_dir, "lib"))
6
+
7
+ require 'rubygems'
8
+ require 'rack'
9
+ require 'groonga'
10
+
11
+ use Rack::CommonLogger
12
+ use Rack::Static, :urls => ["/css", "/images"], :root => "public"
13
+
14
+ Groonga::Database.new("data/database")
15
+
16
+ class Searcher
17
+ include Rack::Utils
18
+
19
+ def initialize
20
+ @documents = Groonga::Context.default["documents"]
21
+ end
22
+
23
+ def call(env)
24
+ request = Rack::Request.new(env)
25
+ response = Rack::Response.new
26
+ response["Content-Type"] = "text/html"
27
+
28
+ response.write(<<-EOH)
29
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
30
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
31
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
32
+ <head>
33
+ <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
34
+ <meta name="robot" content="noindex,nofollow" />
35
+ <title>groongaで全文検索</title>
36
+ <link rel="stylesheet" href="css/groonga.css" type="text/css" media="all" />
37
+ </head>
38
+ <body>
39
+ <div class="header">
40
+ <h1>groongaで全文検索</h1>
41
+ </div>
42
+
43
+ <div class="content">
44
+ EOH
45
+
46
+ render_search_box(request, response)
47
+ render_search_result(request, response)
48
+
49
+ response.write(<<-EOF)
50
+ </div>
51
+
52
+ <div class="footer">
53
+ <p class="powered-by-groonga">
54
+ <a href="http://groonga.org/">groonga</a>
55
+ #{Groonga::VERSION.join('.')}を使っています。
56
+ </p>
57
+ <p class="powered-by-ruby-groonga">
58
+ <a href="http://groonga.rubyforge.org/">Ruby/groonga</a>
59
+ #{Groonga::BINDINGS_VERSION.join('.')}を使っています。
60
+ </p>
61
+ </div>
62
+
63
+ </body>
64
+ </html>
65
+ EOF
66
+ response.to_a
67
+ end
68
+
69
+ private
70
+ def query(request)
71
+ request['query'] || ''
72
+ end
73
+
74
+ def words(request)
75
+ query(request).split
76
+ end
77
+
78
+ def render_search_box(request, response)
79
+ response.write(<<-EOF)
80
+ <form method="get" action="./">
81
+ <p>
82
+ <a href="."><img src="images/mini-groonga.png" alt="groonga" /></a>
83
+ <input name="query" type="text" value="#{escape_html(query(request))}" />
84
+ <input type="submit" value="検索" />
85
+ </p>
86
+ </form>
87
+ EOF
88
+ end
89
+
90
+ def render_search_result(request, response)
91
+ _words = words(request)
92
+ if _words.empty?
93
+ records = []
94
+ response.write(<<-EOS)
95
+ <div class='search-summary'>
96
+ <p>Rubyでgroonga使って全文検索</p>
97
+ </div>
98
+ EOS
99
+ else
100
+ offset = 0
101
+ options = {}
102
+ before = Time.now
103
+ records = @documents.select do |record|
104
+ expression = nil
105
+ _words.each do |word|
106
+ sub_expression = record["content"] =~ word
107
+ if expression.nil?
108
+ expression = sub_expression
109
+ else
110
+ expression &= sub_expression
111
+ end
112
+ end
113
+ expression
114
+ end
115
+ total_records = records.size
116
+ records = records.sort([[".:score", "descending"],
117
+ [".last-modified", "descending"]],
118
+ :limit => 20)
119
+ elapsed = Time.now - before
120
+
121
+ response.write(<<-EOS)
122
+ <div class='search-summary'>
123
+ <p>
124
+ <span class="keyword">#{escape_html(query(request))}</span>の検索結果:
125
+ <span class="total-entries">#{total_records}</span>件中
126
+ <span class="display-range">
127
+ #{total_records.zero? ? 0 : offset + 1}
128
+ -
129
+ #{offset + records.size}
130
+ </span>
131
+ 件(#{elapsed}秒)
132
+ </p>
133
+ </div>
134
+ EOS
135
+ end
136
+
137
+ response.write(" <div class='records'>\n")
138
+ records.each do |record|
139
+ render_record(request, response, record)
140
+ end
141
+ response.write(" </div>\n")
142
+ end
143
+
144
+ def render_record(request, response, record)
145
+ response.write(" <div class='record'>\n")
146
+ href = escape_html(record['.path'])
147
+ title = escape_html(record['.title'])
148
+ last_modified = escape_html(record['.last-modified'].iso8601)
149
+ score = record.score
150
+ response.write(" <h2><a href='#{href}'>#{title}</a>(#{score})</h2>\n")
151
+ render_snippet(request, response, record)
152
+ response.write(<<-EOM)
153
+ <p class="metadata">
154
+ <span class="url">#{unescape(href)}</span>
155
+ -
156
+ <span class="last-modified">#{last_modified}</span>
157
+ </p>
158
+ EOM
159
+ response.write(" </div>\n")
160
+ end
161
+
162
+ def render_snippet(request, response, record)
163
+ open_tag = "<span class=\"keyword\">"
164
+ close_tag = "</span>"
165
+ snippet = Groonga::Snippet.new(:width => 100,
166
+ :default_open_tag => open_tag,
167
+ :default_close_tag => close_tag,
168
+ :html_escape => true,
169
+ :normalize => true)
170
+ words(request).each do |word|
171
+ snippet.add_keyword(word)
172
+ end
173
+ separator = "\n<span class='separator'>...</span>\n"
174
+ response.write(<<-EOS)
175
+ <p class="snippet">
176
+ #{snippet.execute(record[".content"]).join(separator)}
177
+ </p>
178
+ EOS
179
+ end
180
+ end
181
+
182
+ run Searcher.new
@@ -0,0 +1,122 @@
1
+ body
2
+ {
3
+ background-color: #fff;
4
+ color: #000;
5
+ }
6
+
7
+ div.header
8
+ {
9
+ display: none;
10
+ }
11
+
12
+ div.search-box
13
+ {
14
+ width: 100%;
15
+ text-align: center;
16
+ }
17
+
18
+ div.re-search
19
+ {
20
+ }
21
+
22
+ div.search-summary
23
+ {
24
+ font-size: small;
25
+ border-top: 1px solid #fc3;
26
+ background-color: #ffd;
27
+ }
28
+
29
+ div.search-summary p
30
+ {
31
+ padding: 3px;
32
+ margin: 0;
33
+ }
34
+
35
+ div.records
36
+ {
37
+ font-size: smaller;
38
+ padding-top: 10px;
39
+ padding-left: 30px;
40
+ margin-right: 20%;
41
+ margin-bottom: 30px;
42
+ }
43
+
44
+ div.records h2
45
+ {
46
+ padding: 0;
47
+ margin-bottom: 5px;
48
+ }
49
+
50
+ div.records p
51
+ {
52
+ }
53
+
54
+ p.snippet
55
+ {
56
+ padding: 0;
57
+ margin: 0;
58
+ }
59
+
60
+ p.snippet span.separator
61
+ {
62
+ font-weight: bold;
63
+ }
64
+
65
+ span.keyword,
66
+ span.total-entries,
67
+ span.display-range
68
+ {
69
+ font-weight: bold;
70
+ }
71
+
72
+ div.pagination
73
+ {
74
+ text-align: center;
75
+ margin-bottom: 20px;
76
+ }
77
+
78
+ p.metadata
79
+ {
80
+ padding: 0;
81
+ margin: 0;
82
+ margin-top: 5px;
83
+ }
84
+
85
+ span.url
86
+ {
87
+ color: green;
88
+ }
89
+
90
+ pre
91
+ {
92
+ overflow: auto;
93
+ margin: 20px 30px;
94
+ padding: 10px;
95
+ border-top: 1px solid #fc3;
96
+ background-color: #ffd;
97
+ }
98
+
99
+ a img
100
+ {
101
+ border-style: none;
102
+ }
103
+
104
+ ul.sources li
105
+ {
106
+ display: inline;
107
+ }
108
+
109
+ div.footer
110
+ {
111
+ border-top: 1px solid #fc3;
112
+ background-color: #ffd;
113
+ margin-top: 5px;
114
+ text-align: right;
115
+ }
116
+
117
+ div.footer p
118
+ {
119
+ font-size: small;
120
+ padding: 0;
121
+ margin: 0;
122
+ }