Masafumi Yokoyama
null+****@clear*****
Sat Feb 14 01:18:52 JST 2015
Masafumi Yokoyama 2015-02-14 01:18:52 +0900 (Sat, 14 Feb 2015) New Revision: 16833f3f42c9d970dc4a9fbceeb10f34761cfac6 https://github.com/ranguba/rroonga/commit/16833f3f42c9d970dc4a9fbceeb10f34761cfac6 Message: Support calculation for drilldown calc_target and calc_types are supported since Groonga 4.1.1. GitHub: fix #43 Modified files: ext/groonga/rb-grn-table.c lib/groonga/record.rb test/test-table-group.rb Modified: ext/groonga/rb-grn-table.c (+43 -0) =================================================================== --- ext/groonga/rb-grn-table.c 2015-02-13 21:19:38 +0900 (f3b3fd8) +++ ext/groonga/rb-grn-table.c 2015-02-14 01:18:52 +0900 (2bb34bb) @@ -1338,6 +1338,16 @@ rb_grn_table_sort (int argc, VALUE *argv, VALUE self) * @option options :max_n_sub_records * グループ化した後のレコードのそれぞれについて最大 _:max_n_sub_records_ 件まで * そのグループに含まれる _table_ のレコードをサブレコードとして格納する。 + * @option options [String or Symbol] :calc_target + * The target column name for _:calc_types_. + * @option options [::Array] :calc_types + * It specifies how to calculate (aggregate) values in grouped records by + * a drilldown. You can specify multiple calculation types. + * + * - +:max+ := Finding the maximum integer value from integer values in grouped records. + * - +:min+ := Finding the minimum integer value from integer values in grouped records. + * - +:sum+ := Summing integer values in grouped records. + * - +:average+ := Averaging integer/float values in grouped records. * * @!macro table.group.options * @@ -1357,6 +1367,7 @@ rb_grn_table_group (int argc, VALUE *argv, VALUE self) unsigned int max_n_sub_records = 0; grn_rc rc; VALUE rb_keys, rb_options, rb_max_n_sub_records; + VALUE rb_calc_target, rb_calc_types; VALUE *rb_group_keys; rb_grn_table_deconstruct(SELF(self), &table, &context, @@ -1376,6 +1387,8 @@ rb_grn_table_group (int argc, VALUE *argv, VALUE self) rb_grn_scan_options(rb_options, "max_n_sub_records", &rb_max_n_sub_records, + "calc_target", &rb_calc_target, + "calc_types", &rb_calc_types, NULL); if (NIL_P(rb_max_n_sub_records)) { @@ -1427,6 +1440,36 @@ rb_grn_table_group (int argc, VALUE *argv, VALUE self) result.max_n_subrecs = max_n_sub_records; result.calc_target = NULL; + if (!NIL_P(rb_calc_target)) { + const char *name = NULL; + unsigned name_size = 0; + ruby_object_to_column_name(rb_calc_target, &name, &name_size); + result.calc_target = grn_obj_column(context, table, name, name_size); + } + if (result.calc_target) { + int i, n_calc_types; + VALUE *raw_calc_types; + raw_calc_types = RARRAY_PTR(rb_calc_types); + n_calc_types = RARRAY_LEN(rb_calc_types); + for (i = 0; i < n_calc_types; i++) { + VALUE rb_calc_type = raw_calc_types[i]; + if (rb_grn_equal_option(rb_calc_type, "max")) { + result.flags |= GRN_TABLE_GROUP_CALC_MAX; + } else if (rb_grn_equal_option(rb_calc_type, "min")) { + result.flags |= GRN_TABLE_GROUP_CALC_MIN; + } else if (rb_grn_equal_option(rb_calc_type, "sum")) { + result.flags |= GRN_TABLE_GROUP_CALC_SUM; + } else if (rb_grn_equal_option(rb_calc_type, "average")) { + result.flags |= GRN_TABLE_GROUP_CALC_AVG; + } else { + rb_raise(rb_eArgError, + "invalid calculation type: %s: " + "available types: [:max, :min, :sum, :average]", + rb_grn_inspect(rb_calc_type)); + } + } + } + rc = grn_table_group(context, table, keys, n_keys, &result, 1); rb_grn_context_check(context, self); rb_grn_rc_check(rc, self); Modified: lib/groonga/record.rb (+45 -1) =================================================================== --- lib/groonga/record.rb 2015-02-13 21:19:38 +0900 (631d4df) +++ lib/groonga/record.rb 2015-02-14 01:18:52 +0900 (f14402b) @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright (C) 2014 Masafumi Yokoyama <myokoym �� gmail.com> +# Copyright (C) 2014-2015 Masafumi Yokoyama <yokoyama �� clear-code.com> # Copyright (C) 2009-2014 Kouhei Sutou <kou �� clear-code.com> # # This library is free software; you can redistribute it and/or @@ -226,6 +226,50 @@ module Groonga @table.support_sub_records? end + # The maximum integer value from integer values in grouped records. + # It can be used when specifying _:calc_target_ and _:calc_types_ + # to {Groonga::Table#group}. + # + # @return [Integer] The maximum integer value from integer values in grouped records. + # + # @since 5.0.0 + def max + self["_max"] + end + + # The minimum integer value from integer values in grouped records. + # It can be used when specifying _:calc_target_ and _:calc_types_ + # to {Groonga::Table#group}. + # + # @return [Integer] The minimum integer value from integer values in grouped records. + # + # @since 5.0.0 + def min + self["_min"] + end + + # The sum of integer values in grouped records. + # It can be used when specifying _:calc_target_ and _:calc_types_ + # to {Groonga::Table#group}. + # + # @return [Integer] The sum of integer values in grouped records. + # + # @since 5.0.0 + def sum + self["_sum"] + end + + # The average of integer/float values in grouped records. + # It can be used when specifying _:calc_target_ and _:calc_types_ + # to {Groonga::Table#group}. + # + # @return [Float] The average of integer/float values in grouped records. + # + # @since 5.0.0 + def average + self["_avg"] + end + # レコードの値を返す。 def value @table.value(@id, :id => true) Modified: test/test-table-group.rb (+99 -0) =================================================================== --- test/test-table-group.rb 2015-02-13 21:19:38 +0900 (bf09ace) +++ test/test-table-group.rb 2015-02-14 01:18:52 +0900 (cd58a22) @@ -250,4 +250,103 @@ class TableGroupTest < Test::Unit::TestCase grouped_records) end end + + class CalculationTest < self + setup + def setup_schema + Groonga::Schema.define do |schema| + schema.create_table("Tags", :type => :hash) + + schema.create_table("Memos", :type => :hash) do |table| + table.reference("tag") + table.int64("priority") + end + end + end + + setup + def setup_data + setup_tags + setup_memos + end + + def setup_tags + @tags = Groonga["Tags"] + @groonga =****@tags*****("Groonga") + @mroonga =****@tags*****("Mroonga") + @rroonga =****@tags*****("Rroonga") + end + + def setup_memos + @memos = Groonga["Memos"] + @memos.add("Groonga1", + :tag => @groonga, + :priority => 10) + @memos.add("Groonga2", + :tag => @groonga, + :priority => 20) + @memos.add("Groonga3", + :tag => @groonga, + :priority => 40) + @memos.add("Mroonga1", + :tag => @mroonga, + :priority => 50) + @memos.add("Mroonga2", + :tag => @mroonga, + :priority => 25) + @memos.add("Mroonga3", + :tag => @mroonga, + :priority => 10) + @memos.add("Rroonga1", + :tag => @rroonga, + :priority => 25) + @memos.add("Rroonga2", + :tag => @rroonga, + :priority => -25) + @memos.add("Rroonga3", + :tag => @rroonga, + :priority => 0) + end + + def test_max + grouped_records =****@memos*****("tag", + :calc_target => "priority", + :calc_types => [:max]).collect do |group| + tag = group.key + [ + tag.key, + group.max, + ] + end + + assert_equal([ + ["Groonga", 40], + ["Mroonga", 50], + ["Rroonga", 25], + ], + grouped_records) + end + + def test_all_types + grouped_records =****@memos*****("tag", + :calc_target => "priority", + :calc_types => [:max, :min, :sum, :average]).collect do |group| + tag = group.key + [ + tag.key, + group.max, + group.min, + group.sum, + group.average.round(3), + ] + end + + assert_equal([ + ["Groonga", 40, 10, 70, 23.333], + ["Mroonga", 50, 10, 85, 28.333], + ["Rroonga", 25, -25, 0, 0.0], + ], + grouped_records) + end + end end -------------- next part -------------- HTML����������������������������... Download