rroonga 1.2.9 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +1 -0
- data/Rakefile +1 -0
- data/bin/grntest-log-analyze +123 -0
- data/bin/groonga-query-log-extract +117 -0
- data/ext/groonga/rb-grn-accessor.c +7 -5
- data/ext/groonga/rb-grn-array-cursor.c +1 -1
- data/ext/groonga/rb-grn-array.c +34 -44
- data/ext/groonga/rb-grn-column.c +74 -38
- data/ext/groonga/rb-grn-context.c +19 -15
- data/ext/groonga/rb-grn-database.c +47 -42
- data/ext/groonga/rb-grn-double-array-trie-cursor.c +40 -0
- data/ext/groonga/rb-grn-double-array-trie.c +530 -0
- data/ext/groonga/rb-grn-encoding-support.c +1 -1
- data/ext/groonga/rb-grn-encoding.c +1 -1
- data/ext/groonga/rb-grn-exception.c +1 -1
- data/ext/groonga/rb-grn-expression-builder.c +1 -1
- data/ext/groonga/rb-grn-expression.c +63 -51
- data/ext/groonga/rb-grn-fix-size-column.c +7 -7
- data/ext/groonga/rb-grn-hash-cursor.c +1 -1
- data/ext/groonga/rb-grn-hash.c +42 -39
- data/ext/groonga/rb-grn-index-column.c +35 -31
- data/ext/groonga/rb-grn-index-cursor.c +1 -1
- data/ext/groonga/rb-grn-logger.c +23 -18
- data/ext/groonga/rb-grn-object.c +40 -27
- data/ext/groonga/rb-grn-operator.c +1 -1
- data/ext/groonga/rb-grn-patricia-trie-cursor.c +1 -1
- data/ext/groonga/rb-grn-patricia-trie.c +122 -90
- data/ext/groonga/rb-grn-plugin.c +8 -7
- data/ext/groonga/rb-grn-posting.c +1 -1
- data/ext/groonga/rb-grn-procedure.c +1 -1
- data/ext/groonga/rb-grn-query.c +12 -12
- data/ext/groonga/rb-grn-record.c +1 -1
- data/ext/groonga/rb-grn-snippet.c +26 -19
- data/ext/groonga/rb-grn-table-cursor-key-support.c +1 -1
- data/ext/groonga/rb-grn-table-cursor.c +4 -3
- data/ext/groonga/rb-grn-table-key-support.c +23 -23
- data/ext/groonga/rb-grn-table.c +268 -153
- data/ext/groonga/rb-grn-type.c +11 -7
- data/ext/groonga/rb-grn-utils.c +4 -1
- data/ext/groonga/rb-grn-variable-size-column.c +1 -1
- data/ext/groonga/rb-grn-variable.c +2 -2
- data/ext/groonga/rb-grn-view-accessor.c +1 -1
- data/ext/groonga/rb-grn-view-cursor.c +1 -1
- data/ext/groonga/rb-grn-view-record.c +1 -1
- data/ext/groonga/rb-grn-view.c +43 -34
- data/ext/groonga/rb-grn.h +6 -2
- data/ext/groonga/rb-groonga.c +1 -1
- data/lib/groonga.rb +4 -2
- data/lib/groonga/context.rb +16 -41
- data/lib/groonga/dumper.rb +6 -4
- data/lib/groonga/expression-builder.rb +52 -26
- data/lib/groonga/grntest-log.rb +206 -0
- data/lib/groonga/pagination.rb +21 -19
- data/lib/groonga/patricia-trie.rb +7 -10
- data/lib/groonga/posting.rb +1 -1
- data/lib/groonga/query-log.rb +348 -0
- data/lib/groonga/record.rb +47 -143
- data/lib/groonga/schema.rb +679 -406
- data/lib/groonga/view-record.rb +4 -10
- data/rroonga-build.rb +1 -1
- data/test/test-array.rb +25 -4
- data/test/test-column.rb +8 -8
- data/test/test-database.rb +2 -3
- data/test/test-double-array-trie.rb +164 -0
- data/test/test-expression-builder.rb +2 -2
- data/test/test-expression.rb +10 -9
- data/test/test-gqtp.rb +2 -2
- data/test/test-hash.rb +32 -8
- data/test/test-patricia-trie.rb +34 -10
- data/test/test-query-log.rb +258 -0
- data/test/test-record.rb +6 -5
- data/test/test-schema-create-table.rb +8 -0
- data/test/test-schema.rb +491 -234
- data/test/test-table.rb +17 -24
- metadata +123 -100
- data/ext/groonga/Makefile +0 -233
@@ -17,22 +17,19 @@
|
|
17
17
|
|
18
18
|
module Groonga
|
19
19
|
class PatriciaTrie
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
# _text_を走査し、レコードのキーとマッチする部分文字列ごとに
|
24
|
-
# そのレコードが_record_として、その部分文字列が_word_として、
|
20
|
+
# _text_ を走査し、レコードのキーとマッチする部分文字列ごとに
|
21
|
+
# そのレコードが _record_ として、その部分文字列が _word_ として、
|
25
22
|
# ブロックが呼び出される。ブロックから返された文字列が元の部
|
26
23
|
# 分文字列と置換される。全てのヒットに対してのその置換処理が
|
27
24
|
# 行われた文字列が返される。
|
28
25
|
#
|
29
|
-
#
|
26
|
+
# @param options [::Hash] The name and value
|
27
|
+
# pairs. Omitted names are initialized as the default value.
|
28
|
+
# @option options [Proc] :other_text_handler The other_text_handler
|
30
29
|
#
|
31
|
-
#
|
32
|
-
# マッチした部分文字列の前後の文字列を変換するProcを指
|
33
|
-
# 定する。
|
30
|
+
# マッチした部分文字列の前後の文字列を変換するProcを指定する。
|
34
31
|
#
|
35
|
-
#
|
32
|
+
# @example
|
36
33
|
# include ERB::Util
|
37
34
|
# Groonga::Context.default_options = {:encoding => "utf-8"}
|
38
35
|
# words = Groonga::PatriciaTrie.create(:key_type => "ShortText",
|
data/lib/groonga/posting.rb
CHANGED
@@ -52,7 +52,7 @@ module Groonga
|
|
52
52
|
|
53
53
|
# Updates all values.
|
54
54
|
#
|
55
|
-
# @param [Hash] parameters The name and value
|
55
|
+
# @param [::Hash] parameters The name and value
|
56
56
|
# pairs. Omitted names are initialized as the default value.
|
57
57
|
# @option parameters [Integer] :record_id The record_id.
|
58
58
|
# @option parameters [Integer] :section_id The section_id.
|
@@ -0,0 +1,348 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# Copyright (C) 2011 Kouhei Sutou <kou@clear-code.com>
|
4
|
+
#
|
5
|
+
# This library is free software; you can redistribute it and/or
|
6
|
+
# modify it under the terms of the GNU Lesser General Public
|
7
|
+
# License version 2.1 as published by the Free Software Foundation.
|
8
|
+
#
|
9
|
+
# This library is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
12
|
+
# Lesser General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU Lesser General Public
|
15
|
+
# License along with this library; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
17
|
+
|
18
|
+
require "English"
|
19
|
+
require "shellwords"
|
20
|
+
require "cgi"
|
21
|
+
|
22
|
+
module Groonga
|
23
|
+
module QueryLog
|
24
|
+
class Command
|
25
|
+
class << self
|
26
|
+
@@registered_commands = {}
|
27
|
+
def register(name, klass)
|
28
|
+
@@registered_commands[name] = klass
|
29
|
+
end
|
30
|
+
|
31
|
+
def parse(input)
|
32
|
+
if input.start_with?("/d/")
|
33
|
+
parse_uri_path(input)
|
34
|
+
else
|
35
|
+
parse_command_line(input)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
def parse_uri_path(path)
|
41
|
+
name, parameters_string = path.split(/\?/, 2)
|
42
|
+
parameters = {}
|
43
|
+
if parameters_string
|
44
|
+
parameters_string.split(/&/).each do |parameter_string|
|
45
|
+
key, value = parameter_string.split(/\=/, 2)
|
46
|
+
parameters[key] = CGI.unescape(value)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
name = name.gsub(/\A\/d\//, '')
|
50
|
+
name, output_type = name.split(/\./, 2)
|
51
|
+
parameters["output_type"] = output_type if output_type
|
52
|
+
command_class = @@registered_commands[name] || self
|
53
|
+
command = command_class.new(name, parameters)
|
54
|
+
command.original_format = :uri
|
55
|
+
command
|
56
|
+
end
|
57
|
+
|
58
|
+
def parse_command_line(command_line)
|
59
|
+
name, *options = Shellwords.shellwords(command_line)
|
60
|
+
parameters = {}
|
61
|
+
options.each_slice(2) do |key, value|
|
62
|
+
parameters[key.gsub(/\A--/, '')] = value
|
63
|
+
end
|
64
|
+
command_class = @@registered_commands[name] || self
|
65
|
+
command = command_class.new(name, parameters)
|
66
|
+
command.original_format = :command
|
67
|
+
command
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
attr_reader :name, :parameters
|
72
|
+
attr_accessor :original_format
|
73
|
+
def initialize(name, parameters)
|
74
|
+
@name = name
|
75
|
+
@parameters = parameters
|
76
|
+
@original_format = nil
|
77
|
+
end
|
78
|
+
|
79
|
+
def ==(other)
|
80
|
+
other.is_a?(self.class) and
|
81
|
+
@name == other.name and
|
82
|
+
@parameters == other.parameters
|
83
|
+
end
|
84
|
+
|
85
|
+
def uri_format?
|
86
|
+
@original_format == :uri
|
87
|
+
end
|
88
|
+
|
89
|
+
def command_format?
|
90
|
+
@original_format == :command
|
91
|
+
end
|
92
|
+
|
93
|
+
def to_uri_format
|
94
|
+
path = "/d/#{@name}"
|
95
|
+
parameters = @parameters.dup
|
96
|
+
output_type = parameters.delete("output_type")
|
97
|
+
path << ".#{output_type}" if output_type
|
98
|
+
unless parameters.empty?
|
99
|
+
sorted_parameters = parameters.sort_by do |name, _|
|
100
|
+
name.to_s
|
101
|
+
end
|
102
|
+
uri_parameters = sorted_parameters.collect do |name, value|
|
103
|
+
"#{CGI.escape(name)}=#{CGI.escape(value)}"
|
104
|
+
end
|
105
|
+
path << "?"
|
106
|
+
path << uri_parameters.join("&")
|
107
|
+
end
|
108
|
+
path
|
109
|
+
end
|
110
|
+
|
111
|
+
def to_command_format
|
112
|
+
command_line = [@name]
|
113
|
+
sorted_parameters = @parameters.sort_by do |name, _|
|
114
|
+
name.to_s
|
115
|
+
end
|
116
|
+
sorted_parameters.each do |name, value|
|
117
|
+
escaped_value = value.gsub(/[\n"\\]/) do
|
118
|
+
special_character = $MATCH
|
119
|
+
case special_character
|
120
|
+
when "\n"
|
121
|
+
"\\n"
|
122
|
+
else
|
123
|
+
"\\#{special_character}"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
command_line << "--#{name}"
|
127
|
+
command_line << "\"#{escaped_value}\""
|
128
|
+
end
|
129
|
+
command_line.join(" ")
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class SelectCommand < Command
|
134
|
+
register("select", self)
|
135
|
+
|
136
|
+
def sortby
|
137
|
+
@parameters["sortby"]
|
138
|
+
end
|
139
|
+
|
140
|
+
def scorer
|
141
|
+
@parameters["scorer"]
|
142
|
+
end
|
143
|
+
|
144
|
+
def query
|
145
|
+
@parameters["query"]
|
146
|
+
end
|
147
|
+
|
148
|
+
def filter
|
149
|
+
@parameters["filter"]
|
150
|
+
end
|
151
|
+
|
152
|
+
def conditions
|
153
|
+
@conditions ||= filter.split(/(?:&&|&!|\|\|)/).collect do |condition|
|
154
|
+
condition = condition.strip
|
155
|
+
condition = condition.gsub(/\A[\s\(]*/, '')
|
156
|
+
condition = condition.gsub(/[\s\)]*\z/, '') unless /\(/ =~ condition
|
157
|
+
condition
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
def drilldowns
|
162
|
+
@drilldowns ||= (@parameters["drilldown"] || "").split(/\s*,\s*/)
|
163
|
+
end
|
164
|
+
|
165
|
+
def output_columns
|
166
|
+
@parameters["output_columns"]
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
class Statistic
|
171
|
+
attr_reader :context_id, :start_time, :raw_command
|
172
|
+
attr_reader :elapsed, :return_code
|
173
|
+
attr_accessor :slow_operation_threshold, :slow_response_threshold
|
174
|
+
def initialize(context_id)
|
175
|
+
@context_id = context_id
|
176
|
+
@start_time = nil
|
177
|
+
@command = nil
|
178
|
+
@raw_command = nil
|
179
|
+
@operations = []
|
180
|
+
@elapsed = nil
|
181
|
+
@return_code = 0
|
182
|
+
@slow_operation_threshold = 0.1
|
183
|
+
@slow_response_threshold = 0.2
|
184
|
+
end
|
185
|
+
|
186
|
+
def start(start_time, command)
|
187
|
+
@start_time = start_time
|
188
|
+
@raw_command = command
|
189
|
+
end
|
190
|
+
|
191
|
+
def finish(elapsed, return_code)
|
192
|
+
@elapsed = elapsed
|
193
|
+
@return_code = return_code
|
194
|
+
end
|
195
|
+
|
196
|
+
def command
|
197
|
+
@command ||= Command.parse(@raw_command)
|
198
|
+
end
|
199
|
+
|
200
|
+
def elapsed_in_seconds
|
201
|
+
nano_seconds_to_seconds(@elapsed)
|
202
|
+
end
|
203
|
+
|
204
|
+
def last_time
|
205
|
+
@start_time + elapsed_in_seconds
|
206
|
+
end
|
207
|
+
|
208
|
+
def slow?
|
209
|
+
elapsed_in_seconds >= @slow_response_threshold
|
210
|
+
end
|
211
|
+
|
212
|
+
def each_operation
|
213
|
+
previous_elapsed = 0
|
214
|
+
ensure_parse_command
|
215
|
+
operation_context_context = {
|
216
|
+
:filter_index => 0,
|
217
|
+
:drilldown_index => 0,
|
218
|
+
}
|
219
|
+
@operations.each_with_index do |operation, i|
|
220
|
+
relative_elapsed = operation[:elapsed] - previous_elapsed
|
221
|
+
relative_elapsed_in_seconds = nano_seconds_to_seconds(relative_elapsed)
|
222
|
+
previous_elapsed = operation[:elapsed]
|
223
|
+
parsed_operation = {
|
224
|
+
:i => i,
|
225
|
+
:elapsed => operation[:elapsed],
|
226
|
+
:elapsed_in_seconds => nano_seconds_to_seconds(operation[:elapsed]),
|
227
|
+
:relative_elapsed => relative_elapsed,
|
228
|
+
:relative_elapsed_in_seconds => relative_elapsed_in_seconds,
|
229
|
+
:name => operation[:name],
|
230
|
+
:context => operation_context(operation[:name],
|
231
|
+
operation_context_context),
|
232
|
+
:n_records => operation[:n_records],
|
233
|
+
:slow? => slow_operation?(relative_elapsed_in_seconds),
|
234
|
+
}
|
235
|
+
yield parsed_operation
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def add_operation(operation)
|
240
|
+
@operations << operation
|
241
|
+
end
|
242
|
+
|
243
|
+
def operations
|
244
|
+
_operations = []
|
245
|
+
each_operation do |operation|
|
246
|
+
_operations << operation
|
247
|
+
end
|
248
|
+
_operations
|
249
|
+
end
|
250
|
+
|
251
|
+
def select_command?
|
252
|
+
command.name == "select"
|
253
|
+
end
|
254
|
+
|
255
|
+
private
|
256
|
+
def nano_seconds_to_seconds(nano_seconds)
|
257
|
+
nano_seconds / 1000.0 / 1000.0 / 1000.0
|
258
|
+
end
|
259
|
+
|
260
|
+
def operation_context(label, context)
|
261
|
+
case label
|
262
|
+
when "filter"
|
263
|
+
if @select_command.query and context[:query_used].nil?
|
264
|
+
context[:query_used] = true
|
265
|
+
"query: #{@select_command.query}"
|
266
|
+
else
|
267
|
+
index = context[:filter_index]
|
268
|
+
context[:filter_index] += 1
|
269
|
+
@select_command.conditions[index]
|
270
|
+
end
|
271
|
+
when "sort"
|
272
|
+
@select_command.sortby
|
273
|
+
when "score"
|
274
|
+
@select_command.scorer
|
275
|
+
when "output"
|
276
|
+
@select_command.output_columns
|
277
|
+
when "drilldown"
|
278
|
+
index = context[:drilldown_index]
|
279
|
+
context[:drilldown_index] += 1
|
280
|
+
@select_command.drilldowns[index]
|
281
|
+
else
|
282
|
+
nil
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def ensure_parse_command
|
287
|
+
return unless select_command?
|
288
|
+
@select_command = SelectCommand.parse(@raw_command)
|
289
|
+
end
|
290
|
+
|
291
|
+
def slow_operation?(elapsed)
|
292
|
+
elapsed >= @slow_operation_threshold
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
class Parser
|
297
|
+
def initialize
|
298
|
+
end
|
299
|
+
|
300
|
+
def parse(input, &block)
|
301
|
+
current_statistics = {}
|
302
|
+
input.each_line do |line|
|
303
|
+
case line
|
304
|
+
when /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)\.(\d+)\|(.+?)\|([>:<])/
|
305
|
+
year, month, day, hour, minutes, seconds, micro_seconds =
|
306
|
+
$1, $2, $3, $4, $5, $6, $7
|
307
|
+
context_id = $8
|
308
|
+
type = $9
|
309
|
+
rest = $POSTMATCH.strip
|
310
|
+
time_stamp = Time.local(year, month, day, hour, minutes, seconds,
|
311
|
+
micro_seconds)
|
312
|
+
parse_line(current_statistics,
|
313
|
+
time_stamp, context_id, type, rest, &block)
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
private
|
319
|
+
def parse_line(current_statistics,
|
320
|
+
time_stamp, context_id, type, rest, &block)
|
321
|
+
case type
|
322
|
+
when ">"
|
323
|
+
statistic = Statistic.new(context_id)
|
324
|
+
statistic.start(time_stamp, rest)
|
325
|
+
current_statistics[context_id] = statistic
|
326
|
+
when ":"
|
327
|
+
return unless /\A(\d+) (.+)\((\d+)\)/ =~ rest
|
328
|
+
elapsed = $1
|
329
|
+
name = $2
|
330
|
+
n_records = $3.to_i
|
331
|
+
statistic = current_statistics[context_id]
|
332
|
+
return if statistic.nil?
|
333
|
+
statistic.add_operation(:name => name,
|
334
|
+
:elapsed => elapsed.to_i,
|
335
|
+
:n_records => n_records)
|
336
|
+
when "<"
|
337
|
+
return unless /\A(\d+) rc=(\d+)/ =~ rest
|
338
|
+
elapsed = $1
|
339
|
+
return_code = $2
|
340
|
+
statistic = current_statistics.delete(context_id)
|
341
|
+
return if statistic.nil?
|
342
|
+
statistic.finish(elapsed.to_i, return_code.to_i)
|
343
|
+
block.call(statistic)
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
data/lib/groonga/record.rb
CHANGED
@@ -21,7 +21,7 @@ module Groonga
|
|
21
21
|
class Record
|
22
22
|
# レコードが所属するテーブル
|
23
23
|
attr_reader :table
|
24
|
-
# _table_の_id_に対応するレコードを作成する。_values_には各
|
24
|
+
# _table_ の _id_ に対応するレコードを作成する。_values_ には各
|
25
25
|
# カラムに設定する値を以下のような形式で指定する。
|
26
26
|
#
|
27
27
|
# [
|
@@ -40,141 +40,96 @@ module Groonga
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
# _record_と_other_が同じテーブルに属していて、さらに、
|
47
|
-
# 同じレコードIDを持つなら+true+を返し、そうでなければ
|
48
|
-
# +false+を返す。
|
43
|
+
# _record_ と _other_ が同じテーブルに属していて、さらに、
|
44
|
+
# 同じレコードIDを持つなら +true+ を返し、そうでなければ
|
45
|
+
# +false+ を返す。
|
49
46
|
def ==(other)
|
50
47
|
self.class == other.class and
|
51
48
|
[table, id] == [other.table, other.id]
|
52
49
|
end
|
53
50
|
|
54
|
-
# call-seq:
|
55
|
-
# record.eql?(other) -> true/false
|
56
|
-
#
|
57
51
|
# Groonga::Record#==と同じ。
|
58
52
|
def eql?(other)
|
59
53
|
self == other
|
60
54
|
end
|
61
55
|
|
62
|
-
# call-seq:
|
63
|
-
# record.hash -> ハッシュ値
|
64
|
-
#
|
65
56
|
# 同じテーブルの同じIDのレコードに対しては常に同じハッシュ
|
66
57
|
# 値を返す。
|
67
58
|
def hash
|
68
59
|
@table.hash ^ @id.hash
|
69
60
|
end
|
70
61
|
|
71
|
-
#
|
72
|
-
# record[column_name] -> 値
|
73
|
-
#
|
74
|
-
# このレコードの_column_name_で指定されたカラムの値を返す。
|
62
|
+
# このレコードの _column_name_ で指定されたカラムの値を返す。
|
75
63
|
def [](column_name)
|
76
64
|
@table.column_value(@id, column_name, :id => true)
|
77
65
|
end
|
78
66
|
|
79
|
-
#
|
80
|
-
# record[column_name] = 値
|
81
|
-
#
|
82
|
-
# このレコードの_column_name_で指定されたカラムの値を設定す
|
67
|
+
# このレコードの _column_name_ で指定されたカラムの値を設定す
|
83
68
|
# る。
|
84
69
|
def []=(column_name, value)
|
85
70
|
@table.set_column_value(@id, column_name, value, :id => true)
|
86
71
|
end
|
87
72
|
|
88
|
-
#
|
89
|
-
#
|
90
|
-
#
|
91
|
-
# このレコードの_column_name_で指定されたカラムの値の最後に
|
92
|
-
# _value_を追加する。
|
73
|
+
# このレコードの _column_name_ で指定されたカラムの値の最後に
|
74
|
+
# _value_ を追加する。
|
93
75
|
def append(column_name, value)
|
94
76
|
column(column_name).append(@id, value)
|
95
77
|
end
|
96
78
|
|
97
|
-
#
|
98
|
-
#
|
99
|
-
#
|
100
|
-
# このレコードの_column_name_で指定されたカラムの値の最初に
|
101
|
-
# _value_を追加する。
|
79
|
+
# このレコードの _column_name_ で指定されたカラムの値の最初に
|
80
|
+
# _value_ を追加する。
|
102
81
|
def prepend(column_name, value)
|
103
82
|
column(column_name).prepend(@id, value)
|
104
83
|
end
|
105
84
|
|
106
|
-
#
|
107
|
-
#
|
108
|
-
#
|
109
|
-
# _record_が所属するテーブルで主キーを使える場合は+true+
|
110
|
-
# を返し、使えない場合は+false+を返す。
|
85
|
+
# _record_ が所属するテーブルで主キーを使える場合は +true+
|
86
|
+
# を返し、使えない場合は +false+ を返す。
|
111
87
|
def support_key?
|
112
88
|
@table.support_key?
|
113
89
|
end
|
114
90
|
|
115
|
-
#
|
116
|
-
#
|
117
|
-
#
|
118
|
-
# 名前が_name_のカラムがレコードの所属するテーブルで定義され
|
119
|
-
# ているなら+true+を返す。
|
91
|
+
# 名前が _name_ のカラムがレコードの所属するテーブルで定義され
|
92
|
+
# ているなら +true+ を返す。
|
120
93
|
def have_column?(name)
|
121
94
|
not @table.column(normalize_column_name(name)).nil?
|
122
95
|
end
|
123
96
|
|
124
|
-
#
|
125
|
-
# record.reference_column?(name) -> true/false
|
126
|
-
#
|
127
|
-
# 名前が_name_のカラムが参照カラムであるなら+true+を返す。
|
97
|
+
# 名前が _name_ のカラムが参照カラムであるなら +true+ を返す。
|
128
98
|
def reference_column?(name)
|
129
99
|
column(name).reference?
|
130
100
|
end
|
131
101
|
|
132
|
-
#
|
133
|
-
#
|
134
|
-
#
|
135
|
-
# 名前が_name_のカラムが索引カラム
|
136
|
-
# (Groonga::IndexColumn)であるなら+true+を返す。
|
102
|
+
# 名前が _name_ のカラムが索引カラム
|
103
|
+
# (Groonga::IndexColumn)であるなら +true+ を返す。
|
137
104
|
def index_column?(name)
|
138
105
|
column(name).index?
|
139
106
|
end
|
140
107
|
|
141
|
-
#
|
142
|
-
# record.vector_column?(name) -> true/false
|
143
|
-
#
|
144
|
-
# 名前が_name_のカラムの値がベクターであるなら+true+を返す。
|
108
|
+
# 名前が _name_ のカラムの値がベクターであるなら +true+ を返す。
|
145
109
|
#
|
146
110
|
# @since: 1.0.5
|
147
111
|
def vector_column?(name)
|
148
112
|
column(name).vector?
|
149
113
|
end
|
150
114
|
|
151
|
-
#
|
152
|
-
# record.scalar_column?(name) -> true/false
|
153
|
-
#
|
154
|
-
# 名前が_name_のカラムの値がスカラーであるなら+true+を返す。
|
115
|
+
# 名前が _name_ のカラムの値がスカラーであるなら +true+ を返す。
|
155
116
|
#
|
156
117
|
# @since: 1.0.5
|
157
118
|
def scalar_column?(name)
|
158
119
|
column(name).scalar?
|
159
120
|
end
|
160
121
|
|
161
|
-
#
|
162
|
-
#
|
163
|
-
#
|
164
|
-
# 名前が_name_のGroonga::IndexColumnのsearchメソッドを呼ぶ。
|
165
|
-
# _query_と_options_はそのメソッドにそのまま渡される。詳しく
|
122
|
+
# 名前が _name_ のGroonga::IndexColumnの search メソッドを呼ぶ。
|
123
|
+
# _query_ と _options_ はそのメソッドにそのまま渡される。詳しく
|
166
124
|
# はGroonga::IndexColumn#searchを参照。
|
167
125
|
def search(name, query, options={})
|
168
126
|
column(name).search(query, options)
|
169
127
|
end
|
170
128
|
|
171
|
-
# call-seq:
|
172
|
-
# record.key -> 主キー
|
173
|
-
#
|
174
129
|
# レコードの主キーを返す。
|
175
130
|
#
|
176
|
-
# _record_が所属するテーブルがGroonga:::Arrayの場合は常
|
177
|
-
#
|
131
|
+
# _record_ が所属するテーブルがGroonga:::Arrayの場合は常
|
132
|
+
# に +nil+ を返す。
|
178
133
|
def key
|
179
134
|
if support_key?
|
180
135
|
@key ||= @table.key(@id)
|
@@ -182,13 +137,9 @@ module Groonga
|
|
182
137
|
nil
|
183
138
|
end
|
184
139
|
end
|
185
|
-
|
186
|
-
# call-seq:
|
187
|
-
# record.record_id -> IDまたは主キー
|
188
|
-
#
|
189
140
|
# レコードを一意に識別するための情報を返す。
|
190
141
|
#
|
191
|
-
# _record_が所属するテーブルがGroonga:::Arrayの場合はID
|
142
|
+
# _record_ が所属するテーブルがGroonga:::Arrayの場合はID
|
192
143
|
# を返し、それ以外の場合は主キーを返す。
|
193
144
|
def record_id
|
194
145
|
if support_key?
|
@@ -198,36 +149,24 @@ module Groonga
|
|
198
149
|
end
|
199
150
|
end
|
200
151
|
|
201
|
-
# call-seq:
|
202
|
-
# record.record_raw_id -> ID
|
203
|
-
#
|
204
152
|
# レコードのIDを返す。
|
205
153
|
def record_raw_id
|
206
154
|
@id
|
207
155
|
end
|
208
156
|
alias_method :id, :record_raw_id
|
209
157
|
|
210
|
-
# call-seq:
|
211
|
-
# record.score -> スコア値
|
212
|
-
#
|
213
158
|
# レコードのスコア値を返す。検索結果として生成されたテーブル
|
214
159
|
# のみに定義される。
|
215
160
|
def score
|
216
161
|
self["_score"]
|
217
162
|
end
|
218
163
|
|
219
|
-
#
|
220
|
-
# record.support_score? -> true/false
|
221
|
-
#
|
222
|
-
# Groonga::Record#scoreが利用できる場合はtrueを
|
164
|
+
# Groonga::Record#scoreが利用できる場合は +true+ を
|
223
165
|
# 返す。
|
224
166
|
def support_score?
|
225
167
|
@table.have_column?("_score") # TODO delegate to Table
|
226
168
|
end
|
227
169
|
|
228
|
-
# call-seq:
|
229
|
-
# record.n_sub_records -> 件数
|
230
|
-
#
|
231
170
|
# 主キーの値が同一であったレコードの件数を返す。検索結果とし
|
232
171
|
# て生成されたテーブルのみに定義される。
|
233
172
|
#
|
@@ -237,60 +176,39 @@ module Groonga
|
|
237
176
|
self["_nsubrecs"]
|
238
177
|
end
|
239
178
|
|
240
|
-
#
|
241
|
-
# record.support_sub_records? -> true/false
|
242
|
-
#
|
243
|
-
# Groonga::Record#n_sub_recordsが利用できる場合はtrueを
|
179
|
+
# Groonga::Record#n_sub_recordsが利用できる場合は +true+ を
|
244
180
|
# 返す。
|
245
181
|
def support_sub_records?
|
246
182
|
@table.support_sub_records?
|
247
183
|
end
|
248
184
|
|
249
|
-
# call-seq:
|
250
|
-
# record.value -> 値
|
251
|
-
#
|
252
185
|
# レコードの値を返す。
|
253
186
|
def value
|
254
187
|
@table.value(@id, :id => true)
|
255
188
|
end
|
256
189
|
|
257
|
-
# call-seq:
|
258
|
-
# record.value = 値
|
259
|
-
#
|
260
190
|
# レコードの値を設定する。既存の値は上書きされる。
|
261
191
|
def value=(value)
|
262
192
|
@table.set_value(@id, value, :id => true)
|
263
193
|
end
|
264
194
|
|
265
|
-
#
|
266
|
-
#
|
267
|
-
#
|
268
|
-
# このレコードの_name_で指定されたカラムの値を_delta_だけ増
|
269
|
-
# 加する。_delta_が+nil+の場合は1増加する。
|
195
|
+
# このレコードの _name_ で指定されたカラムの値を _delta_ だけ増
|
196
|
+
# 加する。 _delta_ が +nil+ の場合は1増加する。
|
270
197
|
def increment!(name, delta=nil)
|
271
198
|
column(name).increment!(@id, delta)
|
272
199
|
end
|
273
200
|
|
274
|
-
#
|
275
|
-
#
|
276
|
-
#
|
277
|
-
# このレコードの_name_で指定されたカラムの値を_delta_だけ減
|
278
|
-
# 少する。_delta_が+nil+の場合は1減少する。
|
201
|
+
# このレコードの _name_ で指定されたカラムの値を _delta_ だけ減
|
202
|
+
# 少する。 _delta_ が +nil+ の場合は1減少する。
|
279
203
|
def decrement!(name, delta=nil)
|
280
204
|
column(name).decrement!(@id, delta)
|
281
205
|
end
|
282
206
|
|
283
|
-
# call-seq:
|
284
|
-
# record.columns -> Groonga::Columnの配列
|
285
|
-
#
|
286
207
|
# レコードが所属するテーブルの全てのカラムを返す。
|
287
208
|
def columns
|
288
209
|
@table.columns
|
289
210
|
end
|
290
211
|
|
291
|
-
# call-seq:
|
292
|
-
# attributes -> Hash
|
293
|
-
#
|
294
212
|
# レコードが所属しているテーブルで定義されているインデックス
|
295
213
|
# 型のカラムでない全カラムを対象とし、カラムの名前をキーとし
|
296
214
|
# たこのレコードのカラムの値のハッシュを返す。
|
@@ -301,36 +219,26 @@ module Groonga
|
|
301
219
|
accessor.build
|
302
220
|
end
|
303
221
|
|
304
|
-
# call-seq:
|
305
|
-
# record.delete
|
306
|
-
#
|
307
222
|
# レコードを削除する。
|
308
223
|
def delete
|
309
224
|
@table.delete(@id)
|
310
225
|
end
|
311
226
|
|
312
|
-
# call-seq:
|
313
|
-
# record.lock(options={})
|
314
|
-
# record.lock(options={}) {...}
|
315
|
-
#
|
316
227
|
# レコードが所属するテーブルをロックする。ロックに失敗した場
|
317
228
|
# 合はGroonga::ResourceDeadlockAvoided例外が発生する。
|
318
229
|
#
|
319
230
|
# ブロックを指定した場合はブロックを抜けたときにunlockする。
|
320
231
|
#
|
321
|
-
#
|
322
|
-
#
|
323
|
-
#
|
324
|
-
#
|
325
|
-
#
|
326
|
-
#
|
232
|
+
# 利用可能な _option_ は以下の通り。
|
233
|
+
# @param [Hash] options The name and value
|
234
|
+
# pairs. Omitted names are initialized as the default value.
|
235
|
+
# @option options [Integer] :timeout The timeout
|
236
|
+
# ロックを獲得できなかった場合は _:timeout_ 秒間ロックの獲得を試みる。
|
237
|
+
# _:timeout_ 秒以内にロックを獲得できなかった場合は例外が発生する。
|
327
238
|
def lock(options={}, &block)
|
328
239
|
@table.lock(options.merge(:id => @id), &block)
|
329
240
|
end
|
330
241
|
|
331
|
-
# call-seq:
|
332
|
-
# record.unlock(options={})
|
333
|
-
#
|
334
242
|
# レコードが所属するテーブルのロックを解除する。
|
335
243
|
#
|
336
244
|
# 利用可能なオプションは現在は無い。
|
@@ -338,9 +246,6 @@ module Groonga
|
|
338
246
|
@table.unlock(options.merge(:id => @id))
|
339
247
|
end
|
340
248
|
|
341
|
-
# call-seq:
|
342
|
-
# record.clear_lock(options={})
|
343
|
-
#
|
344
249
|
# レコードが所属するテーブルのロックを強制的に解除する。
|
345
250
|
#
|
346
251
|
# 利用可能なオプションは現在は無い。
|
@@ -348,25 +253,20 @@ module Groonga
|
|
348
253
|
@table.clear_lock(options.merge(:id => @id))
|
349
254
|
end
|
350
255
|
|
351
|
-
#
|
352
|
-
# record.locked?(options={}) -> true/false
|
353
|
-
#
|
354
|
-
# レコードが所属するテーブルがロックされていれば+true+を返す。
|
256
|
+
# レコードが所属するテーブルがロックされていれば +true+ を返す。
|
355
257
|
#
|
356
258
|
# 利用可能なオプションは現在は無い。
|
357
259
|
def locked?(options={})
|
358
260
|
@table.locked?(options.merge(:id => @id))
|
359
261
|
end
|
360
262
|
|
361
|
-
#
|
362
|
-
# record.valid_id? -> true/false
|
363
|
-
#
|
364
|
-
# レコードが持つIDが有効なIDであれば+true+を返す。
|
263
|
+
# レコードが持つIDが有効なIDであれば +true+ を返す。
|
365
264
|
def valid_id?
|
366
265
|
@table.exist?(@id)
|
367
266
|
end
|
368
267
|
|
369
|
-
|
268
|
+
# @private
|
269
|
+
def methods(include_inherited=true)
|
370
270
|
_methods = super
|
371
271
|
return _methods unless include_inherited
|
372
272
|
columns.each do |column|
|
@@ -377,7 +277,8 @@ module Groonga
|
|
377
277
|
_methods
|
378
278
|
end
|
379
279
|
|
380
|
-
|
280
|
+
# @private
|
281
|
+
def respond_to?(name)
|
381
282
|
super or !@table.column(name.to_s.sub(/=\z/, '')).nil?
|
382
283
|
end
|
383
284
|
|
@@ -395,7 +296,8 @@ module Groonga
|
|
395
296
|
name.to_s
|
396
297
|
end
|
397
298
|
|
398
|
-
|
299
|
+
# @private
|
300
|
+
def column(name)
|
399
301
|
_column = @table.column(normalize_column_name(name))
|
400
302
|
raise NoSuchColumn, "column(#{name.inspect}) is nil" if _column.nil?
|
401
303
|
_column
|
@@ -421,7 +323,8 @@ module Groonga
|
|
421
323
|
end
|
422
324
|
end
|
423
325
|
|
424
|
-
|
326
|
+
# @private
|
327
|
+
class AttributeHashBuilder
|
425
328
|
attr_reader :attributes
|
426
329
|
|
427
330
|
def initialize(root_record)
|
@@ -510,3 +413,4 @@ module Groonga
|
|
510
413
|
end
|
511
414
|
end
|
512
415
|
end
|
416
|
+
|