This is an automated email from the ASF dual-hosted git repository.
zhouyao2023 pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/seatunnel.git
The following commit(s) were added to refs/heads/dev by this push:
new 2cc680fe65 [Improve][Connector-V2][HBase] Support
DATE/TIME/TIMESTAMP/DECIMAL in sink and fix DECIMAL deserialization (#10291)
2cc680fe65 is described below
commit 2cc680fe65c48b3cd1b68d148ea174fde34a49b5
Author: yzeng1618 <[email protected]>
AuthorDate: Sun Jan 11 21:59:56 2026 +0800
[Improve][Connector-V2][HBase] Support DATE/TIME/TIMESTAMP/DECIMAL in sink
and fix DECIMAL deserialization (#10291)
Co-authored-by: zengyi <[email protected]>
---
docs/en/connector-v2/sink/Hbase.md | 9 +-
docs/zh/connector-v2/sink/Hbase.md | 9 +-
.../hbase/format/HBaseDeserializationFormat.java | 14 ++-
.../seatunnel/hbase/sink/HbaseSinkWriter.java | 56 ++++++++-
.../hbase/sink/HbaseSinkWriterTypeConvertTest.java | 134 +++++++++++++++++++++
.../seatunnel/e2e/connector/hbase/HbaseIT.java | 12 ++
.../fake-to-hbase-with-date-time-decimal.conf | 53 ++++++++
.../hbase-to-assert-with-date-time-decimal.conf | 124 +++++++++++++++++++
8 files changed, 400 insertions(+), 11 deletions(-)
diff --git a/docs/en/connector-v2/sink/Hbase.md
b/docs/en/connector-v2/sink/Hbase.md
index e405a24dd1..1cd49acfda 100644
--- a/docs/en/connector-v2/sink/Hbase.md
+++ b/docs/en/connector-v2/sink/Hbase.md
@@ -92,7 +92,14 @@ The write buffer size of hbase client, default `8 * 1024 *
1024`
### encoding [string]
-The encoding of string field, support [`utf8`, `gbk`], default `utf8`
+The encoding used for STRING/DECIMAL/DATE/TIME/TIMESTAMP/ARRAY fields, support
[`utf8`, `gbk`], default `utf8`
+
+### Data types
+
+Hbase stores bytes. The connector supports:
+
+- TINYINT/SMALLINT/INT/BIGINT/FLOAT/DOUBLE/BOOLEAN/BYTES
+- STRING/DECIMAL/DATE/TIME/TIMESTAMP/ARRAY (serialized as strings using
`encoding`)
### hbase_extra_config [config]
diff --git a/docs/zh/connector-v2/sink/Hbase.md
b/docs/zh/connector-v2/sink/Hbase.md
index 537a87470c..4d16497187 100644
--- a/docs/zh/connector-v2/sink/Hbase.md
+++ b/docs/zh/connector-v2/sink/Hbase.md
@@ -92,7 +92,14 @@ hbase 客户端的写入缓冲区大小,默认 8 * 1024 * 1024
### encoding [string]
-字符串字段的编码,支持[ utf8 , gbk],默认 utf8
+字符串类字段的编码(STRING/DECIMAL/DATE/TIME/TIMESTAMP/ARRAY),支持 [utf8, gbk],默认 utf8
+
+### 数据类型
+
+Hbase 存储字节,连接器支持:
+
+- TINYINT/SMALLINT/INT/BIGINT/FLOAT/DOUBLE/BOOLEAN/BYTES
+- STRING/DECIMAL/DATE/TIME/TIMESTAMP/ARRAY(使用 encoding 序列化为字符串后写入)
### hbase_extra_config [config]
diff --git
a/seatunnel-connectors-v2/connector-hbase/src/main/java/org/apache/seatunnel/connectors/seatunnel/hbase/format/HBaseDeserializationFormat.java
b/seatunnel-connectors-v2/connector-hbase/src/main/java/org/apache/seatunnel/connectors/seatunnel/hbase/format/HBaseDeserializationFormat.java
index 578df7101c..cb5ac74a06 100644
---
a/seatunnel-connectors-v2/connector-hbase/src/main/java/org/apache/seatunnel/connectors/seatunnel/hbase/format/HBaseDeserializationFormat.java
+++
b/seatunnel-connectors-v2/connector-hbase/src/main/java/org/apache/seatunnel/connectors/seatunnel/hbase/format/HBaseDeserializationFormat.java
@@ -29,17 +29,17 @@ import
org.apache.seatunnel.connectors.seatunnel.hbase.exception.HbaseConnectorE
import org.apache.hadoop.hbase.util.Bytes;
+import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
-import static
org.apache.seatunnel.common.utils.DateTimeUtils.Formatter.YYYY_MM_DD_HH_MM_SS;
-
public class HBaseDeserializationFormat {
private final DateUtils.Formatter dateFormat =
DateUtils.Formatter.YYYY_MM_DD;
- private final DateTimeUtils.Formatter datetimeFormat = YYYY_MM_DD_HH_MM_SS;
+ private final DateTimeUtils.Formatter datetimeFormat =
+ DateTimeUtils.Formatter.YYYY_MM_DD_HH_MM_SS;
private final TimeUtils.Formatter timeFormat =
TimeUtils.Formatter.HH_MM_SS;
public SeaTunnelRow deserialize(byte[][] rowCell, SeaTunnelRowType
seaTunnelRowType) {
@@ -68,8 +68,14 @@ public class HBaseDeserializationFormat {
case BIGINT:
return Bytes.toLong(cell);
case FLOAT:
- case DECIMAL:
return Bytes.toFloat(cell);
+ case DECIMAL:
+ String decimalAsString = Bytes.toString(cell);
+ try {
+ return new BigDecimal(decimalAsString);
+ } catch (NumberFormatException e) {
+ return new BigDecimal(Float.toString(Bytes.toFloat(cell)));
+ }
case DOUBLE:
return Bytes.toDouble(cell);
case BYTES:
diff --git
a/seatunnel-connectors-v2/connector-hbase/src/main/java/org/apache/seatunnel/connectors/seatunnel/hbase/sink/HbaseSinkWriter.java
b/seatunnel-connectors-v2/connector-hbase/src/main/java/org/apache/seatunnel/connectors/seatunnel/hbase/sink/HbaseSinkWriter.java
index 99b3cdfba5..f8d5286a35 100644
---
a/seatunnel-connectors-v2/connector-hbase/src/main/java/org/apache/seatunnel/connectors/seatunnel/hbase/sink/HbaseSinkWriter.java
+++
b/seatunnel-connectors-v2/connector-hbase/src/main/java/org/apache/seatunnel/connectors/seatunnel/hbase/sink/HbaseSinkWriter.java
@@ -24,6 +24,9 @@ import org.apache.seatunnel.api.table.type.SeaTunnelRow;
import org.apache.seatunnel.api.table.type.SeaTunnelRowType;
import org.apache.seatunnel.api.table.type.SqlType;
import org.apache.seatunnel.common.exception.CommonErrorCodeDeprecated;
+import org.apache.seatunnel.common.utils.DateTimeUtils;
+import org.apache.seatunnel.common.utils.DateUtils;
+import org.apache.seatunnel.common.utils.TimeUtils;
import org.apache.seatunnel.connectors.seatunnel.hbase.client.HbaseClient;
import org.apache.seatunnel.connectors.seatunnel.hbase.config.HbaseParameters;
import
org.apache.seatunnel.connectors.seatunnel.hbase.exception.HbaseConnectorException;
@@ -37,7 +40,11 @@ import org.apache.hadoop.hbase.util.Bytes;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.math.BigDecimal;
import java.nio.charset.Charset;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -56,6 +63,8 @@ public class HbaseSinkWriter
private final HbaseParameters hbaseParameters;
+ private final Charset charset;
+
private List<Integer> rowkeyColumnIndexes;
private int versionColumnIndex;
@@ -67,8 +76,18 @@ public class HbaseSinkWriter
HbaseParameters hbaseParameters,
List<Integer> rowkeyColumnIndexes,
int versionColumnIndex) {
+ this(seaTunnelRowType, hbaseParameters, rowkeyColumnIndexes,
versionColumnIndex, null);
+ }
+
+ HbaseSinkWriter(
+ SeaTunnelRowType seaTunnelRowType,
+ HbaseParameters hbaseParameters,
+ List<Integer> rowkeyColumnIndexes,
+ int versionColumnIndex,
+ HbaseClient hbaseClient) {
this.seaTunnelRowType = seaTunnelRowType;
this.hbaseParameters = hbaseParameters;
+ this.charset =
Charset.forName(hbaseParameters.getEnCoding().toString());
this.rowkeyColumnIndexes = rowkeyColumnIndexes;
this.versionColumnIndex = versionColumnIndex;
@@ -77,7 +96,8 @@ public class HbaseSinkWriter
hbaseParameters.getFamilyNames().getOrDefault(ALL_COLUMNS,
defaultFamilyName);
}
- this.hbaseClient = HbaseClient.createInstance(hbaseParameters);
+ this.hbaseClient =
+ hbaseClient == null ?
HbaseClient.createInstance(hbaseParameters) : hbaseClient;
}
@Override
@@ -211,13 +231,39 @@ public class HbaseSinkWriter
return Bytes.toBytes((Double) field);
case BOOLEAN:
return Bytes.toBytes((Boolean) field);
+ case BYTES:
+ return (byte[]) field;
+ case DECIMAL:
+ BigDecimal decimal =
+ field instanceof BigDecimal
+ ? (BigDecimal) field
+ : new BigDecimal(field.toString());
+ return decimal.toPlainString().getBytes(charset);
+ case DATE:
+ LocalDate date =
+ field instanceof LocalDate
+ ? (LocalDate) field
+ : DateUtils.parse(field.toString());
+ return DateUtils.toString(date,
DateUtils.Formatter.YYYY_MM_DD).getBytes(charset);
+ case TIME:
+ LocalTime time =
+ field instanceof LocalTime
+ ? (LocalTime) field
+ : TimeUtils.parse(field.toString());
+ return TimeUtils.toString(time,
TimeUtils.Formatter.HH_MM_SS).getBytes(charset);
+ case TIMESTAMP:
+ LocalDateTime timestamp =
+ field instanceof LocalDateTime
+ ? (LocalDateTime) field
+ : DateTimeUtils.parse(field.toString());
+ return DateTimeUtils.toString(
+ timestamp,
DateTimeUtils.Formatter.YYYY_MM_DD_HH_MM_SS)
+ .getBytes(charset);
case ARRAY:
String arrayAsString =
field.toString().replaceAll("\\[|\\]|\\s", "");
- return arrayAsString.getBytes(
-
Charset.forName(hbaseParameters.getEnCoding().toString()));
+ return arrayAsString.getBytes(charset);
case STRING:
- return field.toString()
-
.getBytes(Charset.forName(hbaseParameters.getEnCoding().toString()));
+ return field.toString().getBytes(charset);
default:
String errorMsg =
String.format(
diff --git
a/seatunnel-connectors-v2/connector-hbase/src/test/java/org/apache/seatunnel/connectors/seatunnel/hbase/sink/HbaseSinkWriterTypeConvertTest.java
b/seatunnel-connectors-v2/connector-hbase/src/test/java/org/apache/seatunnel/connectors/seatunnel/hbase/sink/HbaseSinkWriterTypeConvertTest.java
new file mode 100644
index 0000000000..976c1ae0a3
--- /dev/null
+++
b/seatunnel-connectors-v2/connector-hbase/src/test/java/org/apache/seatunnel/connectors/seatunnel/hbase/sink/HbaseSinkWriterTypeConvertTest.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.seatunnel.connectors.seatunnel.hbase.sink;
+
+import org.apache.seatunnel.api.table.type.BasicType;
+import org.apache.seatunnel.api.table.type.DecimalType;
+import org.apache.seatunnel.api.table.type.LocalTimeType;
+import org.apache.seatunnel.api.table.type.SeaTunnelDataType;
+import org.apache.seatunnel.api.table.type.SeaTunnelRow;
+import org.apache.seatunnel.api.table.type.SeaTunnelRowType;
+import org.apache.seatunnel.connectors.seatunnel.hbase.client.HbaseClient;
+import org.apache.seatunnel.connectors.seatunnel.hbase.config.HbaseParameters;
+import
org.apache.seatunnel.connectors.seatunnel.hbase.format.HBaseDeserializationFormat;
+
+import org.apache.hadoop.hbase.Cell;
+import org.apache.hadoop.hbase.CellUtil;
+import org.apache.hadoop.hbase.client.Put;
+import org.apache.hadoop.hbase.util.Bytes;
+
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.util.Collections;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+public class HbaseSinkWriterTypeConvertTest {
+
+ @Test
+ public void testWriteAndDeserializeTemporalAndDecimalTypes() throws
Exception {
+ SeaTunnelRowType rowType =
+ new SeaTunnelRowType(
+ new String[] {"name", "c_decimal", "c_date", "c_time",
"c_timestamp"},
+ new SeaTunnelDataType[] {
+ BasicType.STRING_TYPE,
+ new DecimalType(10, 2),
+ LocalTimeType.LOCAL_DATE_TYPE,
+ LocalTimeType.LOCAL_TIME_TYPE,
+ LocalTimeType.LOCAL_DATE_TIME_TYPE
+ });
+
+ HbaseClient hbaseClient = mock(HbaseClient.class);
+ HbaseParameters parameters =
+ HbaseParameters.builder()
+ .familyNames(Collections.singletonMap("all_columns",
"info"))
+ .build();
+
+ HbaseSinkWriter writer =
+ new HbaseSinkWriter(
+ rowType, parameters, Collections.singletonList(0), -1,
hbaseClient);
+
+ SeaTunnelRow row =
+ new SeaTunnelRow(
+ new Object[] {
+ "row1",
+ new BigDecimal("999999.90"),
+ LocalDate.parse("2012-12-21"),
+ LocalTime.parse("12:34:56"),
+ LocalDateTime.parse("2012-12-21T12:34:56")
+ });
+
+ writer.write(row);
+
+ ArgumentCaptor<Put> putCaptor = ArgumentCaptor.forClass(Put.class);
+ verify(hbaseClient).mutate(putCaptor.capture());
+ Put put = putCaptor.getValue();
+
+ assertArrayEquals(Bytes.toBytes("row1"), put.getRow());
+
+ byte[] family = Bytes.toBytes("info");
+ byte[] decimalBytes = getValue(put, family, "c_decimal");
+ byte[] dateBytes = getValue(put, family, "c_date");
+ byte[] timeBytes = getValue(put, family, "c_time");
+ byte[] timestampBytes = getValue(put, family, "c_timestamp");
+
+ assertEquals("999999.90", Bytes.toString(decimalBytes));
+ assertEquals("2012-12-21", Bytes.toString(dateBytes));
+ assertEquals("12:34:56", Bytes.toString(timeBytes));
+ assertEquals("2012-12-21 12:34:56", Bytes.toString(timestampBytes));
+
+ HBaseDeserializationFormat deserializationFormat = new
HBaseDeserializationFormat();
+ SeaTunnelRowType deserializeRowType =
+ new SeaTunnelRowType(
+ new String[] {"c_decimal", "c_date", "c_time",
"c_timestamp"},
+ new SeaTunnelDataType[] {
+ new DecimalType(10, 2),
+ LocalTimeType.LOCAL_DATE_TYPE,
+ LocalTimeType.LOCAL_TIME_TYPE,
+ LocalTimeType.LOCAL_DATE_TIME_TYPE
+ });
+
+ SeaTunnelRow deserialized =
+ deserializationFormat.deserialize(
+ new byte[][] {decimalBytes, dateBytes, timeBytes,
timestampBytes},
+ deserializeRowType);
+
+ assertEquals(new BigDecimal("999999.90"), deserialized.getField(0));
+ assertEquals(LocalDate.parse("2012-12-21"), deserialized.getField(1));
+ assertEquals(LocalTime.parse("12:34:56"), deserialized.getField(2));
+ assertEquals(LocalDateTime.parse("2012-12-21T12:34:56"),
deserialized.getField(3));
+ }
+
+ private static byte[] getValue(Put put, byte[] family, String qualifier) {
+ List<Cell> cells = put.get(family, Bytes.toBytes(qualifier));
+ assertNotNull(cells);
+ assertFalse(cells.isEmpty());
+ return CellUtil.cloneValue(cells.get(0));
+ }
+}
diff --git
a/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-hbase-e2e/src/test/java/org/apache/seatunnel/e2e/connector/hbase/HbaseIT.java
b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-hbase-e2e/src/test/java/org/apache/seatunnel/e2e/connector/hbase/HbaseIT.java
index c75b2377d5..b511cde2a1 100644
---
a/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-hbase-e2e/src/test/java/org/apache/seatunnel/e2e/connector/hbase/HbaseIT.java
+++
b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-hbase-e2e/src/test/java/org/apache/seatunnel/e2e/connector/hbase/HbaseIT.java
@@ -264,6 +264,18 @@ public class HbaseIT extends TestSuiteBase implements
TestResource {
scanner.close();
}
+ @TestTemplate
+ public void testHbaseSinkWithDateTimeDecimal(TestContainer container)
+ throws IOException, InterruptedException {
+ deleteData(table);
+ Container.ExecResult sinkExecResult =
+
container.executeJob("/fake-to-hbase-with-date-time-decimal.conf");
+ Assertions.assertEquals(0, sinkExecResult.getExitCode());
+ Container.ExecResult sourceExecResult =
+
container.executeJob("/hbase-to-assert-with-date-time-decimal.conf");
+ Assertions.assertEquals(0, sourceExecResult.getExitCode());
+ }
+
@TestTemplate
public void testHbaseSinkWithBinaryRowkey(TestContainer container)
throws IOException, InterruptedException {
diff --git
a/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-hbase-e2e/src/test/resources/fake-to-hbase-with-date-time-decimal.conf
b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-hbase-e2e/src/test/resources/fake-to-hbase-with-date-time-decimal.conf
new file mode 100644
index 0000000000..25d1f4795b
--- /dev/null
+++
b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-hbase-e2e/src/test/resources/fake-to-hbase-with-date-time-decimal.conf
@@ -0,0 +1,53 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+env {
+ parallelism = 1
+ job.mode = "BATCH"
+}
+
+source {
+ FakeSource {
+ schema {
+ fields {
+ name = string
+ c_decimal = "decimal(10, 2)"
+ c_date = date
+ c_time = time
+ c_timestamp = timestamp
+ }
+ }
+ rows = [
+ {
+ kind = INSERT
+ fields = ["A", "999999.90", "2012-12-21", "12:34:56",
"2012-12-21T12:34:56"]
+ }
+ ]
+ }
+}
+
+sink {
+ Hbase {
+ zookeeper_quorum = "hbase_e2e:2181"
+ table = "seatunnel_test"
+ rowkey_column = ["name"]
+ family_name {
+ all_columns = info
+ }
+ }
+}
+
diff --git
a/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-hbase-e2e/src/test/resources/hbase-to-assert-with-date-time-decimal.conf
b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-hbase-e2e/src/test/resources/hbase-to-assert-with-date-time-decimal.conf
new file mode 100644
index 0000000000..85b337cdfe
--- /dev/null
+++
b/seatunnel-e2e/seatunnel-connector-v2-e2e/connector-hbase-e2e/src/test/resources/hbase-to-assert-with-date-time-decimal.conf
@@ -0,0 +1,124 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+env {
+ parallelism = 1
+ job.mode = "BATCH"
+}
+
+source {
+ Hbase {
+ zookeeper_quorum = "hbase_e2e:2181"
+ table = "seatunnel_test"
+ query_columns = [
+ "rowkey",
+ "info:c_decimal",
+ "info:c_date",
+ "info:c_time",
+ "info:c_timestamp"
+ ]
+ schema = {
+ columns = [
+ {
+ name = rowkey
+ type = string
+ },
+ {
+ name = "info:c_decimal"
+ type = "decimal(10, 2)"
+ },
+ {
+ name = "info:c_date"
+ type = date
+ },
+ {
+ name = "info:c_time"
+ type = time
+ },
+ {
+ name = "info:c_timestamp"
+ type = timestamp
+ }
+ ]
+ }
+ }
+}
+
+sink {
+ Assert {
+ rules {
+ row_rules = [
+ {
+ rule_type = MAX_ROW
+ rule_value = 1
+ },
+ {
+ rule_type = MIN_ROW
+ rule_value = 1
+ }
+ ],
+ field_rules = [
+ {
+ field_name = rowkey
+ field_type = string
+ field_value = [
+ {
+ equals_to = "A"
+ }
+ ]
+ },
+ {
+ field_name = "info:c_decimal"
+ field_type = "decimal(10, 2)"
+ field_value = [
+ {
+ equals_to = "999999.90"
+ }
+ ]
+ },
+ {
+ field_name = "info:c_date"
+ field_type = date
+ field_value = [
+ {
+ equals_to = "2012-12-21"
+ }
+ ]
+ },
+ {
+ field_name = "info:c_time"
+ field_type = time
+ field_value = [
+ {
+ equals_to = "12:34:56"
+ }
+ ]
+ },
+ {
+ field_name = "info:c_timestamp"
+ field_type = timestamp
+ field_value = [
+ {
+ equals_to = "2012-12-21T12:34:56"
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
+