Techioz Blog

Ruby クライアントを使用して Google Analytics Data API (GA4) のリストにフィルター式を追加するにはどうすればよいですか?

概要

Ruby クライアントで GA4 API をクエリするために google-analytics-data gem を使用しています。

フィルター式の配列をフィルター式リストに追加しようとすると、161 行目でエラーが発生します。

analytics_request_object_filter_expression_list.expressions = analytics_request_object_filter_expression_list_expressions

method_missing': Expected repeated field array

これが私のコードです

#! /usr/bin/ruby

require 'google-analytics-data'

module GoogleAnalyticsObjects

    def self.get_analytics_data( analytics:,
                                 start_date:,
                                 end_date:,
                                 metrics:,
                                 dimensions:, 
                                 order_bys: [] )
                                 
                          
      analytics_date_range_object = Google::Analytics::Data::V1beta::DateRange.new
      analytics_date_range_object.start_date = start_date
      analytics_date_range_object.end_date = end_date
  
     
      report_request_object_metrics = []
  
      metrics.each { |metric|
        
      analytics_metric_object = Google::Analytics::Data::V1beta::Metric.new
      analytics_metric_object.name = metric 
      
      report_request_object_metrics.push(analytics_metric_object) } 
  
      unless dimensions.empty?
  
        report_request_object_dimensions = []
  
        dimensions.each { |dimension|
          analytics_dimension_object = Google::Analytics::Data::V1beta::Dimension.new
          analytics_dimension_object.name = dimension
          report_request_object_dimensions.push(analytics_dimension_object) }
      end       

      unless order_bys.empty?
  
        report_request_object_order_bys = []
  
        order_bys.each { |orderby|
          analytics_orderby_object = Google::Analytics::Data::V1beta::OrderBy.new 

          analytics_orderby_object.field_name = orderby
          analytics_orderby_object.sort_order = 'DESCENDING'
          report_request_object_order_bys.push(analytics_orderby_object)}
      end
      
      ################
      analytics_request_object_filter_expression = Google::Analytics::Data::V1beta::FilterExpression.new    
      ################

      ################
      analytics_request_object_filter_expression_campaign = Google::Analytics::Data::V1beta::FilterExpression.new  
      ################
      
      ################
      analytics_request_object_filter_campaign = Google::Analytics::Data::V1beta::Filter.new

      analytics_request_object_filter_campaign.field_name = 'campaign'

      analytics_request_object_filter_campaign_string_filter = Google::Analytics::Data::V1beta::Filter::StringFilter.new

      analytics_request_object_filter_campaign_string_filter.case_sensitive = false

      analytics_request_object_filter_campaign_string_filter.value = '(not set)'

      analytics_request_object_filter_campaign_string_filter.match_type = 'EXACT'

      analytics_request_object_filter_campaign.string_filter = analytics_request_object_filter_campaign_string_filter 
     
      analytics_request_object_filter_expression_campaign.filter = analytics_request_object_filter_campaign # this filter expression gets added to the top level filter expression
      
      puts 
      puts 'not expression filter expression'
      puts analytics_request_object_filter_expression_campaign.inspect
      ###############

      ###############
      analytics_request_object_filter_expression.not_expression = analytics_request_object_filter_expression_campaign  # here
      ###############    

      puts
      puts 'top level filter expression after not expression added'
      puts analytics_request_object_filter_expression.inspect

      ###############
      analytics_request_object_filter_expression_list = Google::Analytics::Data::V1beta::FilterExpressionList.new # gets added to the top level filter expression list
      ###############
            
      ###############
      analytics_request_object_filter_expression_list_expressions = [] # array to hold top level filter expression and group filter expressions
      ###############
   
      ###############
      analytics_request_object_filter_expression_source_medium = Google::Analytics::Data::V1beta::FilterExpression.new
  
      analytics_request_object_filter_source_medium = Google::Analytics::Data::V1beta::Filter.new

      analytics_request_object_filter_source_medium.field_name = 'sourceMedium'

      analytics_request_object_filter_source_medium_string_filter = Google::Analytics::Data::V1beta::Filter::StringFilter.new

      analytics_request_object_filter_source_medium_string_filter.case_sensitive = false

      analytics_request_object_filter_source_medium_string_filter.match_type = 'EXACT'

      analytics_request_object_filter_source_medium_string_filter.value = 'aomestring'

      analytics_request_object_filter_source_medium.string_filter = analytics_request_object_filter_source_medium_string_filter 

      analytics_request_object_filter_expression_source_medium.filter = analytics_request_object_filter_source_medium  

      puts
      puts 'sourceMedium filter expression'
      puts analytics_request_object_filter_expression_source_medium.inspect
      ##############

      ##############
      analytics_request_object_filter_expression_list_expressions.push(analytics_request_object_filter_expression_source_medium)  # add to array 
      ##############


      ##############
      analytics_request_object_filter_expression_transactions = Google::Analytics::Data::V1beta::FilterExpression.new

      analytics_request_object_filter_transactions = Google::Analytics::Data::V1beta::Filter.new

      analytics_request_object_filter_transactions.field_name = 'transactions'
      
      analytics_request_object_filter_transactions_numeric_filter = Google::Analytics::Data::V1beta::Filter::NumericFilter.new

      analytics_request_object_filter_transactions_numeric_filter.operation = 'GREATER_THAN'

      analytics_request_object_filter_transactions_numeric_value = Google::Analytics::Data::V1beta::NumericValue.new

      analytics_request_object_filter_transactions_numeric_value.int64_value = 0

      analytics_request_object_filter_transactions_numeric_filter.value = analytics_request_object_filter_transactions_numeric_value

      analytics_request_object_filter_transactions.numeric_filter = analytics_request_object_filter_transactions_numeric_filter 

      analytics_request_object_filter_expression_transactions.filter = analytics_request_object_filter_transactions

      puts
      puts 'transactions filter expression'
      puts analytics_request_object_filter_expression_transactions.inspect
      #############

      #############
      analytics_request_object_filter_expression_list_expressions.push(analytics_request_object_filter_expression_transactions) # add to array
      #############

      puts
      puts 'array of and group expressions'
      puts analytics_request_object_filter_expression_list_expressions.inspect
      #############   

      analytics_request_object_filter_expression_list.expressions = analytics_request_object_filter_expression_list_expressions

      puts
      puts 'and group expressions list final'
      puts analytics_request_object_filter_expression_list.inspect
      #############

      #############
      #analytics_request_object_filter_expression.and_group = analytics_request_object_filter_expression_and_group   
      analytics_request_object_filter_expression.and_group = analytics_request_object_filter_expressions_list

      puts
      puts 'top level filter expression'
      puts analytics_request_object_filter_expression.inspect
      #############

      report_request_object = Google::Analytics::Data::V1beta::RunReportRequest.new(
        property: "properties/#{ANALYTICS_PROPERTY_ID}",
        metrics: report_request_object_metrics,
        dimensions: report_request_object_dimensions,
        order_bys: report_request_object_order_bys,
        date_ranges: [analytics_date_range_object],
        dimension_filter: analytics_request_object_filter_expression) 

      response = analytics.run_report(report_request_object)

    end
  
