code: private void populateComparableKeyRecord(GenericRecord source, GenericRecord target) {
      for (Field field : target.getSchema().getFields()) {
        if (field.schema().getType() == Schema.Type.UNION) {
  
          // Since a UNION has multiple types, we need to use induce() to get the actual type in the record.
          Object fieldData = source.get(field.name());
          Schema actualFieldSchema = GenericData.get().induce(fieldData);
          if (actualFieldSchema.getType() == Schema.Type.RECORD) {
  
            // If the actual type is RECORD (which may contain another UNION), we need to recursively
            // populate it.
            for (Schema candidateType : field.schema().getTypes()) {
              if (candidateType.getFullName().equals(actualFieldSchema.getFullName())) {
                GenericRecord record = new GenericData.Record(candidateType);
                target.put(field.name(), record);
                populateComparableKeyRecord((GenericRecord) fieldData, record);
                break;
              }
            }
          } else {
            target.put(field.name(), source.get(field.name()));
          }
        } else if (field.schema().getType() == Schema.Type.RECORD) {
          GenericRecord record = (GenericRecord) target.get(field.name());
          if (record == null) {
            record = new GenericData.Record(field.schema());
            target.put(field.name(), record);
          }
          populateComparableKeyRecord((GenericRecord) source.get(field.name()), record);
        } else {
          target.put(field.name(), source.get(field.name()));
        }
      }
    }
docs: Populate the target record, based on the field values in the source record.
  Target record's schema should be a subset of source record's schema.
  Target record's schema cannot have MAP, ARRAY or ENUM fields, or UNION fields that
  contain these fields.