<commit_before>module Neo4j
  module Server
    class CypherResponse
      attr_reader :data, :columns, :error_msg, :error_status, :error_code, :response

      class ResponseError < StandardError
        attr_reader :status, :code

        def initialize(msg, status, code)
          super(msg)
          @status = status
          @code = code
        end
      end


      class HashEnumeration
        include Enumerable
        extend Forwardable
        def_delegator :@response, :error_msg
        def_delegator :@response, :error_status
        def_delegator :@response, :error_code
        def_delegator :@response, :columns
        def_delegator :@response, :struct

        def initialize(response, query)
          @response = response
          @query = query
        end

        def to_s
          @query
        end

        def inspect
          "Enumerable query: '#{@query}'"
        end

        def each(&block)
          @response.each_data_row do |row|
            yield struct_rows(row)
          end
        end

        def struct_rows(row)
          struct.new.tap do |result|
            row.each_with_index { |value, i| result[columns[i]] = value }
          end
        end
      end

      EMPTY_STRING = ''
      def to_struct_enumeration(cypher = EMPTY_STRING)
        HashEnumeration.new(self, cypher)
      end

      def to_node_enumeration(cypher = EMPTY_STRING, session = Neo4j::Session.current)
        Enumerator.new do |yielder|
          @result_index = 0
          to_struct_enumeration(cypher).each do |row|
            @row_index = 0
            yielder << row_pair_in_struct(row, session)
            @result_index += 1
          end
        end
      end

      def row_pair_in_struct(row, session)
        @struct.new.tap do |result|
          row.each_pair do |column, value|
            result[column] = map_row_value(value, session)
            @row_index += 1
          end
        end
      end

      def map_row_value(value, session)
        if value.is_a?(Hash)
          hash_value_as_object(value, session)
        elsif value.is_a?(Array)
          value.map! { |v| map_row_value(v, session) }
        else
          value
        end
      end

      def hash_value_as_object(value, session)
        data =  case
                when transaction_response?
                  add_transaction_entity_id
                  mapped_rest_data
                when value[:labels] || value[:type]
                  add_entity_id(value)
                  value
                else
                  return value
                end
        (node?(value) ? CypherNode : CypherRelationship).new(session, data).wrapper
      end

      def node?(value)
        transaction_response? ? !mapped_rest_data[:start] : value[:labels]
      end

      attr_reader :struct

      def initialize(response, uncommited = false)
        @response = response
        @uncommited = uncommited
      end

      def entity_data(id = nil)
        if @uncommited
          data = @data.first[:row].first
          data.is_a?(Hash) ? {data: data, id: id} : data
        else
          data = @data[0][0]
          data.is_a?(Hash) ? add_entity_id(data) : data
        end
      end

      def first_data(id = nil)
        if @uncommited
          data = @data.first[:row].first
          # data.is_a?(Hash) ? {'data' => data, 'id' => id} : data
        else
          data = @data[0][0]
          data.is_a?(Hash) ? add_entity_id(data) : data
        end
      end

      def add_entity_id(data)
        data.merge!(id: self.class.id_from_url(data[:self]))
      end

      def add_transaction_entity_id
        mapped_rest_data.merge!(id: mapped_rest_data[:self].split('/').last.to_i)
      end

      def error?
        !!@error
      end

      def data?
        !response.body[:data].nil?
      end

      def raise_unless_response_code(code)
        fail "Response code #{response.status}, expected #{code} for #{response.headers[:location]}, #{response.body}" unless response.status == code
      end

      def each_data_row
        if @uncommited
          data.each { |r| yield r[:row] }
        else
          data.each { |r| yield r }
        end
      end

      def set_data(data, columns)<commit_msg>use metadata id when available<commit_after>module Neo4j
  module Server
    class CypherResponse
      attr_reader :data, :columns, :error_msg, :error_status, :error_code, :response

      class ResponseError < StandardError
        attr_reader :status, :code

        def initialize(msg, status, code)
          super(msg)
          @status = status
          @code = code
        end
      end


      class HashEnumeration
        include Enumerable
        extend Forwardable
        def_delegator :@response, :error_msg
        def_delegator :@response, :error_status
        def_delegator :@response, :error_code
        def_delegator :@response, :columns
        def_delegator :@response, :struct

        def initialize(response, query)
          @response = response
          @query = query
        end

        def to_s
          @query
        end

        def inspect
          "Enumerable query: '#{@query}'"
        end

        def each(&block)
          @response.each_data_row do |row|
            yield struct_rows(row)
          end
        end

        def struct_rows(row)
          struct.new.tap do |result|
            row.each_with_index { |value, i| result[columns[i]] = value }
          end
        end
      end

      EMPTY_STRING = ''
      def to_struct_enumeration(cypher = EMPTY_STRING)
        HashEnumeration.new(self, cypher)
      end

      def to_node_enumeration(cypher = EMPTY_STRING, session = Neo4j::Session.current)
        Enumerator.new do |yielder|
          @result_index = 0
          to_struct_enumeration(cypher).each do |row|
            @row_index = 0
            yielder << row_pair_in_struct(row, session)
            @result_index += 1
          end
        end
      end

      def row_pair_in_struct(row, session)
        @struct.new.tap do |result|
          row.each_pair do |column, value|
            result[column] = map_row_value(value, session)
            @row_index += 1
          end
        end
      end

      def map_row_value(value, session)
        if value.is_a?(Hash)
          hash_value_as_object(value, session)
        elsif value.is_a?(Array)
          value.map! { |v| map_row_value(v, session) }
        else
          value
        end
      end

      def hash_value_as_object(value, session)
        data =  case
                when transaction_response?
                  add_transaction_entity_id
                  mapped_rest_data
                when value[:labels] || value[:type]
                  add_entity_id(value)
                  value
                else
                  return value
                end
        (node?(value) ? CypherNode : CypherRelationship).new(session, data).wrapper
      end

      def node?(value)
        transaction_response? ? !mapped_rest_data[:start] : value[:labels]
      end

      attr_reader :struct

      def initialize(response, uncommited = false)
        @response = response
        @uncommited = uncommited
      end

      def entity_data(id = nil)
        if @uncommited
          data = @data.first[:row].first
          data.is_a?(Hash) ? {data: data, id: id} : data
        else
          data = @data[0][0]
          data.is_a?(Hash) ? add_entity_id(data) : data
        end
      end

      def first_data(id = nil)
        if @uncommited
          data = @data.first[:row].first
          # data.is_a?(Hash) ? {'data' => data, 'id' => id} : data
        else
          data = @data[0][0]
          data.is_a?(Hash) ? add_entity_id(data) : data
        end
      end

      def add_entity_id(data)
        if data[:metadata] && data[:metadata][:id]
          data[:id] = data[:metadata][:id]
        else
          data.merge!(id: self.class.id_from_url(data[:self]))
        end
      end

      def add_transaction_entity_id
        mapped_rest_data.merge!(id: mapped_rest_data[:self].split('/').last.to_i)
      end

      def error?
        !!@error
      end

      def data?
        !response.body[:data].nil?
      end

      def raise_unless_response_code(code)
        fail "Response code #{response.status}, expected #{code} for #{response.headers[:location]}, #{response.body}" unless response.status == code
      end

      def each_data_row
        if @uncommited
          data.each { |r| yield r[:row] }
        else
          data.each { |r| yield r }
        end
      end

      def set_data(data, columns)
{'fraction_non_alphanumeric': 0.09220598469032706, 'fraction_numerical': 0.002319647413593134, 'mean_word_length': 1.732256020278834, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4210346', 'n_tokens_mistral': 2614, 'n_tokens_neox': 2524, 'n_words': 588}