end # module  

編集。これは上記のコードからの出力です

not expression filter expression. This filter expression attaches to the not expression of the top level filter expression 
<Google::Analytics::Data::V1beta::FilterExpression: 
    filter: <Google::Analytics::Data::V1beta::Filter: 
        field_name: "campaign", 
        string_filter: <Google::Analytics::Data::V1beta::Filter::StringFilter: 
        match_type: :EXACT, 
        value: "(not set)", 
        case_sensitive: false>>>

top level filter expression after not expression added
<Google::Analytics::Data::V1beta::FilterExpression: 
    not_expression: 
        <Google::Analytics::Data::V1beta::FilterExpression: 
            filter: <Google::Analytics::Data::V1beta::Filter: 
            field_name: "campaign", 
            string_filter: <Google::Analytics::Data::V1beta::Filter::StringFilter: 
                match_type: :EXACT, 
                value: "(not set)", 
                case_sensitive: false>>>>

Above is a singleton filter expression.

sourceMedium filter expression. First element of the and group
<Google::Analytics::Data::V1beta::FilterExpression: 
    filter: <Google::Analytics::Data::V1beta::Filter: 
        field_name: "sourceMedium", 
        string_filter: <Google::Analytics::Data::V1beta::Filter::StringFilter: 
            match_type: :EXACT, 
            value: "somestring", 
            case_sensitive: false>>>

