Kouhei Sutou 2019-04-26 15:43:52 +0900 (Fri, 26 Apr 2019) Revision: ceaabf6e8a48b4b9bcd811b3e6ecee5816cccec8 https://github.com/groonga/groonga/commit/ceaabf6e8a48b4b9bcd811b3e6ecee5816cccec8 Message: logical_range_filter: add support for window function over shards Removed files: test/command/suite/sharding/logical_range_filter/columns/stage/filtered/window_function/shards.reject test/command/suite/sharding/logical_range_filter/columns/stage/initial/window_function/shards.reject Modified files: plugins/sharding/dynamic_columns.rb plugins/sharding/logical_range_filter.rb test/command/suite/sharding/logical_range_filter/columns/stage/filtered/window_function/shards.test test/command/suite/sharding/logical_range_filter/columns/stage/initial/range.expected test/command/suite/sharding/logical_range_filter/columns/stage/initial/window_function/shards.test Modified: plugins/sharding/dynamic_columns.rb (+34 -21) =================================================================== --- plugins/sharding/dynamic_columns.rb 2019-04-26 15:43:03 +0900 (18cc3a8bf) +++ plugins/sharding/dynamic_columns.rb 2019-04-26 15:43:52 +0900 (004c6527e) @@ -1,6 +1,8 @@ module Groonga module Sharding class DynamicColumns + include Enumerable + class << self def parse(input) initial_contexts = {} @@ -48,6 +50,12 @@ module Groonga not @output_contexts.empty? end + def have_window_function? + any? do |context| + context.window_function? + end + end + def each_initial(&block) @initial_contexts.tsort_each(&block) end @@ -66,16 +74,16 @@ module Groonga each_output(&block) end - def apply_initial(targets) - apply(@initial_contexts, targets) + def apply_initial(targets, options={}) + apply(@initial_contexts, targets, options) end - def apply_filtered(targets) - apply(@filtered_contexts, targets) + def apply_filtered(targets, options={}) + apply(@filtered_contexts, targets, options) end - def apply_output(targets) - apply(@output_contexts, targets) + def apply_output(targets, options={}) + apply(@output_contexts, targets, options) end def empty? @@ -105,7 +113,7 @@ module Groonga end private - def apply(contexts, targets) + def apply(contexts, targets, options) window_function_contexts = [] normal_contexts = [] contexts.each do |context| @@ -116,13 +124,17 @@ module Groonga end end - targets.each do |result_set, options| - normal_contexts.each do |context| - context.apply(result_set, options) + if options.fetch(:normal, true) + targets.each do |result_set, options| + normal_contexts.each do |context| + context.apply(result_set, options) + end end end - window_function_contexts.each do |context| - context.apply_window_function(targets) + if options.fetch(:window_function, true) + window_function_contexts.each do |context| + context.apply_window_function(targets) + end end end end @@ -196,11 +208,8 @@ module Groonga end def apply(table, options=nil) - if context_target?(options) - column = table.find_column(@label) - else - column = table.create_column(@label, @flags, @type) - end + return if table.find_column(@label) + column = table.create_column(@label, @flags, @type) return if table.empty? condition = nil @@ -223,11 +232,15 @@ module Groonga executor.group_keys = @window_group_keys.join(", ") executor.output_column_name = @label targets.each do |table, options| - unless context_target?(options) - table.create_column(@label, @flags, @type) - end + is_context_table = context_target?(options) + column = table.find_column(@label) + column ||= table.create_column(@label, @flags, @type) next if table.empty? - executor.add_table(table) + if is_context_table + executor.add_context_table(table) + else + executor.add_table(table) + end end executor.execute ensure Modified: plugins/sharding/logical_range_filter.rb (+237 -114) =================================================================== --- plugins/sharding/logical_range_filter.rb 2019-04-26 15:43:03 +0900 (80a0850ae) +++ plugins/sharding/logical_range_filter.rb 2019-04-26 15:43:52 +0900 (a80b7825f) @@ -97,6 +97,7 @@ module Groonga attr_reader :result_sets attr_reader :temporary_tables attr_reader :threshold + attr_reader :time_classify_types def initialize(input) @input = input @use_range_index = parse_use_range_index(@input[:use_range_index]) @@ -117,6 +118,8 @@ module Groonga @temporary_tables = [] @threshold = compute_threshold + + @time_classify_types = detect_time_classify_types end def close @@ -125,6 +128,12 @@ module Groonga end end + def need_look_ahead? + return false unless @dynamic_columns.have_window_function? + return false unless @time_classify_types.empty? + true + end + private def parse_use_range_index(use_range_index) case use_range_index @@ -159,6 +168,33 @@ module Groonga default_threshold = 0.2 (threshold_env || default_threshold).to_f end + + def detect_time_classify_types + window_group_keys = [] + @dynamic_columns.each_filtered do |dynamic_column| + window_group_keys.concat(dynamic_column.window_group_keys) + end + return [] if window_group_keys.empty? + + types = [] + @dynamic_columns.each do |dynamic_column| + next unless window_group_keys.include?(dynamic_column.label) + case dynamic_column.value.strip + when /\Atime_classify_(.+?)\s*\(\s*([a-zA-Z\d_]+)\s*[,)]/ + type = $1 + column = $2 + next if column !=****@enume*****_key_name + + case type + when "minute", "second" + types << type + when "day", "hour" + types << type + end + end + end + types + end end class Executor @@ -168,20 +204,13 @@ module Groonga def execute first_shard = nil - enumerator =****@conte***** - target_range = enumerator.target_range - if****@conte***** == :descending - each_method = :reverse_each - else - each_method = :each - end - enumerator.send(each_method) do |shard, shard_range| - first_shard ||= shard - shard_executor = ShardExecutor.new(@context, shard, shard_range) + each_shard_executor do |shard_executor| + first_shard ||= shard_executor.shard shard_executor.execute break if****@conte*****_limit.zero? end if first_shard.nil? + enumerator =****@conte***** message = "[logical_range_filter] no shard exists: " + "logical_table: <#{enumerator.logical_table}>: " + @@ -198,6 +227,36 @@ module Groonga @context.result_sets << result_set end end + + private + def each_shard_executor(&block) + enumerator =****@conte***** + target_range = enumerator.target_range + if****@conte***** == :descending + each_method = :reverse_each + else + each_method = :each + end + if****@conte*****_look_ahead? + executors = [] + previous_executor = nil + enumerator.send(each_method) do |shard, shard_range| + current_executor = ShardExecutor.new(@context, shard, shard_range) + if previous_executor + previous_executor.next_executor = current_executor + current_executor.previous_executor = previous_executor + executors << previous_executor + end + previous_executor = current_executor + end + executors << previous_executor if previous_executor + executors.each(&block) + else + enumerator.send(each_method) do |shard, shard_range| + yield(ShardExecutor.new(@context, shard, shard_range)) + end + end + end end class Window @@ -435,72 +494,137 @@ module Groonga end class ShardExecutor + attr_reader :shard + attr_writer :previous_executor + attr_writer :next_executor def initialize(context, shard, shard_range) @context = context @shard = shard @shard_range = shard_range + @shard_key = shard.key @target_table =****@shard***** @filter =****@conte***** @post_filter =****@conte*****_filter @result_sets =****@conte*****_sets + @filtered_result_sets = [] @temporary_tables =****@conte*****_tables @target_range =****@conte*****_range @cover_type = @target_range.cover_type(@shard_range) + + @previous_executor = nil + @next_executor = nil + + @initital_table = nil + + @range_index = nil + @window = nil + + @prepared = false + @filtered = false end def execute - return if @cover_type == :none - return if @target_table.empty? + ensure_filtered + + return if @filtered_result_sets.empty? + + if @window + @filtered_result_sets.each do |result_set| + @window.each(result_set) do |windowed_result_set| + @temporary_tables << windowed_result_set + if****@conte*****_columns.have_filtered? + apply_targets = [[windowed_result_set]] + @context.dynamic_columns.apply_filtered(apply_targets) + end + sort_result_set(windowed_result_set) + return if****@conte*****_limit.zero? + end + end + else + apply_targets = [] + if @previous_executor + @previous_executor.add_filtered_stage_context(apply_targets) + end + @filtered_result_sets.each do |result_set| + apply_targets << [result_set] + end + if @next_executor + @next_executor.add_filtered_stage_context(apply_targets) + end + options = {} + if****@conte*****_look_ahead? + options[:normal] = false + end + @context.dynamic_columns.apply_filtered(apply_targets, options) + @filtered_result_sets.each do |result_set| + sort_result_set(result_set) + end + end + end + + def add_initial_stage_context(apply_targets) + ensure_prepared + return unless @initial_table + apply_targets << [@initial_table, {context: true}] + end + + def add_filtered_stage_context(apply_targets) + ensure_filtered + @filtered_result_sets.each do |table| + apply_targets << [table, {context: true}] + end + end - shard_key =****@shard***** - if shard_key.nil? + private + def have_record? + return false if @cover_type == :none + return false if @target_table.empty? + true + end + + def ensure_prepared + return if @prepared + @prepared = true + + return unless have_record? + + if @shard_key.nil? message = "[logical_range_filter] shard_key doesn't exist: " + "<#{@shard.key_name}>" raise InvalidArgument, message end - expression_builder = RangeExpressionBuilder.new(shard_key, - @target_range) - expression_builder.filter = @filter - - index_info = shard_key.find_index(Operator::LESS) - if index_info - range_index = index_info.index - unless use_range_index?(range_index, expression_builder) - range_index = nil - end - else - range_index = nil + @expression_builder = RangeExpressionBuilder.new(@shard_key, + @target_range) + @expression_builder.filter = @filter + index_info = @shard_key.find_index(Operator::LESS) + if index_info and use_range_index? + @range_index = index_info.index end if****@conte*****_columns.have_initial? - if @target_table ==****@shard***** - if @cover_type == :all - @target_table = @target_table.select_all - else - expression_builder.filter = nil - @target_table = create_expression(@target_table) do |expression| - expression_builder.build(expression, @shard_range) - @target_table.select(expression) - end - @cover_type = :all - expression_builder.filter = @filter + if @cover_type == :all + @target_table = @target_table.select_all + else + @expression_builder.filter = nil + @target_table = create_expression(@target_table) do |expression| + @expression_builder.build(expression, @shard_range) + @target_table.select(expression) end - @temporary_tables << @target_table + @expression_builder.filter = @filter + @cover_type = :all end - apply_targets = [] - apply_targets << [@target_table] - @context.dynamic_columns.apply_initial(apply_targets) + @temporary_tables << @target_table + @initial_table = @target_table end - execute_filter(range_index, expression_builder) + @window = detect_window end - private def decide_use_range_index(use, reason, line, method) message = "[logical_range_filter]" if use @@ -519,7 +643,7 @@ module Groonga use end - def use_range_index?(range_index, expression_builder) + def use_range_index? use_range_index_parameter_message = "force by use_range_index parameter" case****@conte*****_range_index @@ -595,7 +719,7 @@ module Groonga when :all if @filter create_expression(table) do |expression| - expression_builder.build_all(expression) + @expression_builder.build_all(expression) unless range_index_available_expression?(expression, __LINE__, __method__) return false @@ -607,7 +731,7 @@ module Groonga end when :partial_min create_expression(table) do |expression| - expression_builder.build_partial_min(expression) + @expression_builder.build_partial_min(expression) unless range_index_available_expression?(expression, __LINE__, __method__) return false @@ -616,7 +740,7 @@ module Groonga end when :partial_max create_expression(table) do |expression| - expression_builder.build_partial_max(expression) + @expression_builder.build_partial_max(expression) unless range_index_available_expression?(expression, __LINE__, __method__) return false @@ -625,7 +749,7 @@ module Groonga end when :partial_min_and_max create_expression(table) do |expression| - expression_builder.build_partial_min_and_max(expression) + @expression_builder.build_partial_min_and_max(expression) unless range_index_available_expression?(expression, __LINE__, __method__) return false @@ -698,65 +822,102 @@ module Groonga nil end - def execute_filter(range_index, expression_builder) + def ensure_filtered + return if @filtered + + @filtered = true + + ensure_prepared + return unless have_record? + + if****@conte*****_columns.have_initial? + apply_targets = [] + if @previous_executor + @previous_executor.add_initial_stage_context(apply_targets) + end + apply_targets << [@target_table] + if @next_executor + @next_executor.add_initial_stage_context(apply_targets) + end + @context.dynamic_columns.apply_initial(apply_targets) + end + + execute_filter(@range_index) + + return unles****@conte*****_look_ahead? + + apply_targets = [] + @filtered_result_sets = @filtered_result_sets.collect do |result_set| + if result_set ==****@shard***** + result_set = result_set.select_all + @temporary_tables << result_set + end + apply_targets << [result_set] + result_set + end + @context.dynamic_columns.apply_filtered(apply_targets, + window_function: false) + end + + def execute_filter(range_index) case @cover_type when :all - filter_shard_all(range_index, expression_builder) + filter_shard_all(range_index) when :partial_min if range_index - filter_by_range(range_index, expression_builder, + filter_by_range(range_index, @target_range.min, @target_range.min_border, nil, nil) else filter_table do |expression| - expression_builder.build_partial_min(expression) + @expression_builder.build_partial_min(expression) end end when :partial_max if range_index - filter_by_range(range_index, expression_builder, + filter_by_range(range_index, nil, nil, @target_range.max, @target_range.max_border) else filter_table do |expression| - expression_builder.build_partial_max(expression) + @expression_builder.build_partial_max(expression) end end when :partial_min_and_max if range_index - filter_by_range(range_index, expression_builder, + filter_by_range(range_index, @target_range.min, @target_range.min_border, @target_range.max, @target_range.max_border) else filter_table do |expression| - expression_builder.build_partial_min_and_max(expression) + @expression_builder.build_partial_min_and_max(expression) end end end end - def filter_shard_all(range_index, expression_builder) + def filter_shard_all(range_index) table = @target_table if****@filte*****? if @post_filter.nil? and table.size <=****@conte*****_offset @context.current_offset -= table.size return end - if range_index - filter_by_range(range_index, expression_builder, + if @range_index + filter_by_range(range_index, nil, nil, nil, nil) else - add_result_set(table) + add_filtered_result_set(table) end else - if range_index - filter_by_range(range_index, expression_builder, + if @range_index + filter_by_range(range_index, nil, nil, nil, nil) else filter_table do |expression| - expression_builder.build_all(expression) + @expression_builder.build_all(expression) end end end @@ -771,8 +932,7 @@ module Groonga end end - def filter_by_range(range_index, expression_builder, - min, min_border, max, max_border) + def filter_by_range(range_index, min, min_border, max, max_border) lexicon = range_index.domain data_table = range_index.range flags = build_range_search_flags(min_border, max_border) @@ -819,7 +979,7 @@ module Groonga decide_use_range_index(false, fallback_message, __LINE__, __method__) - execute_filter(nil, expression_builder) + execute_filter(nil) return end end @@ -886,7 +1046,7 @@ module Groonga yield(expression) result_set = table.select(expression) @temporary_tables << result_set - add_result_set(result_set) + add_filtered_result_set(result_set) end end @@ -897,34 +1057,12 @@ module Groonga end end - def add_result_set(result_set) - if result_set.empty? - return - end - - window = detect_window - if window - window.each(result_set) do |windowed_result_set| - @temporary_tables << windowed_result_set - sort_result_set(windowed_result_set) - break if****@conte*****_limit.zero? - end - else - sort_result_set(result_set) - end + def add_filtered_result_set(result_set) + return if result_set.empty? + @filtered_result_sets << result_set end def sort_result_set(result_set) - if****@conte*****_columns.have_filtered? - if result_set ==****@shard***** - result_set = result_set.select_all - @temporary_tables << result_set - end - apply_targets = [] - apply_targets << [result_set] - @context.dynamic_columns.apply_filtered(apply_targets) - end - unless @post_filter.nil? result_set = apply_post_filter(result_set) @temporary_tables << result_set @@ -973,30 +1111,15 @@ module Groonga end def detect_window - window_group_keys = [] - @context.dynamic_columns.each_filtered do |dynamic_column| - window_group_keys.concat(dynamic_column.window_group_keys) - end - return nil if window_group_keys.empty? - # TODO: return nil if result_set is small enough - windows = [] - @context.dynamic_columns.each do |dynamic_column| - next unless window_group_keys.include?(dynamic_column.label) - case dynamic_column.value.strip - when /\Atime_classify_(.+?)\s*\(\s*([a-zA-Z\d_]+)/ - type = $1 - column = $2 - next if column !=****@conte*****_key_name - - case type - when "minute", "second" - windows << Window.new(@context, @shard, @shard_range, :hour, 1) - when "day", "hour" - unless @shard_range.is_a?(LogicalEnumerator::DayShardRange) - windows << Window.new(@context, @shard, @shard_range, :day, 1) - end + @context.time_classify_types.each do |type| + case type + when "minute", "second" + windows << Window.new(@context, @shard, @shard_range, :hour, 1) + when "day", "hour" + unless @shard_range.is_a?(LogicalEnumerator::DayShardRange) + windows << Window.new(@context, @shard, @shard_range, :day, 1) end end end Deleted: test/command/suite/sharding/logical_range_filter/columns/stage/filtered/window_function/shards.reject (+0 -111) 100644 =================================================================== --- test/command/suite/sharding/logical_range_filter/columns/stage/filtered/window_function/shards.reject 2019-04-26 15:43:03 +0900 (80af6d986) +++ /dev/null @@ -1,111 +0,0 @@ -plugin_register sharding -[[0,0.0,0.0],true] -plugin_register functions/time -[[0,0.0,0.0],true] -table_create Logs_20170315 TABLE_NO_KEY -[[0,0.0,0.0],true] -column_create Logs_20170315 timestamp COLUMN_SCALAR Time -[[0,0.0,0.0],true] -column_create Logs_20170315 price COLUMN_SCALAR UInt32 -[[0,0.0,0.0],true] -table_create Logs_20170316 TABLE_NO_KEY -[[0,0.0,0.0],true] -column_create Logs_20170316 timestamp COLUMN_SCALAR Time -[[0,0.0,0.0],true] -column_create Logs_20170316 price COLUMN_SCALAR UInt32 -[[0,0.0,0.0],true] -table_create Logs_20170317 TABLE_NO_KEY -[[0,0.0,0.0],true] -column_create Logs_20170317 timestamp COLUMN_SCALAR Time -[[0,0.0,0.0],true] -column_create Logs_20170317 price COLUMN_SCALAR UInt32 -[[0,0.0,0.0],true] -load --table Logs_20170315 -[ -{"timestamp": "2017/03/15 00:00:00", "price": 1000}, -{"timestamp": "2017/03/15 11:00:00", "price": 900}, -{"timestamp": "2017/03/15 12:00:00", "price": 300}, -{"timestamp": "2017/03/15 13:00:00", "price": 200} -] -[[0,0.0,0.0],4] -load --table Logs_20170316 -[ -{"timestamp": "2017/03/16 04:00:00", "price": 530}, -{"timestamp": "2017/03/16 05:00:00", "price": 520}, -{"timestamp": "2017/03/16 09:00:00", "price": 110}, -{"timestamp": "2017/03/16 13:00:00", "price": 410}, -{"timestamp": "2017/03/16 14:00:00", "price": 710} -] -[[0,0.0,0.0],5] -load --table Logs_20170317 -[ -{"timestamp": "2017/03/17 09:00:00", "price": 800}, -{"timestamp": "2017/03/17 21:00:00", "price": 400}, -{"timestamp": "2017/03/17 22:00:00", "price": 500}, -{"timestamp": "2017/03/17 23:00:00", "price": 300} -] -[[0,0.0,0.0],4] -logical_range_filter Logs --shard_key timestamp --max '2017/03/17 10:00:00.000' --max_border exclude --filter 'price >= 300' --columns[day].stage filtered --columns[day].type Time --columns[day].flags COLUMN_SCALAR --columns[day].value 'time_classify_day(timestamp-36000000000)' --columns[price_per_day].stage filtered --columns[price_per_day].type UInt32 --columns[price_per_day].flags COLUMN_SCALAR --columns[price_per_day].value 'window_sum(price)' --columns[price_per_day].window.group_keys 'day' --output_columns _id,day,price_per_day -[ - [ - 0, - 0.0, - 0.0 - ], - [ - [ - [ - "_id", - "UInt32" - ], - [ - "day", - "Time" - ], - [ - "price_per_day", - "UInt32" - ] - ], - [ - 1, - 1489417200.0, - 1000 - ], - [ - 2, - 1489503600.0, - 1200 - ], - [ - 3, - 1489503600.0, - 1200 - ], - [ - 1, - 1489503600.0, - 1050 - ], - [ - 2, - 1489503600.0, - 1050 - ], - [ - 4, - 1489590000.0, - 1120 - ], - [ - 5, - 1489590000.0, - 1120 - ], - [ - 1, - 1489590000.0, - 800 - ] - ] -] Modified: test/command/suite/sharding/logical_range_filter/columns/stage/filtered/window_function/shards.test (+0 -2) =================================================================== --- test/command/suite/sharding/logical_range_filter/columns/stage/filtered/window_function/shards.test 2019-04-26 15:43:03 +0900 (7ccf4fef5) +++ test/command/suite/sharding/logical_range_filter/columns/stage/filtered/window_function/shards.test 2019-04-26 15:43:52 +0900 (00fd87edc) @@ -1,5 +1,3 @@ -#@omit TODO: Implemente me - #@on-error omit plugin_register sharding plugin_register functions/time Modified: test/command/suite/sharding/logical_range_filter/columns/stage/initial/range.expected (+6 -6) =================================================================== --- test/command/suite/sharding/logical_range_filter/columns/stage/initial/range.expected 2019-04-26 15:43:03 +0900 (e4b09cd39) +++ test/command/suite/sharding/logical_range_filter/columns/stage/initial/range.expected 2019-04-26 15:43:52 +0900 (760e2e691) @@ -80,32 +80,32 @@ logical_range_filter Logs --shard_key timestamp --min "2017/03/15 01:00:00" [ 2, 900, - 1200 + 2360 ], [ 3, 300, - 1200 + 2360 ], [ 1, 530, - 1160 + 3160 ], [ 2, 520, - 1160 + 3160 ], [ 3, 110, - 1160 + 3160 ], [ 1, 800, - 800 + 1960 ] ] ] Deleted: test/command/suite/sharding/logical_range_filter/columns/stage/initial/window_function/shards.reject (+0 -121) 100644 =================================================================== --- test/command/suite/sharding/logical_range_filter/columns/stage/initial/window_function/shards.reject 2019-04-26 15:43:03 +0900 (a71b87a55) +++ /dev/null @@ -1,121 +0,0 @@ -plugin_register sharding -[[0,0.0,0.0],true] -plugin_register functions/time -[[0,0.0,0.0],true] -table_create Logs_20170315 TABLE_NO_KEY -[[0,0.0,0.0],true] -column_create Logs_20170315 timestamp COLUMN_SCALAR Time -[[0,0.0,0.0],true] -column_create Logs_20170315 price COLUMN_SCALAR UInt32 -[[0,0.0,0.0],true] -table_create Logs_20170316 TABLE_NO_KEY -[[0,0.0,0.0],true] -column_create Logs_20170316 timestamp COLUMN_SCALAR Time -[[0,0.0,0.0],true] -column_create Logs_20170316 price COLUMN_SCALAR UInt32 -[[0,0.0,0.0],true] -table_create Logs_20170317 TABLE_NO_KEY -[[0,0.0,0.0],true] -column_create Logs_20170317 timestamp COLUMN_SCALAR Time -[[0,0.0,0.0],true] -column_create Logs_20170317 price COLUMN_SCALAR UInt32 -[[0,0.0,0.0],true] -load --table Logs_20170315 -[ -{"timestamp": "2017/03/15 00:00:00", "price": 1000}, -{"timestamp": "2017/03/15 11:00:00", "price": 900}, -{"timestamp": "2017/03/15 12:00:00", "price": 300}, -{"timestamp": "2017/03/15 13:00:00", "price": 200} -] -[[0,0.0,0.0],4] -load --table Logs_20170316 -[ -{"timestamp": "2017/03/16 04:00:00", "price": 530}, -{"timestamp": "2017/03/16 05:00:00", "price": 520}, -{"timestamp": "2017/03/16 09:00:00", "price": 110}, -{"timestamp": "2017/03/16 13:00:00", "price": 410}, -{"timestamp": "2017/03/16 14:00:00", "price": 710} -] -[[0,0.0,0.0],5] -load --table Logs_20170317 -[ -{"timestamp": "2017/03/17 09:00:00", "price": 800}, -{"timestamp": "2017/03/17 21:00:00", "price": 400}, -{"timestamp": "2017/03/17 22:00:00", "price": 500}, -{"timestamp": "2017/03/17 23:00:00", "price": 300} -] -[[0,0.0,0.0],4] -logical_range_filter Logs --shard_key timestamp --max '2017/03/17 10:00:00.000' --max_border exclude --columns[day].stage initial --columns[day].type Time --columns[day].flags COLUMN_SCALAR --columns[day].value 'time_classify_day(timestamp-36000000000)' --columns[price_per_day].stage initial --columns[price_per_day].type UInt32 --columns[price_per_day].flags COLUMN_SCALAR --columns[price_per_day].value 'window_sum(price)' --columns[price_per_day].window.group_keys 'day' --output_columns _id,day,price_per_day -[ - [ - 0, - 0.0, - 0.0 - ], - [ - [ - [ - "_id", - "UInt32" - ], - [ - "day", - "Time" - ], - [ - "price_per_day", - "UInt32" - ] - ], - [ - 1, - 1489417200.0, - 1000 - ], - [ - 2, - 1489503600.0, - 1400 - ], - [ - 3, - 1489503600.0, - 1400 - ], - [ - 4, - 1489503600.0, - 1400 - ], - [ - 1, - 1489503600.0, - 1160 - ], - [ - 2, - 1489503600.0, - 1160 - ], - [ - 3, - 1489503600.0, - 1160 - ], - [ - 4, - 1489590000.0, - 1120 - ], - [ - 5, - 1489590000.0, - 1120 - ], - [ - 1, - 1489590000.0, - 800 - ] - ] -] Modified: test/command/suite/sharding/logical_range_filter/columns/stage/initial/window_function/shards.test (+0 -2) =================================================================== --- test/command/suite/sharding/logical_range_filter/columns/stage/initial/window_function/shards.test 2019-04-26 15:43:03 +0900 (e4aef3666) +++ test/command/suite/sharding/logical_range_filter/columns/stage/initial/window_function/shards.test 2019-04-26 15:43:52 +0900 (6b04900c5) @@ -1,5 +1,3 @@ -#@omit TODO: Implemente me - #@on-error omit plugin_register sharding plugin_register functions/time -------------- next part -------------- An HTML attachment was scrubbed... URL: <https://lists.osdn.me/mailman/archives/groonga-commit/attachments/20190426/cb4d01a3/attachment-0001.html>