JSON: StandardSQLTypeName.JSON
*
*
* No other types are supported through that entry point. The other types can be created by
@@ -254,6 +256,22 @@ public static QueryParameterValue string(String value) {
return of(value, StandardSQLTypeName.STRING);
}
+ /**
+ * Creates a {@code QueryParameterValue} object with a type of JSON. Currently, this is only
+ * supported in INSERT, not in query as a filter
+ */
+ public static QueryParameterValue json(String value) {
+ return of(value, StandardSQLTypeName.JSON);
+ }
+
+ /**
+ * Creates a {@code QueryParameterValue} object with a type of JSON. Currently, this is only
+ * supported in INSERT, not in query as a filter
+ */
+ public static QueryParameterValue json(JsonObject value) {
+ return of(value, StandardSQLTypeName.JSON);
+ }
+
/** Creates a {@code QueryParameterValue} object with a type of BYTES. */
public static QueryParameterValue bytes(byte[] value) {
return of(value, StandardSQLTypeName.BYTES);
@@ -347,6 +365,10 @@ private static StandardSQLTypeName classToType(Class type) {
return StandardSQLTypeName.NUMERIC;
} else if (Date.class.isAssignableFrom(type)) {
return StandardSQLTypeName.DATE;
+ } else if (String.class.isAssignableFrom(type)) {
+ return StandardSQLTypeName.JSON;
+ } else if (JsonObject.class.isAssignableFrom(type)) {
+ return StandardSQLTypeName.JSON;
}
throw new IllegalArgumentException("Unsupported object type for QueryParameter: " + type);
}
@@ -384,6 +406,9 @@ private static String valueToStringOrNull(T value, StandardSQLTypeName type)
break;
case STRING:
return value.toString();
+ case JSON:
+ if (value instanceof String || value instanceof JsonObject) return value.toString();
+ break;
case STRUCT:
throw new IllegalArgumentException("Cannot convert STRUCT to String value");
case ARRAY:
diff --git a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/StandardSQLTypeName.java b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/StandardSQLTypeName.java
index d618b7656..57152a2a6 100644
--- a/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/StandardSQLTypeName.java
+++ b/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/StandardSQLTypeName.java
@@ -56,5 +56,7 @@ public enum StandardSQLTypeName {
/** Represents a year, month, day, hour, minute, second, and subsecond (microsecond precision). */
DATETIME,
/** Represents a set of geographic points, represented as a Well Known Text (WKT) string. */
- GEOGRAPHY
+ GEOGRAPHY,
+ /** Represents JSON data */
+ JSON
}
diff --git a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/QueryParameterValueTest.java b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/QueryParameterValueTest.java
index b643ae580..679b6ec5c 100644
--- a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/QueryParameterValueTest.java
+++ b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/QueryParameterValueTest.java
@@ -24,6 +24,7 @@
import com.google.api.services.bigquery.model.QueryParameterType;
import com.google.common.collect.ImmutableMap;
+import com.google.gson.JsonObject;
import java.math.BigDecimal;
import java.text.ParseException;
import java.util.Date;
@@ -193,6 +194,24 @@ public void testString() {
assertThat(value.getArrayValues()).isNull();
}
+ @Test
+ public void testJson() {
+ QueryParameterValue value =
+ QueryParameterValue.json("{\"class\" : {\"students\" : [{\"name\" : \"Jane\"}]}}");
+ JsonObject jsonObject = new JsonObject();
+ jsonObject.addProperty("class", "student");
+ QueryParameterValue value1 = QueryParameterValue.json(jsonObject);
+ assertThat(value.getValue())
+ .isEqualTo("{\"class\" : {\"students\" : [{\"name\" : \"Jane\"}]}}");
+ assertThat(value1.getValue()).isEqualTo("{\"class\":\"student\"}");
+ assertThat(value.getType()).isEqualTo(StandardSQLTypeName.JSON);
+ assertThat(value1.getType()).isEqualTo(StandardSQLTypeName.JSON);
+ assertThat(value.getArrayType()).isNull();
+ assertThat(value1.getArrayType()).isNull();
+ assertThat(value.getArrayValues()).isNull();
+ assertThat(value1.getArrayType()).isNull();
+ }
+
@Test
public void testBytes() {
QueryParameterValue value = QueryParameterValue.bytes(new byte[] {1, 3});
diff --git a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java
index 479a80e89..09c9b7a7b 100644
--- a/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java
+++ b/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/it/ITBigQueryTest.java
@@ -122,6 +122,7 @@
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.common.io.BaseEncoding;
+import com.google.gson.JsonObject;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
@@ -707,6 +708,113 @@ public void testCreateTableWithRangePartitioning() {
}
}
+ @Test
+ public void testJsonType() throws InterruptedException {
+ String tableName = "test_create_table_jsontype";
+ TableId tableId = TableId.of(DATASET, tableName);
+ Schema schema = Schema.of(Field.of("jsonField", StandardSQLTypeName.JSON));
+ StandardTableDefinition standardTableDefinition = StandardTableDefinition.of(schema);
+ try {
+ // Create a table with a JSON column
+ Table createdTable = bigquery.create(TableInfo.of(tableId, standardTableDefinition));
+ assertNotNull(createdTable);
+
+ // Insert 4 rows of JSON data into the JSON column
+ Map jsonRow1 =
+ Collections.singletonMap(
+ "jsonField", "{\"student\" : {\"name\" : \"Jane\", \"id\": 10}}");
+ Map jsonRow2 =
+ Collections.singletonMap("jsonField", "{\"student\" : {\"name\" : \"Joy\", \"id\": 11}}");
+ Map jsonRow3 =
+ Collections.singletonMap(
+ "jsonField", "{\"student\" : {\"name\" : \"Alice\", \"id\": 12}}");
+ Map jsonRow4 =
+ Collections.singletonMap(
+ "jsonField", "{\"student\" : {\"name\" : \"Bijoy\", \"id\": 14}}");
+ InsertAllRequest request =
+ InsertAllRequest.newBuilder(tableId)
+ .addRow(jsonRow1)
+ .addRow(jsonRow2)
+ .addRow(jsonRow3)
+ .addRow(jsonRow4)
+ .build();
+ InsertAllResponse response = bigquery.insertAll(request);
+ assertFalse(response.hasErrors());
+ assertEquals(0, response.getInsertErrors().size());
+
+ // Query the JSON column with string positional query parameter
+ String sql =
+ "SELECT jsonField.class.student.id FROM "
+ + tableId.getTable()
+ + " WHERE JSON_VALUE(jsonField, \"$.class.student.name\") = ? ";
+ QueryParameterValue stringParameter = QueryParameterValue.string("Jane");
+ QueryJobConfiguration queryJobConfiguration =
+ QueryJobConfiguration.newBuilder(sql)
+ .setDefaultDataset(DatasetId.of(DATASET))
+ .setUseLegacySql(false)
+ .addPositionalParameter(stringParameter)
+ .build();
+ TableResult result = bigquery.query(queryJobConfiguration);
+ for (FieldValueList values : result.iterateAll()) {
+ assertEquals("10", values.get(0).getValue());
+ }
+
+ // Insert another JSON row parsed from a String with json positional query parameter
+ String dml = "INSERT INTO " + tableId.getTable() + " (jsonField) VALUES(?)";
+ QueryParameterValue jsonParameter =
+ QueryParameterValue.json("{\"class\" : {\"student\" : [{\"name\" : \"Amy\"}]}}");
+ QueryJobConfiguration dmlQueryJobConfiguration =
+ QueryJobConfiguration.newBuilder(dml)
+ .setDefaultDataset(DatasetId.of(DATASET))
+ .setUseLegacySql(false)
+ .addPositionalParameter(jsonParameter)
+ .build();
+ bigquery.query(dmlQueryJobConfiguration);
+ Page rows = bigquery.listTableData(tableId);
+ assertEquals(5, Iterables.size(rows.getValues()));
+
+ // Insert another JSON row parsed from a JsonObject with json positional query parameter
+ JsonObject jsonObject = new JsonObject();
+ jsonObject.addProperty("class", "student");
+ QueryParameterValue jsonParameter1 = QueryParameterValue.json(jsonObject);
+ QueryJobConfiguration dmlQueryJobConfiguration1 =
+ QueryJobConfiguration.newBuilder(dml)
+ .setDefaultDataset(DatasetId.of(DATASET))
+ .setUseLegacySql(false)
+ .addPositionalParameter(jsonParameter1)
+ .build();
+ bigquery.query(dmlQueryJobConfiguration1);
+ Page rows1 = bigquery.listTableData(tableId);
+ assertEquals(6, Iterables.size(rows1.getValues()));
+ int rowCount = 0;
+ for (FieldValueList row : rows1.iterateAll()) {
+ FieldValue jsonCell = row.get(0);
+ if (rowCount == 1) assertEquals("{\"class\":\"student\"}", jsonCell.getStringValue());
+ rowCount++;
+ }
+
+ // Try inserting a malformed JSON
+ QueryParameterValue badJsonParameter =
+ QueryParameterValue.json("{\"class\" : {\"student\" : [{\"name\" : \"BadBoy\"}}");
+ QueryJobConfiguration dmlQueryJobConfiguration2 =
+ QueryJobConfiguration.newBuilder(dml)
+ .setDefaultDataset(DatasetId.of(DATASET))
+ .setUseLegacySql(false)
+ .addPositionalParameter(badJsonParameter)
+ .build();
+ try {
+ bigquery.query(dmlQueryJobConfiguration2);
+ fail("Querying with malformed JSON shouldn't work");
+ } catch (BigQueryException e) {
+ BigQueryError error = e.getError();
+ assertNotNull(error);
+ assertEquals("invalidQuery", error.getReason());
+ }
+ } finally {
+ assertTrue(bigquery.delete(tableId));
+ }
+ }
+
@Test
public void testCreateTableWithConstraints() {
String tableName = "test_create_table_with_constraints";
diff --git a/pom.xml b/pom.xml
index df1183cee..3860c978e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.google.cloud
google-cloud-bigquery-parent
pom
- 2.6.2
+ 2.7.0
BigQuery Parent
https://github.com/googleapis/java-bigquery
@@ -14,7 +14,7 @@
com.google.cloud
google-cloud-shared-config
- 1.2.4
+ 1.2.6
@@ -84,7 +84,7 @@
com.google.cloud
google-cloud-bigquery
- 2.6.2
+ 2.7.0
@@ -93,6 +93,12 @@
${google-api-services-bigquery.version}
+
+ com.google.code.gson
+ gson
+ 2.8.9
+
+
junit
@@ -115,20 +121,20 @@
org.mockito
mockito-core
- 4.2.0
+ 4.3.1
test
com.google.cloud
google-cloud-storage
- 2.2.3
+ 2.3.0
test
org.assertj
assertj-core
- 2.9.1
+ 3.22.0
test
diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml
index 692846384..72c5dc009 100644
--- a/samples/install-without-bom/pom.xml
+++ b/samples/install-without-bom/pom.xml
@@ -45,19 +45,19 @@
com.google.cloud
google-cloud-bigquery
- 2.6.0
+ 2.6.2
com.google.oauth-client
google-oauth-client-java6
- 1.32.1
+ 1.33.0
com.google.oauth-client
google-oauth-client-jetty
- 1.32.1
+ 1.33.0
diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml
index 411763a3b..834fceabd 100644
--- a/samples/snapshot/pom.xml
+++ b/samples/snapshot/pom.xml
@@ -44,18 +44,18 @@
com.google.cloud
google-cloud-bigquery
- 2.6.2
+ 2.7.0
com.google.oauth-client
google-oauth-client-java6
- 1.32.1
+ 1.33.0
com.google.oauth-client
google-oauth-client-jetty
- 1.32.1
+ 1.33.0
diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml
index 6f3c794a9..ebc1260a6 100644
--- a/samples/snippets/pom.xml
+++ b/samples/snippets/pom.xml
@@ -47,7 +47,7 @@
com.google.cloud
libraries-bom
- 24.1.2
+ 24.2.0
pom
import
@@ -66,12 +66,12 @@
com.google.oauth-client
google-oauth-client-java6
- 1.32.1
+ 1.33.0
com.google.oauth-client
google-oauth-client-jetty
- 1.32.1
+ 1.33.0
diff --git a/versions.txt b/versions.txt
index 9a4015d7e..19cca9733 100644
--- a/versions.txt
+++ b/versions.txt
@@ -1,4 +1,4 @@
# Format:
# module:released-version:current-version
-google-cloud-bigquery:2.6.2:2.6.2
\ No newline at end of file
+google-cloud-bigquery:2.7.0:2.7.0
\ No newline at end of file