transactions filter expression. Second element of the and group
<Google::Analytics::Data::V1beta::FilterExpression: 
    filter: <Google::Analytics::Data::V1beta::Filter: 
        field_name: "transactions", 
        numeric_filter: <Google::Analytics::Data::V1beta::Filter::NumericFilter: 
            operation: :GREATER_THAN, 
            value: <Google::Analytics::Data::V1beta::NumericValue: int64_value: 0>>>>

Array of and group expressions. Error occurs adding this array to top level filter and group

"google_analytics_objects.rb:173:in `method_missing': Expected repeated field array"

[
    <Google::Analytics::Data::V1beta::FilterExpression: 
        filter: <Google::Analytics::Data::V1beta::Filter: 
            field_name: "sourceMedium", 
            string_filter: <Google::Analytics::Data::V1beta::Filter::StringFilter: 
                match_type: :EXACT, 
                value: "somestring", 
                case_sensitive: false>>>, 
                
    <Google::Analytics::Data::V1beta::FilterExpression: 
        filter: <Google::Analytics::Data::V1beta::Filter: 
            field_name: "transactions", 
            numeric_filter: <Google::Analytics::Data::V1beta::Filter::NumericFilter: 
                operation: :GREATER_THAN, 
                value: <Google::Analytics::Data::V1beta::NumericValue: int64_value: 0>>>>
]

API doc [https://cloud.google.com/ruby/docs/reference/google-analytics-data-v1beta/latest/Google-Analytics-Data-V1beta-FilterExpression][1]

解決策

このエラーは、gRPC のバーフィングのように見えます。配列を予期したときに別のものを受け取ったか、配列を受け取ったものの、配列内の何かが予期した種類の反復オブジェクトではないためです。

コードにプッシュするために渡される引数は配列ですか?その場合は、要素を個々の引数に分散するために、先頭にスプラット (*) を付ける必要があります。その式割り当ての protobuf が、繰り返される Expression 式 = 1 の行に沿った定義を持つメッセージを予期している場合。そして、要素の 1 つが要素の別の配列である配列オブジェクトを受け取ると、そのようなエラーが発生します。 expression=(value) 割り当てメソッドのドキュメントには、FilterExpression オブジェクトの配列が必要であることが示されており、コードでもそれが行われているように見えますが、コード内の変数名の一部は追跡されないため、いくつかの仮定を立てます。 …

Analytics_request_object_filter_expression_list の初期化が欠落していますが、Google::Analytics::Data::V1beta::FilterExpressionList として初期化されていると仮定します。

これが実際の問題行だと思います:

analytics_request_object_filter_expression_and_group.push(analytics_request_object_filter_expression_list)

タイプミスがあり、最後から 2 番目のアンダースコアがピリオドであると仮定すると、この行は、and_group アクセサー メソッドによって返された ExpressionList に ExpressionList 全体を (単一の要素として) 追加することになります (これにより、エラーが発生します)取得)。これはうまくいくと推測します。

analytics_request_object_filter_expression.and_group.push(
  *analytics_request_object_filter_expression_list
)

ただし、API は and_group の割り当てメソッドとアクセサー メソッドを定義していますが、追加メソッドは定義していません…アクセサー メソッドによって返されたリストに対して #push を呼び出すと、技術的には新しい要素を追加するように機能しますが、Ruby の変数処理を使用してハッキングしています。オブジェクトがどのように対話できるように設計されているか。そのオブジェクトの and_group も、指定した内容に基づいてすでに空になっているはずなので、単にリストを and_group= 割り当てメソッドに渡すことをお勧めします。

# If you don't need to merge the list into the existing `and_group` list:
analytics_request_object_filter_expression.and_group = analytics_request_object_filter_expression_list

コンソールでコードを 1 行ずつ実行し、オブジェクトを詳しく調べることをお勧めします。