You are an expert in writing code in many different languages. Your goal is to perform code completion for the following code, keeping in mind the rest of the code and the file meta data.
    Metadata details:
    filename: bulk_builder.rb
    path of file: ./repos/chewy/lib/chewy/index/import
    
    the code of the file until where you have to start completion: module Chewy
  class Index
    module Import
      # This class purpose is to build ES client-acceptable bulk
      # request body from the passed objects for index and deletion.
      # It handles parent-child relationships as well by fetching
      # existing documents from ES and database, taking their join field values and
      # using it in the bulk body.
      # If fields are passed - it creates partial update entries except for
      # the cases when the type has parent and parent_id has been changed.
      class BulkBuilder
        # @param index [Chewy::Index] desired index
        # @param to_index [Array<Object>] objects to index
        # @param delete [Array<Object>] objects or ids to delete
        # @param fields [Array<Symbol, String>] and array of fields for documents update
        def initialize(index, to_index: [], delete: [], fields: [])
          @index = index
          @to_index = to_index
          @delete = del
    
        def existing_routing(id)
          # All objects needed here should be cached in #load_cache,
          # if not, we return nil. In some cases we don't have existing routing cached,
          # e.g. for loaded descendants
          return unless cache(id)

          cache(id)[:routing]
        end

        # Two types of ids:
        # * of parents of the objects to be indexed
        # * of objects to be deleted
        def ids_for_cache
          ids = @to_index.flat_map do |object|
            [find_parent_id(object), object.id] if object.respond_to?(:id)
          end
          ids.concat(@delete.map do |object|
            object.id if object.respond_to?(:id)
          end)
          ids.uniq.compact
        end

        def routing(object)
          # filter out non-model objects, early return on object==nil
          return unless object.respond_to?(:id)

          parent_id = find_parent_id(object)
          if parent_id
            routing(index_objects_by_id[parent_id.to_s]) || existing_routing(parent_id)
          else
            object.id.to_s
          end
        end

        def find_parent_id(object)
          return unless object.respond_to?(:id)

          join = data_for(object, fields: [join_field.to_sym])[join_field]
          join['parent'] if join
        end

        def join_field
          return @join_field if defined?(@join_field)

          @join_field = find_join_field
        end

        def find_join_field
          type_settings = @index.mappings_hash[:mappings]
          return unless type_settings

          properties = type_settings[:properties]
          join_fields = properties.find { |_, options| options[:type] == :join }
          return unless join_fields

          join_fields.first.to_s
        end

        def join_field_type(object)
          return unless join_field?

          raw_object =
            if @index._default_import_options[:raw_import]
              @index._default_import_options[:raw_import].call(object.attributes)
            else
              object
            end

          join_field_value = data_for(
            raw_object,
            fields: [join_field.to_sym], # build only the field that is needed
            crutches: Chewy::Index::Crutch::Crutches.new(@index, [raw_object])
          )[join_field]

          case join_field_value
          when String
            join_field_value
          when Hash
            join_field_value['name']
          end