summaryrefslogtreecommitdiffstats
path: root/searchcore/src/tests/applyattrupdates
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
committerJon Bratseth <bratseth@yahoo-inc.com>2016-06-15 23:09:44 +0200
commit72231250ed81e10d66bfe70701e64fa5fe50f712 (patch)
tree2728bba1131a6f6e5bdf95afec7d7ff9358dac50 /searchcore/src/tests/applyattrupdates
Publish
Diffstat (limited to 'searchcore/src/tests/applyattrupdates')
-rw-r--r--searchcore/src/tests/applyattrupdates/.gitignore4
-rw-r--r--searchcore/src/tests/applyattrupdates/CMakeLists.txt9
-rw-r--r--searchcore/src/tests/applyattrupdates/applyattrupdates.cpp338
-rw-r--r--searchcore/src/tests/applyattrupdates/doctypes.cfg174
4 files changed, 525 insertions, 0 deletions
diff --git a/searchcore/src/tests/applyattrupdates/.gitignore b/searchcore/src/tests/applyattrupdates/.gitignore
new file mode 100644
index 00000000000..b7789427c09
--- /dev/null
+++ b/searchcore/src/tests/applyattrupdates/.gitignore
@@ -0,0 +1,4 @@
+.depend
+Makefile
+applyattrupdates_test
+searchcore_applyattrupdates_test_app
diff --git a/searchcore/src/tests/applyattrupdates/CMakeLists.txt b/searchcore/src/tests/applyattrupdates/CMakeLists.txt
new file mode 100644
index 00000000000..2778d0f62dc
--- /dev/null
+++ b/searchcore/src/tests/applyattrupdates/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+vespa_add_executable(searchcore_applyattrupdates_test_app
+ SOURCES
+ applyattrupdates.cpp
+ DEPENDS
+ searchcore_pcommon
+ searchcore_util
+)
+vespa_add_test(NAME searchcore_applyattrupdates_test_app COMMAND searchcore_applyattrupdates_test_app)
diff --git a/searchcore/src/tests/applyattrupdates/applyattrupdates.cpp b/searchcore/src/tests/applyattrupdates/applyattrupdates.cpp
new file mode 100644
index 00000000000..bc1f44740da
--- /dev/null
+++ b/searchcore/src/tests/applyattrupdates/applyattrupdates.cpp
@@ -0,0 +1,338 @@
+// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+#include <vespa/fastos/fastos.h>
+#include <vespa/document/config/config-documenttypes.h>
+#include <vespa/document/fieldvalue/arrayfieldvalue.h>
+#include <vespa/document/fieldvalue/bytefieldvalue.h>
+#include <vespa/document/fieldvalue/doublefieldvalue.h>
+#include <vespa/document/fieldvalue/floatfieldvalue.h>
+#include <vespa/document/fieldvalue/intfieldvalue.h>
+#include <vespa/document/fieldvalue/longfieldvalue.h>
+#include <vespa/document/fieldvalue/stringfieldvalue.h>
+#include <vespa/document/fieldvalue/weightedsetfieldvalue.h>
+#include <vespa/document/repo/documenttyperepo.h>
+#include <vespa/document/update/addvalueupdate.h>
+#include <vespa/document/update/assignvalueupdate.h>
+#include <vespa/document/update/clearvalueupdate.h>
+#include <vespa/document/update/documentupdate.h>
+#include <vespa/document/update/removevalueupdate.h>
+#include <vespa/log/log.h>
+#include <vespa/searchcore/proton/common/attrupdate.h>
+#include <vespa/searchlib/attribute/attributefactory.h>
+#include <vespa/searchlib/attribute/attributevector.hpp>
+#include <vespa/vespalib/testkit/testapp.h>
+
+LOG_SETUP("applyattrupdates_test");
+
+using namespace document;
+using search::attribute::BasicType;
+using search::attribute::Config;
+using search::attribute::CollectionType;
+
+namespace search {
+
+//-----------------------------------------------------------------------------
+
+template <typename T>
+class Vector
+{
+private:
+ std::vector<T> _vec;
+public:
+ Vector() : _vec() {}
+ size_t size() const {
+ return _vec.size();
+ }
+ Vector & pb(const T & val) {
+ _vec.push_back(val);
+ return *this;
+ }
+ const T & operator [] (size_t idx) const {
+ return _vec[idx];
+ }
+};
+
+//-----------------------------------------------------------------------------
+
+typedef AttributeVector::SP AttributePtr;
+typedef AttributeVector::WeightedInt WeightedInt;
+typedef AttributeVector::WeightedFloat WeightedFloat;
+typedef AttributeVector::WeightedString WeightedString;
+
+class Test : public vespalib::TestApp
+{
+private:
+ template <typename T, typename VectorType>
+ AttributePtr
+ create(uint32_t numDocs, T val, int32_t weight,
+ const std::string & baseName,
+ const Config &info)
+ {
+ LOG(info, "create attribute vector: %s", baseName.c_str());
+ AttributePtr vec = AttributeFactory::createAttribute(baseName, info);
+ VectorType * api = static_cast<VectorType *>(vec.get());
+ for (uint32_t i = 0; i < numDocs; ++i) {
+ if (!api->addDoc(i)) {
+ LOG(info, "failed adding doc: %u", i);
+ return AttributePtr();
+ }
+ if (api->hasMultiValue()) {
+ if (!api->append(i, val, weight)) {
+ LOG(info, "failed append to doc: %u", i);
+ }
+ } else {
+ if (!api->update(i, val)) {
+ LOG(info, "failed update doc: %u", i);
+ return AttributePtr();
+ }
+ }
+ }
+ api->commit();
+ return vec;
+ }
+
+ template <typename T>
+ bool check(const AttributePtr & vec, uint32_t docId, const Vector<T> & values) {
+ uint32_t sz = vec->getValueCount(docId);
+ if (!EXPECT_EQUAL(sz, values.size())) return false;
+ std::vector<T> buf(sz);
+ uint32_t asz = vec->get(docId, &buf[0], sz);
+ if (!EXPECT_EQUAL(sz, asz)) return false;
+ for (uint32_t i = 0; i < values.size(); ++i) {
+ if (!EXPECT_EQUAL(buf[i].getValue(), values[i].getValue())) return false;
+ if (!EXPECT_EQUAL(buf[i].getWeight(), values[i].getWeight())) return false;
+ }
+ return true;
+ }
+
+ void applyValueUpdate(AttributeVector & vec, uint32_t docId, const ValueUpdate & upd) {
+ FieldUpdate fupd(_docType->getField(vec.getName()));
+ fupd.addUpdate(upd);
+ search::AttrUpdate::handleUpdate(vec, docId, fupd);
+ vec.commit();
+ }
+
+ void applyArrayUpdates(AttributeVector & vec, const FieldValue & assign,
+ const FieldValue & first, const FieldValue & second) {
+ applyValueUpdate(vec, 0, AssignValueUpdate(assign));
+ applyValueUpdate(vec, 1, AddValueUpdate(second));
+ applyValueUpdate(vec, 2, RemoveValueUpdate(first));
+ applyValueUpdate(vec, 3, ClearValueUpdate());
+ }
+
+ void applyWeightedSetUpdates(AttributeVector & vec, const FieldValue & assign,
+ const FieldValue & first, const FieldValue & second) {
+ applyValueUpdate(vec, 0, AssignValueUpdate(assign));
+ applyValueUpdate(vec, 1, AddValueUpdate(second, 20));
+ applyValueUpdate(vec, 2, RemoveValueUpdate(first));
+ applyValueUpdate(vec, 3, ClearValueUpdate());
+ ArithmeticValueUpdate arithmetic(ArithmeticValueUpdate::Add, 10);
+ applyValueUpdate(vec, 4, MapValueUpdate(first, arithmetic));
+ }
+
+ void requireThatSingleAttributesAreUpdated();
+ void requireThatArrayAttributesAreUpdated();
+ void requireThatWeightedSetAttributesAreUpdated();
+
+ DocumentTypeRepo _repo;
+ const DocumentType* _docType;
+
+public:
+ Test();
+ int Main();
+};
+
+void
+Test::requireThatSingleAttributesAreUpdated()
+{
+ using search::attribute::getUndefined;
+ CollectionType ct(CollectionType::SINGLE);
+ {
+ BasicType bt(BasicType::INT32);
+ AttributePtr vec = create<int32_t, IntegerAttribute>(3, 32, 0,
+ "in1/int",
+ Config(bt, ct));
+ applyValueUpdate(*vec, 0, AssignValueUpdate(IntFieldValue(64)));
+ applyValueUpdate(*vec, 1, ArithmeticValueUpdate(ArithmeticValueUpdate::Add, 10));
+ applyValueUpdate(*vec, 2, ClearValueUpdate());
+ EXPECT_EQUAL(3u, vec->getNumDocs());
+ EXPECT_TRUE(check(vec, 0, Vector<WeightedInt>().pb(WeightedInt(64))));
+ EXPECT_TRUE(check(vec, 1, Vector<WeightedInt>().pb(WeightedInt(42))));
+ EXPECT_TRUE(check(vec, 2, Vector<WeightedInt>().pb(WeightedInt(getUndefined<int32_t>()))));
+ }
+ {
+ BasicType bt(BasicType::FLOAT);
+ AttributePtr vec = create<float, FloatingPointAttribute>(3, 55.5f, 0,
+ "in1/float",
+ Config(bt,
+ ct));
+ applyValueUpdate(*vec, 0, AssignValueUpdate(FloatFieldValue(77.7f)));
+ applyValueUpdate(*vec, 1, ArithmeticValueUpdate(ArithmeticValueUpdate::Add, 10));
+ applyValueUpdate(*vec, 2, ClearValueUpdate());
+ EXPECT_EQUAL(3u, vec->getNumDocs());
+ EXPECT_TRUE(check(vec, 0, Vector<WeightedFloat>().pb(WeightedFloat(77.7f))));
+ EXPECT_TRUE(check(vec, 1, Vector<WeightedFloat>().pb(WeightedFloat(65.5f))));
+ EXPECT_TRUE(std::isnan(vec->getFloat(2)));
+ }
+ {
+ BasicType bt(BasicType::STRING);
+ AttributePtr vec = create<std::string, StringAttribute>(3, "first", 0,
+ "in1/string",
+ Config(bt,
+ ct));
+ applyValueUpdate(*vec, 0, AssignValueUpdate(StringFieldValue("second")));
+ applyValueUpdate(*vec, 2, ClearValueUpdate());
+ EXPECT_EQUAL(3u, vec->getNumDocs());
+ EXPECT_TRUE(check(vec, 0, Vector<WeightedString>().pb(WeightedString("second"))));
+ EXPECT_TRUE(check(vec, 1, Vector<WeightedString>().pb(WeightedString("first"))));
+ EXPECT_TRUE(check(vec, 2, Vector<WeightedString>().pb(WeightedString(""))));
+ }
+}
+
+void
+Test::requireThatArrayAttributesAreUpdated()
+{
+ CollectionType ct(CollectionType::ARRAY);
+ {
+ BasicType bt(BasicType::INT32);
+ AttributePtr vec = create<int32_t, IntegerAttribute>(5, 32, 1,
+ "in1/aint",
+ Config(bt, ct));
+ IntFieldValue first(32);
+ IntFieldValue second(64);
+ ArrayFieldValue assign(_docType->getField("aint").getDataType());
+ assign.add(second);
+ applyArrayUpdates(*vec, assign, first, second);
+
+ EXPECT_EQUAL(5u, vec->getNumDocs());
+ EXPECT_TRUE(check(vec, 0, Vector<WeightedInt>().pb(WeightedInt(64))));
+ EXPECT_TRUE(check(vec, 1, Vector<WeightedInt>().pb(WeightedInt(32)).pb(WeightedInt(64))));
+ EXPECT_TRUE(check(vec, 2, Vector<WeightedInt>()));
+ EXPECT_TRUE(check(vec, 3, Vector<WeightedInt>()));
+ EXPECT_TRUE(check(vec, 4, Vector<WeightedInt>().pb(WeightedInt(32))));
+ }
+ {
+ BasicType bt(BasicType::FLOAT);
+ AttributePtr vec = create<float, FloatingPointAttribute>(5, 55.5f, 1,
+ "in1/afloat",
+ Config(bt,
+ ct));
+ FloatFieldValue first(55.5f);
+ FloatFieldValue second(77.7f);
+ ArrayFieldValue assign(_docType->getField("afloat").getDataType());
+ assign.add(second);
+ applyArrayUpdates(*vec, assign, first, second);
+
+ EXPECT_EQUAL(5u, vec->getNumDocs());
+ EXPECT_TRUE(check(vec, 0, Vector<WeightedFloat>().pb(WeightedFloat(77.7f))));
+ EXPECT_TRUE(check(vec, 1, Vector<WeightedFloat>().pb(WeightedFloat(55.5f)).pb(WeightedFloat(77.7f))));
+ EXPECT_TRUE(check(vec, 2, Vector<WeightedFloat>()));
+ EXPECT_TRUE(check(vec, 3, Vector<WeightedFloat>()));
+ EXPECT_TRUE(check(vec, 4, Vector<WeightedFloat>().pb(WeightedFloat(55.5f))));
+ }
+ {
+ BasicType bt(BasicType::STRING);
+ AttributePtr vec = create<std::string, StringAttribute>(5, "first", 1,
+ "in1/astring",
+ Config(bt, ct));
+ StringFieldValue first("first");
+ StringFieldValue second("second");
+ ArrayFieldValue assign(_docType->getField("astring").getDataType());
+ assign.add(second);
+ applyArrayUpdates(*vec, assign, first, second);
+
+ EXPECT_EQUAL(5u, vec->getNumDocs());
+ EXPECT_TRUE(check(vec, 0, Vector<WeightedString>().pb(WeightedString("second"))));
+ EXPECT_TRUE(check(vec, 1, Vector<WeightedString>().pb(WeightedString("first")).pb(WeightedString("second"))));
+ EXPECT_TRUE(check(vec, 2, Vector<WeightedString>()));
+ EXPECT_TRUE(check(vec, 3, Vector<WeightedString>()));
+ EXPECT_TRUE(check(vec, 4, Vector<WeightedString>().pb(WeightedString("first"))));
+ }
+}
+
+void
+Test::requireThatWeightedSetAttributesAreUpdated()
+{
+ CollectionType ct(CollectionType::WSET);
+ {
+ BasicType bt(BasicType::INT32);
+ AttributePtr vec = create<int32_t, IntegerAttribute>(5, 32, 100,
+ "in1/wsint",
+ Config(bt, ct));
+ IntFieldValue first(32);
+ IntFieldValue second(64);
+ WeightedSetFieldValue
+ assign(_docType->getField("wsint").getDataType());
+ assign.add(second, 20);
+ applyWeightedSetUpdates(*vec, assign, first, second);
+
+ EXPECT_EQUAL(5u, vec->getNumDocs());
+ EXPECT_TRUE(check(vec, 0, Vector<WeightedInt>().pb(WeightedInt(64, 20))));
+ EXPECT_TRUE(check(vec, 1, Vector<WeightedInt>().pb(WeightedInt(32, 100)).pb(WeightedInt(64, 20))));
+ EXPECT_TRUE(check(vec, 2, Vector<WeightedInt>()));
+ EXPECT_TRUE(check(vec, 3, Vector<WeightedInt>()));
+ EXPECT_TRUE(check(vec, 4, Vector<WeightedInt>().pb(WeightedInt(32, 110))));
+ }
+ {
+ BasicType bt(BasicType::FLOAT);
+ AttributePtr vec = create<float, FloatingPointAttribute>(5, 55.5f, 100,
+ "in1/wsfloat",
+ Config(bt,
+ ct));
+ FloatFieldValue first(55.5f);
+ FloatFieldValue second(77.7f);
+ WeightedSetFieldValue
+ assign(_docType->getField("wsfloat").getDataType());
+ assign.add(second, 20);
+ applyWeightedSetUpdates(*vec, assign, first, second);
+
+ EXPECT_EQUAL(5u, vec->getNumDocs());
+ EXPECT_TRUE(check(vec, 0, Vector<WeightedFloat>().pb(WeightedFloat(77.7f, 20))));
+ EXPECT_TRUE(check(vec, 1, Vector<WeightedFloat>().pb(WeightedFloat(55.5f, 100)).pb(WeightedFloat(77.7f, 20))));
+ EXPECT_TRUE(check(vec, 2, Vector<WeightedFloat>()));
+ EXPECT_TRUE(check(vec, 3, Vector<WeightedFloat>()));
+ EXPECT_TRUE(check(vec, 4, Vector<WeightedFloat>().pb(WeightedFloat(55.5f, 110))));
+ }
+ {
+ BasicType bt(BasicType::STRING);
+ AttributePtr vec = create<std::string, StringAttribute>(5, "first",
+ 100,
+ "in1/wsstring",
+ Config(bt,
+ ct));
+ StringFieldValue first("first");
+ StringFieldValue second("second");
+ WeightedSetFieldValue
+ assign(_docType->getField("wsstring").getDataType());
+ assign.add(second, 20);
+ applyWeightedSetUpdates(*vec, assign, first, second);
+
+ EXPECT_EQUAL(5u, vec->getNumDocs());
+ EXPECT_TRUE(check(vec, 0, Vector<WeightedString>().pb(WeightedString("second", 20))));
+ EXPECT_TRUE(check(vec, 1, Vector<WeightedString>().pb(WeightedString("first", 100)).pb(WeightedString("second", 20))));
+ EXPECT_TRUE(check(vec, 2, Vector<WeightedString>()));
+ EXPECT_TRUE(check(vec, 3, Vector<WeightedString>()));
+ EXPECT_TRUE(check(vec, 4, Vector<WeightedString>().pb(WeightedString("first", 110))));
+ }
+}
+
+Test::Test()
+ : _repo(readDocumenttypesConfig("doctypes.cfg")),
+ _docType(_repo.getDocumentType("testdoc"))
+{
+}
+
+int
+Test::Main()
+{
+ TEST_INIT("applyattrupdates_test");
+
+ TEST_DO(requireThatSingleAttributesAreUpdated());
+ TEST_DO(requireThatArrayAttributesAreUpdated());
+ TEST_DO(requireThatWeightedSetAttributesAreUpdated());
+
+ TEST_DONE();
+}
+
+} // namespace search
+
+TEST_APPHOOK(search::Test);
diff --git a/searchcore/src/tests/applyattrupdates/doctypes.cfg b/searchcore/src/tests/applyattrupdates/doctypes.cfg
new file mode 100644
index 00000000000..23cbf06629e
--- /dev/null
+++ b/searchcore/src/tests/applyattrupdates/doctypes.cfg
@@ -0,0 +1,174 @@
+enablecompression false
+documenttype[1]
+documenttype[0].id -1175657560
+documenttype[0].name "testdoc"
+documenttype[0].version 0
+documenttype[0].headerstruct -1636745577
+documenttype[0].bodystruct 1878320748
+documenttype[0].inherits[0]
+documenttype[0].datatype[8]
+documenttype[0].datatype[0].id 100
+documenttype[0].datatype[0].type ARRAY
+documenttype[0].datatype[0].array.element.id 0
+documenttype[0].datatype[0].map.key.id 0
+documenttype[0].datatype[0].map.value.id 0
+documenttype[0].datatype[0].wset.key.id 0
+documenttype[0].datatype[0].wset.createifnonexistent false
+documenttype[0].datatype[0].wset.removeifzero false
+documenttype[0].datatype[0].annotationref.annotation.id 0
+documenttype[0].datatype[0].sstruct.name ""
+documenttype[0].datatype[0].sstruct.version 0
+documenttype[0].datatype[0].sstruct.compression.type NONE
+documenttype[0].datatype[0].sstruct.compression.level 0
+documenttype[0].datatype[0].sstruct.compression.threshold 95
+documenttype[0].datatype[0].sstruct.compression.minsize 200
+documenttype[0].datatype[0].sstruct.field[0]
+documenttype[0].datatype[1].id 101
+documenttype[0].datatype[1].type ARRAY
+documenttype[0].datatype[1].array.element.id 1
+documenttype[0].datatype[1].map.key.id 0
+documenttype[0].datatype[1].map.value.id 0
+documenttype[0].datatype[1].wset.key.id 0
+documenttype[0].datatype[1].wset.createifnonexistent false
+documenttype[0].datatype[1].wset.removeifzero false
+documenttype[0].datatype[1].annotationref.annotation.id 0
+documenttype[0].datatype[1].sstruct.name ""
+documenttype[0].datatype[1].sstruct.version 0
+documenttype[0].datatype[1].sstruct.compression.type NONE
+documenttype[0].datatype[1].sstruct.compression.level 0
+documenttype[0].datatype[1].sstruct.compression.threshold 95
+documenttype[0].datatype[1].sstruct.compression.minsize 200
+documenttype[0].datatype[1].sstruct.field[0]
+documenttype[0].datatype[2].id 102
+documenttype[0].datatype[2].type ARRAY
+documenttype[0].datatype[2].array.element.id 2
+documenttype[0].datatype[2].map.key.id 0
+documenttype[0].datatype[2].map.value.id 0
+documenttype[0].datatype[2].wset.key.id 0
+documenttype[0].datatype[2].wset.createifnonexistent false
+documenttype[0].datatype[2].wset.removeifzero false
+documenttype[0].datatype[2].annotationref.annotation.id 0
+documenttype[0].datatype[2].sstruct.name ""
+documenttype[0].datatype[2].sstruct.version 0
+documenttype[0].datatype[2].sstruct.compression.type NONE
+documenttype[0].datatype[2].sstruct.compression.level 0
+documenttype[0].datatype[2].sstruct.compression.threshold 95
+documenttype[0].datatype[2].sstruct.compression.minsize 200
+documenttype[0].datatype[2].sstruct.field[0]
+documenttype[0].datatype[3].id 200
+documenttype[0].datatype[3].type WSET
+documenttype[0].datatype[3].array.element.id 0
+documenttype[0].datatype[3].map.key.id 0
+documenttype[0].datatype[3].map.value.id 0
+documenttype[0].datatype[3].wset.key.id 0
+documenttype[0].datatype[3].wset.createifnonexistent false
+documenttype[0].datatype[3].wset.removeifzero false
+documenttype[0].datatype[3].annotationref.annotation.id 0
+documenttype[0].datatype[3].sstruct.name ""
+documenttype[0].datatype[3].sstruct.version 0
+documenttype[0].datatype[3].sstruct.compression.type NONE
+documenttype[0].datatype[3].sstruct.compression.level 0
+documenttype[0].datatype[3].sstruct.compression.threshold 95
+documenttype[0].datatype[3].sstruct.compression.minsize 200
+documenttype[0].datatype[3].sstruct.field[0]
+documenttype[0].datatype[4].id 201
+documenttype[0].datatype[4].type WSET
+documenttype[0].datatype[4].array.element.id 0
+documenttype[0].datatype[4].map.key.id 0
+documenttype[0].datatype[4].map.value.id 0
+documenttype[0].datatype[4].wset.key.id 1
+documenttype[0].datatype[4].wset.createifnonexistent false
+documenttype[0].datatype[4].wset.removeifzero false
+documenttype[0].datatype[4].annotationref.annotation.id 0
+documenttype[0].datatype[4].sstruct.name ""
+documenttype[0].datatype[4].sstruct.version 0
+documenttype[0].datatype[4].sstruct.compression.type NONE
+documenttype[0].datatype[4].sstruct.compression.level 0
+documenttype[0].datatype[4].sstruct.compression.threshold 95
+documenttype[0].datatype[4].sstruct.compression.minsize 200
+documenttype[0].datatype[4].sstruct.field[0]
+documenttype[0].datatype[5].id 202
+documenttype[0].datatype[5].type WSET
+documenttype[0].datatype[5].array.element.id 0
+documenttype[0].datatype[5].map.key.id 0
+documenttype[0].datatype[5].map.value.id 0
+documenttype[0].datatype[5].wset.key.id 2
+documenttype[0].datatype[5].wset.createifnonexistent false
+documenttype[0].datatype[5].wset.removeifzero false
+documenttype[0].datatype[5].annotationref.annotation.id 0
+documenttype[0].datatype[5].sstruct.name ""
+documenttype[0].datatype[5].sstruct.version 0
+documenttype[0].datatype[5].sstruct.compression.type NONE
+documenttype[0].datatype[5].sstruct.compression.level 0
+documenttype[0].datatype[5].sstruct.compression.threshold 95
+documenttype[0].datatype[5].sstruct.compression.minsize 200
+documenttype[0].datatype[5].sstruct.field[0]
+documenttype[0].datatype[6].id -1636745577
+documenttype[0].datatype[6].type STRUCT
+documenttype[0].datatype[6].array.element.id 0
+documenttype[0].datatype[6].map.key.id 0
+documenttype[0].datatype[6].map.value.id 0
+documenttype[0].datatype[6].wset.key.id 0
+documenttype[0].datatype[6].wset.createifnonexistent false
+documenttype[0].datatype[6].wset.removeifzero false
+documenttype[0].datatype[6].annotationref.annotation.id 0
+documenttype[0].datatype[6].sstruct.name "testdoc.header"
+documenttype[0].datatype[6].sstruct.version 0
+documenttype[0].datatype[6].sstruct.compression.type NONE
+documenttype[0].datatype[6].sstruct.compression.level 0
+documenttype[0].datatype[6].sstruct.compression.threshold 90
+documenttype[0].datatype[6].sstruct.compression.minsize 0
+documenttype[0].datatype[6].sstruct.field[9]
+documenttype[0].datatype[6].sstruct.field[0].name "afloat"
+documenttype[0].datatype[6].sstruct.field[0].id 401182245
+documenttype[0].datatype[6].sstruct.field[0].id_v6 303812879
+documenttype[0].datatype[6].sstruct.field[0].datatype 101
+documenttype[0].datatype[6].sstruct.field[1].name "aint"
+documenttype[0].datatype[6].sstruct.field[1].id 19542829
+documenttype[0].datatype[6].sstruct.field[1].id_v6 764769238
+documenttype[0].datatype[6].sstruct.field[1].datatype 100
+documenttype[0].datatype[6].sstruct.field[2].name "astring"
+documenttype[0].datatype[6].sstruct.field[2].id 1494118564
+documenttype[0].datatype[6].sstruct.field[2].id_v6 1745177607
+documenttype[0].datatype[6].sstruct.field[2].datatype 102
+documenttype[0].datatype[6].sstruct.field[3].name "float"
+documenttype[0].datatype[6].sstruct.field[3].id 151686688
+documenttype[0].datatype[6].sstruct.field[3].id_v6 827904364
+documenttype[0].datatype[6].sstruct.field[3].datatype 1
+documenttype[0].datatype[6].sstruct.field[4].name "int"
+documenttype[0].datatype[6].sstruct.field[4].id 123383020
+documenttype[0].datatype[6].sstruct.field[4].id_v6 2014709351
+documenttype[0].datatype[6].sstruct.field[4].datatype 0
+documenttype[0].datatype[6].sstruct.field[5].name "string"
+documenttype[0].datatype[6].sstruct.field[5].id 1572342091
+documenttype[0].datatype[6].sstruct.field[5].id_v6 1847335717
+documenttype[0].datatype[6].sstruct.field[5].datatype 2
+documenttype[0].datatype[6].sstruct.field[6].name "wsfloat"
+documenttype[0].datatype[6].sstruct.field[6].id 821634779
+documenttype[0].datatype[6].sstruct.field[6].id_v6 1168403784
+documenttype[0].datatype[6].sstruct.field[6].datatype 201
+documenttype[0].datatype[6].sstruct.field[7].name "wsint"
+documenttype[0].datatype[6].sstruct.field[7].id 1160390473
+documenttype[0].datatype[6].sstruct.field[7].id_v6 1177062897
+documenttype[0].datatype[6].sstruct.field[7].datatype 200
+documenttype[0].datatype[6].sstruct.field[8].name "wsstring"
+documenttype[0].datatype[6].sstruct.field[8].id 981031285
+documenttype[0].datatype[6].sstruct.field[8].id_v6 682978193
+documenttype[0].datatype[6].sstruct.field[8].datatype 202
+documenttype[0].datatype[7].id 1878320748
+documenttype[0].datatype[7].type STRUCT
+documenttype[0].datatype[7].array.element.id 0
+documenttype[0].datatype[7].map.key.id 0
+documenttype[0].datatype[7].map.value.id 0
+documenttype[0].datatype[7].wset.key.id 0
+documenttype[0].datatype[7].wset.createifnonexistent false
+documenttype[0].datatype[7].wset.removeifzero false
+documenttype[0].datatype[7].annotationref.annotation.id 0
+documenttype[0].datatype[7].sstruct.name "testdoc.body"
+documenttype[0].datatype[7].sstruct.version 0
+documenttype[0].datatype[7].sstruct.compression.type NONE
+documenttype[0].datatype[7].sstruct.compression.level 0
+documenttype[0].datatype[7].sstruct.compression.threshold 90
+documenttype[0].datatype[7].sstruct.compression.minsize 0
+documenttype[0].datatype[7].sstruct.field[0]
+documenttype[0].annotationtype[0]