Skip to content

Commit

Permalink
Add support for BYTES data type
Browse files Browse the repository at this point in the history
  • Loading branch information
mziccard committed Jun 10, 2016
1 parent 27836e4 commit 085c872
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public static class Type implements Serializable {
private static final long serialVersionUID = 2841484762609576959L;

public enum Value {
STRING, INTEGER, FLOAT, BOOLEAN, TIMESTAMP, RECORD
BYTES, STRING, INTEGER, FLOAT, BOOLEAN, TIMESTAMP, RECORD
}

private final Value value;
Expand Down Expand Up @@ -108,6 +108,13 @@ public List<Field> fields() {
return fields;
}

/**
* Returns a {@link Value#BYTES} field value.
*/
public static Type bytes() {
return new Type(Value.BYTES);
}

/**
* Returns a {@link Value#STRING} field value.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.google.common.base.Function;
import com.google.common.base.MoreObjects;
import com.google.common.collect.Lists;
import com.google.common.io.BaseEncoding;

import java.io.Serializable;
import java.util.List;
Expand Down Expand Up @@ -54,7 +55,7 @@ public FieldValue apply(Object pb) {
public enum Attribute {
/**
* A primitive field value. A {@code FieldValue} is primitive when the corresponding field has
* type {@link Field.Type#bool()}, {@link Field.Type#string()},
* type {@link Field.Type#bytes()}, {@link Field.Type#bool()}, {@link Field.Type#string()},
* {@link Field.Type#floatingPoint()}, {@link Field.Type#integer()},
* {@link Field.Type#timestamp()} or the value is set to {@code null}.
*/
Expand All @@ -80,7 +81,7 @@ public enum Attribute {
* Returns the attribute of this Field Value.
*
* @return {@link Attribute#PRIMITIVE} if the field is a primitive type
* ({@link Field.Type#bool()}, {@link Field.Type#string()},
* ({@link Field.Type#bytes()}, {@link Field.Type#bool()}, {@link Field.Type#string()},
* {@link Field.Type#floatingPoint()}, {@link Field.Type#integer()},
* {@link Field.Type#timestamp()}) or is {@code null}. Returns {@link Attribute#REPEATED} if
* the corresponding field has ({@link Field.Mode#REPEATED}) mode. Returns
Expand Down Expand Up @@ -108,8 +109,8 @@ public Object value() {

/**
* Returns this field's value as a {@link String}. This method should only be used if the
* corresponding field has primitive type ({@link Field.Type#bool()}, {@link Field.Type#string()},
* {@link Field.Type#floatingPoint()}, {@link Field.Type#integer()},
* corresponding field has primitive type ({@link Field.Type#bytes()}, {@link Field.Type#bool()},
* {@link Field.Type#string()}, {@link Field.Type#floatingPoint()}, {@link Field.Type#integer()},
* {@link Field.Type#timestamp()}).
*
* @throws ClassCastException if the field is not a primitive type
Expand All @@ -121,6 +122,22 @@ public String stringValue() {
return (String) value;
}

/**
* Returns this field's value as a byte array. This method should only be used if the
* corresponding field has primitive type ({@link Field.Type#bytes()}.
*
* @throws ClassCastException if the field is not a primitive type
* @throws NullPointerException if {@link #isNull()} returns {@code true}
* @throws IllegalStateException if the field value is not encoded in base64
*/
public byte[] bytesValue() {
try {
return BaseEncoding.base64().decode(stringValue());
} catch (IllegalArgumentException ex) {
throw new IllegalStateException(ex);
}
}

/**
* Returns this field's value as a {@code long}. This method should only be used if the
* corresponding field has {@link Field.Type#integer()} type.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ public final class InsertAllRequest implements Serializable {

/**
* A Google Big Query row to be inserted into a table. Each {@code RowToInsert} has an associated
* id used by BigQuery to detect duplicate insertion requests on a best-effort basis.
* id used by BigQuery to detect duplicate insertion requests on a best-effort basis. Please
* notice that data for fields of type {@link Field.Type#bytes()} must be provided as a base64
* encoded string.
*
* <p>Example usage of creating a row to insert:
* <pre> {@code
Expand All @@ -58,8 +60,9 @@ public final class InsertAllRequest implements Serializable {
* recordContent.put("subfieldName1", "value");
* recordContent.put("subfieldName2", repeatedFieldValue);
* Map<String, Object> rowContent = new HashMap<String, Object>();
* rowContent.put("fieldName1", true);
* rowContent.put("fieldName2", recordContent);
* rowContent.put("booleanFieldName", true);
* rowContent.put("bytesFieldName", "DQ4KDQ==");
* rowContent.put("recordFieldName", recordContent);
* RowToInsert row = new RowToInsert("rowId", rowContent);
* }</pre>
*
Expand Down Expand Up @@ -116,7 +119,8 @@ public boolean equals(Object obj) {
}

/**
* Creates a row to be inserted with associated id.
* Creates a row to be inserted with associated id. Please notice that data for fields of type
* {@link Field.Type#bytes()} must be provided as a base64 encoded string.
*
* @param id id of the row, used to identify duplicates
* @param content the actual content of the row
Expand All @@ -126,7 +130,8 @@ public static RowToInsert of(String id, Map<String, Object> content) {
}

/**
* Creates a row to be inserted without associated id.
* Creates a row to be inserted without associated id. Please notice that data for fields of
* type {@link Field.Type#bytes()} must be provided as a base64 encoded string.
*
* @param content the actual content of the row
*/
Expand Down Expand Up @@ -174,7 +179,8 @@ public Builder addRow(RowToInsert rowToInsert) {
}

/**
* Adds a row to be inserted with associated id.
* Adds a row to be inserted with associated id. Please notice that data for fields of type
* {@link Field.Type#bytes()} must be provided as a base64 encoded string.
*
* <p>Example usage of adding a row with associated id:
* <pre> {@code
Expand All @@ -184,8 +190,9 @@ public Builder addRow(RowToInsert rowToInsert) {
* recordContent.put("subfieldName1", "value");
* recordContent.put("subfieldName2", repeatedFieldValue);
* Map<String, Object> rowContent = new HashMap<String, Object>();
* rowContent.put("fieldName1", true);
* rowContent.put("fieldName2", recordContent);
* rowContent.put("booleanFieldName", true);
* rowContent.put("bytesFieldName", "DQ4KDQ==");
* rowContent.put("recordFieldName", recordContent);
* builder.addRow("rowId", rowContent);
* }</pre>
*/
Expand All @@ -195,7 +202,8 @@ public Builder addRow(String id, Map<String, Object> content) {
}

/**
* Adds a row to be inserted without an associated id.
* Adds a row to be inserted without an associated id. Please notice that data for fields of
* type {@link Field.Type#bytes()} must be provided as a base64 encoded string.
*
* <p>Example usage of adding a row without an associated id:
* <pre> {@code
Expand All @@ -205,8 +213,9 @@ public Builder addRow(String id, Map<String, Object> content) {
* recordContent.put("subfieldName1", "value");
* recordContent.put("subfieldName2", repeatedFieldValue);
* Map<String, Object> rowContent = new HashMap<String, Object>();
* rowContent.put("fieldName1", true);
* rowContent.put("fieldName2", recordContent);
* rowContent.put("booleanFieldName", true);
* rowContent.put("bytesFieldName", "DQ4KDQ==");
* rowContent.put("recordFieldName", recordContent);
* builder.addRow(rowContent);
* }</pre>
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.google.cloud.bigquery;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
Expand All @@ -24,18 +25,22 @@
import com.google.api.services.bigquery.model.TableCell;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.io.BaseEncoding;

import org.junit.Test;

import java.util.Map;

public class FieldValueTest {

private static final byte[] BYTES = {0xD, 0xE, 0xA, 0xD};
private static final String BYTES_BASE64 = BaseEncoding.base64().encode(BYTES);
private static final TableCell BOOLEAN_FIELD = new TableCell().setV("false");
private static final Map<String, String> INTEGER_FIELD = ImmutableMap.of("v", "1");
private static final Map<String, String> FLOAT_FIELD = ImmutableMap.of("v", "1.5");
private static final Map<String, String> STRING_FIELD = ImmutableMap.of("v", "string");
private static final Map<String, String> TIMESTAMP_FIELD = ImmutableMap.of("v", "42");
private static final Map<String, String> BYTES_FIELD = ImmutableMap.of("v", BYTES_BASE64);
private static final Map<String, Object> NULL_FIELD =
ImmutableMap.of("v", Data.nullOf(String.class));
private static final Map<String, Object> REPEATED_FIELD =
Expand All @@ -60,6 +65,9 @@ public void testFromPb() {
value = FieldValue.fromPb(TIMESTAMP_FIELD);
assertEquals(FieldValue.Attribute.PRIMITIVE, value.attribute());
assertEquals(42000000, value.timestampValue());
value = FieldValue.fromPb(BYTES_FIELD);
assertEquals(FieldValue.Attribute.PRIMITIVE, value.attribute());
assertArrayEquals(BYTES, value.bytesValue());
value = FieldValue.fromPb(NULL_FIELD);
assertNull(value.value());
value = FieldValue.fromPb(REPEATED_FIELD);
Expand Down Expand Up @@ -94,6 +102,10 @@ public void testEquals() {
assertEquals(timestampValue, FieldValue.fromPb(TIMESTAMP_FIELD));
assertEquals(timestampValue.hashCode(), FieldValue.fromPb(TIMESTAMP_FIELD).hashCode());

FieldValue bytesValue = new FieldValue(FieldValue.Attribute.PRIMITIVE, BYTES_BASE64);
assertEquals(bytesValue, FieldValue.fromPb(BYTES_FIELD));
assertEquals(bytesValue.hashCode(), FieldValue.fromPb(BYTES_FIELD).hashCode());

FieldValue nullValue = new FieldValue(FieldValue.Attribute.PRIMITIVE, null);
assertEquals(nullValue, FieldValue.fromPb(NULL_FIELD));
assertEquals(nullValue.hashCode(), FieldValue.fromPb(NULL_FIELD).hashCode());
Expand Down
Loading

0 comments on commit 085c872

Please sign in to comment.