From 652d16508199bbe141d27af3bef56553a2890ba2 Mon Sep 17 00:00:00 2001 From: Sannikov Ilya Date: Fri, 22 Dec 2023 12:20:17 +0300 Subject: [PATCH] Update to version v4.13.0 --- .clang-format | 5 +- bindings.go | 73 +- bindings/builtin/builtin.go | 31 +- bindings/builtin/windows_config.go.in | 9 + bindings/builtinserver/builtinserver.go | 8 +- bindings/builtinserver/config/config.go | 25 +- bindings/builtinserver/windows_config.go.in | 9 + bindings/consts.go | 27 +- bindings/cproto/connection.go | 36 +- bindings/cproto/connection.mock.go | 10 +- bindings/cproto/cproto.go | 65 +- bindings/cproto/cproto_unit_test.go | 6 +- bindings/cproto/netbuf.go | 3 +- bindings/interface.go | 28 +- changelog.md | 18 + cjson/creflect.go | 438 ++- cjson/decoder.go | 313 ++- cjson/state.go | 14 +- cpp_src/CMakeLists.txt | 84 +- cpp_src/client/coroqueryresults.cc | 11 +- cpp_src/client/coroqueryresults.h | 5 +- cpp_src/client/cororeindexer.cc | 2 +- cpp_src/client/cororeindexer.h | 5 +- cpp_src/client/item.h | 6 +- cpp_src/client/itemimplbase.cc | 19 +- cpp_src/client/itemimplbase.h | 2 +- cpp_src/client/queryresults.h | 10 +- cpp_src/client/raftclient.cc | 2 +- cpp_src/client/raftclient.h | 2 +- cpp_src/client/reindexer.cc | 2 +- cpp_src/client/reindexer.h | 5 +- cpp_src/client/reindexerimpl.cc | 6 +- cpp_src/client/resultserializer.cc | 23 +- cpp_src/client/resultserializer.h | 4 +- cpp_src/client/rpcclient.cc | 26 +- cpp_src/client/rpcclient.h | 2 +- cpp_src/cluster/clusterizator.cc | 2 +- cpp_src/cluster/config.cc | 12 +- cpp_src/cluster/config.h | 5 + cpp_src/cluster/raftmanager.cc | 1 + .../replication/asyncdatareplicator.cc | 2 +- .../replication/clusterdatareplicator.cc | 2 +- .../cluster/replication/clusterreplthread.cc | 2 +- cpp_src/cluster/replication/leadersyncer.cc | 95 +- cpp_src/cluster/replication/leadersyncer.h | 7 +- .../cluster/replication/replicationthread.cc | 20 +- cpp_src/cluster/replication/roleswitcher.cc | 2 +- cpp_src/cluster/replication/updatesqueue.h | 1 + .../cluster/sharding/locatorserviceadapter.cc | 6 + .../cluster/sharding/locatorserviceadapter.h | 3 +- cpp_src/cluster/sharding/sharding.cc | 71 +- cpp_src/cluster/sharding/sharding.h | 1 + .../sharding/shardingcontrolrequest.cc | 17 +- .../cluster/sharding/shardingcontrolrequest.h | 23 +- cpp_src/cmd/reindexer_server/CMakeLists.txt | 10 + .../cmd/reindexer_server/contrib/Dockerfile | 2 +- cpp_src/cmd/reindexer_server/main.cc | 4 + .../sharding/go_test_sharding.sh | 4 +- .../test/get_last_rx_version.py | 28 + cpp_src/cmd/reindexer_tool/CMakeLists.txt | 13 + .../cmd/reindexer_tool/commandsexecutor.cc | 422 +-- cpp_src/cmd/reindexer_tool/commandsexecutor.h | 35 +- .../cmd/reindexer_tool/commandsprocessor.cc | 13 +- cpp_src/cmd/reindexer_tool/readme.md | 2 +- cpp_src/cmd/reindexer_tool/reindexer_tool.cc | 39 +- cpp_src/cmd/reindexer_tool/repair_tool.cc | 39 +- cpp_src/core/cbinding/reindexer_c.cc | 50 +- cpp_src/core/cbinding/reindexer_c.h | 1 - cpp_src/core/cbinding/resultserializer.cc | 35 +- cpp_src/core/cbinding/resultserializer.h | 4 +- cpp_src/core/cjson/cjsondecoder.cc | 154 +- cpp_src/core/cjson/cjsondecoder.h | 149 +- cpp_src/core/cjson/cjsontools.cc | 31 +- cpp_src/core/cjson/cjsontools.h | 7 +- cpp_src/core/cjson/ctag.h | 9 +- cpp_src/core/cjson/fieldextractor.h | 4 +- cpp_src/core/cjson/jsonbuilder.cc | 7 +- cpp_src/core/cjson/jsonbuilder.h | 19 +- cpp_src/core/cjson/tagsmatcher.h | 4 +- cpp_src/core/cjson/tagsmatcherimpl.h | 36 +- cpp_src/core/cjson/tagspathcache.h | 4 +- cpp_src/core/clusterproxy.cc | 58 +- cpp_src/core/clusterproxy.h | 29 +- cpp_src/core/comparator.cc | 11 +- cpp_src/core/comparator.h | 8 +- cpp_src/core/comparatorimpl.h | 6 +- cpp_src/core/compositearraycomparator.cc | 40 +- cpp_src/core/compositearraycomparator.h | 14 +- cpp_src/core/dbconfig.cc | 58 +- cpp_src/core/dbconfig.h | 50 +- cpp_src/core/defnsconfigs.h | 13 +- cpp_src/core/expressiontree.h | 232 +- cpp_src/core/ft/areaholder.h | 3 +- cpp_src/core/ft/ft_fast/dataprocessor.cc | 7 +- cpp_src/core/ft/ft_fast/selecter.cc | 31 +- cpp_src/core/ft/ft_fuzzy/merger/basemerger.cc | 2 +- cpp_src/core/ft/ftsetcashe.h | 2 +- cpp_src/core/ft/idrelset.cc | 6 - cpp_src/core/ft/idrelset.h | 12 +- cpp_src/core/ft/numtotext.cc | 5 +- cpp_src/core/iclientsstats.cc | 1 + cpp_src/core/iclientsstats.h | 5 + cpp_src/core/idset.cc | 10 - cpp_src/core/idset.h | 30 +- cpp_src/core/idsetcache.h | 5 +- cpp_src/core/index/index.cc | 24 +- cpp_src/core/index/index.h | 26 +- cpp_src/core/index/indexordered.cc | 31 +- cpp_src/core/index/indexordered.h | 9 +- cpp_src/core/index/indexstore.cc | 61 +- cpp_src/core/index/indexstore.h | 10 +- cpp_src/core/index/indextext/fastindextext.cc | 17 +- cpp_src/core/index/indextext/fastindextext.h | 12 +- cpp_src/core/index/indextext/fieldsgetter.h | 1 - .../core/index/indextext/fuzzyindextext.cc | 12 +- cpp_src/core/index/indextext/fuzzyindextext.h | 17 +- cpp_src/core/index/indextext/indextext.cc | 31 +- cpp_src/core/index/indextext/indextext.h | 17 +- cpp_src/core/index/indexunordered.cc | 152 +- cpp_src/core/index/indexunordered.h | 12 +- cpp_src/core/index/keyentry.h | 8 +- cpp_src/core/index/payload_map.h | 29 +- cpp_src/core/index/rtree/greenesplitter.h | 5 +- cpp_src/core/index/rtree/indexrtree.cc | 47 +- cpp_src/core/index/rtree/indexrtree.h | 9 +- cpp_src/core/index/rtree/rstarsplitter.h | 5 +- cpp_src/core/index/string_map.h | 4 +- cpp_src/core/index/ttlindex.cc | 8 +- cpp_src/core/index/ttlindex.h | 9 +- cpp_src/core/index/uuid_index.cc | 5 +- cpp_src/core/index/uuid_index.h | 9 +- cpp_src/core/indexdef.cc | 4 +- cpp_src/core/indexdef.h | 4 +- cpp_src/core/indexopts.cc | 5 +- cpp_src/core/indexopts.h | 8 +- cpp_src/core/item.cc | 3 +- cpp_src/core/itemimpl.cc | 24 +- cpp_src/core/itemimpl.h | 1 + cpp_src/core/itemmodifier.cc | 182 +- cpp_src/core/itemmodifier.h | 43 +- cpp_src/core/joincache.h | 8 +- cpp_src/core/key_value_type.h | 7 +- cpp_src/core/keyvalue/geometry.h | 3 +- cpp_src/core/keyvalue/uuid.h | 2 +- cpp_src/core/keyvalue/variant.cc | 64 +- cpp_src/core/keyvalue/variant.h | 47 +- cpp_src/core/lrucache.cc | 12 +- cpp_src/core/lrucache.h | 8 +- cpp_src/core/namespace/asyncstorage.cc | 1 - cpp_src/core/namespace/incarnationtags.h | 16 + cpp_src/core/namespace/itemsloader.cc | 2 +- cpp_src/core/namespace/itemsloader.h | 1 + cpp_src/core/namespace/namespace.cc | 28 +- cpp_src/core/namespace/namespace.h | 277 +- cpp_src/core/namespace/namespaceimpl.cc | 537 ++-- cpp_src/core/namespace/namespaceimpl.h | 80 +- cpp_src/core/namespace/namespacestat.cc | 7 + cpp_src/core/namespace/namespacestat.h | 11 +- .../namespace/snapshot/snapshothandler.cc | 9 +- cpp_src/core/nsselecter/aggregator.cc | 11 +- cpp_src/core/nsselecter/crashqueryreporter.cc | 89 +- cpp_src/core/nsselecter/crashqueryreporter.h | 5 +- cpp_src/core/nsselecter/explaincalc.cc | 15 +- cpp_src/core/nsselecter/fieldscomparator.h | 13 +- cpp_src/core/nsselecter/joinedselector.cc | 172 +- cpp_src/core/nsselecter/joinedselector.h | 22 +- cpp_src/core/nsselecter/joinedselectormock.h | 2 +- cpp_src/core/nsselecter/nsselecter.cc | 226 +- cpp_src/core/nsselecter/nsselecter.h | 3 +- cpp_src/core/nsselecter/querypreprocessor.cc | 545 ++-- cpp_src/core/nsselecter/querypreprocessor.h | 20 +- .../nsselecter/selectiteratorcontainer.cc | 254 +- .../core/nsselecter/selectiteratorcontainer.h | 17 +- cpp_src/core/parallelexecutor.cc | 43 +- cpp_src/core/parallelexecutor.h | 43 +- cpp_src/core/payload/fieldsset.h | 84 +- cpp_src/core/payload/payloadfieldvalue.cc | 2 +- cpp_src/core/payload/payloadiface.cc | 159 +- cpp_src/core/payload/payloadiface.h | 10 + cpp_src/core/payload/payloadtype.h | 2 +- cpp_src/core/payload/payloadvalue.cc | 20 +- cpp_src/core/payload/payloadvalue.h | 10 +- cpp_src/core/query/dsl/dslencoder.cc | 191 +- cpp_src/core/query/dsl/dslparser.cc | 149 +- cpp_src/core/query/dsl/dslparser.h | 9 +- cpp_src/core/query/dsl/query.json.h | 2 +- cpp_src/core/query/query.cc | 457 +-- cpp_src/core/query/query.h | 882 +++--- cpp_src/core/query/queryentry.cc | 416 ++- cpp_src/core/query/queryentry.h | 409 ++- cpp_src/core/query/sql/sqlencoder.cc | 219 +- cpp_src/core/query/sql/sqlencoder.h | 9 +- cpp_src/core/query/sql/sqlparser.cc | 304 +- cpp_src/core/query/sql/sqlparser.h | 12 +- cpp_src/core/query/sql/sqlsuggester.cc | 25 +- cpp_src/core/query/sql/sqlsuggester.h | 14 +- cpp_src/core/querycache.h | 13 +- cpp_src/core/queryresults/aggregationresult.h | 2 +- .../core/queryresults/localqueryresults.cc | 53 +- cpp_src/core/queryresults/localqueryresults.h | 30 +- cpp_src/core/queryresults/queryresults.cc | 34 +- cpp_src/core/queryresults/queryresults.h | 88 +- cpp_src/core/queryresults/tableviewbuilder.h | 4 +- cpp_src/core/rdxcontext.h | 29 +- cpp_src/core/reindexer.cc | 11 +- cpp_src/core/reindexer.h | 33 +- .../{ => reindexer_impl}/reindexerimpl.cc | 557 ++-- .../core/{ => reindexer_impl}/reindexerimpl.h | 138 +- cpp_src/core/reindexer_impl/rx_selector.cc | 508 ++++ cpp_src/core/reindexer_impl/rx_selector.h | 99 + cpp_src/core/schema.cc | 3 - cpp_src/core/schema.h | 2 +- .../core/selectfunc/nsselectfuncinterface.cc | 18 +- .../core/selectfunc/nsselectfuncinterface.h | 19 +- cpp_src/core/selectfunc/selectfunc.cc | 53 +- cpp_src/core/selectfunc/selectfunc.h | 9 +- cpp_src/core/selectkeyresult.h | 18 +- cpp_src/core/shardingproxy.cc | 202 +- cpp_src/core/shardingproxy.h | 28 +- cpp_src/core/sorting/sortexpression.cc | 4 +- cpp_src/core/storage/leveldblogger.cc | 22 + cpp_src/core/storage/leveldblogger.h | 13 + cpp_src/core/storage/leveldbstorage.cc | 9 +- cpp_src/core/storage/leveldbstorage.h | 1 + cpp_src/core/storage/rocksdbstorage.cc | 7 +- cpp_src/core/transaction/localtransaction.h | 12 +- .../core/transaction/proxiedtransaction.cc | 4 +- cpp_src/core/transaction/transactionimpl.cc | 17 +- cpp_src/core/transaction/transactionsteps.h | 27 +- cpp_src/core/type_consts.h | 42 +- cpp_src/core/type_consts_helpers.cc | 4 +- cpp_src/core/type_consts_helpers.h | 37 +- cpp_src/debug/backtrace.cc | 101 +- cpp_src/debug/backtrace.h | 1 + cpp_src/debug/terminate_handler.cpp | 1 + cpp_src/estl/chunk.h | 48 +- cpp_src/estl/chunk_buf.h | 7 +- cpp_src/estl/contexted_locks.h | 61 +- cpp_src/estl/h_vector.h | 7 +- cpp_src/estl/suffix_map.h | 2 +- cpp_src/estl/tokenizer.cc | 3 +- cpp_src/estl/tokenizer.h | 19 +- cpp_src/gtests/bench/fixtures/aggregation.cc | 2 +- .../gtests/bench/fixtures/api_tv_composite.cc | 51 +- .../gtests/bench/fixtures/api_tv_simple.cc | 140 +- cpp_src/gtests/bench/fixtures/api_tv_simple.h | 45 + .../fixtures/api_tv_simple_comparators.cc | 6 +- cpp_src/gtests/bench/fixtures/ft_fixture.cc | 165 +- cpp_src/gtests/bench/fixtures/ft_fixture.h | 18 +- cpp_src/gtests/bench/fixtures/geometry.cc | 6 +- cpp_src/gtests/bench/ft_bench.cc | 25 +- cpp_src/gtests/bench/reindexer_bench.cc | 5 +- cpp_src/gtests/tests/API/api.cc | 12 +- cpp_src/gtests/tests/API/base_tests.cc | 289 +- .../gtests/tests/fixtures/clientsstats_api.h | 2 +- .../tests/fixtures/clusterization_api.cc | 70 +- .../tests/fixtures/clusterization_api.h | 36 +- .../tests/fixtures/clusterization_async_api.h | 6 +- .../fixtures/clusterization_extras_api.h | 10 +- .../tests/fixtures/clusterization_proxy.h | 3 +- cpp_src/gtests/tests/fixtures/ft_api.cc | 13 +- cpp_src/gtests/tests/fixtures/ft_api.h | 2 +- .../gtests/tests/fixtures/fuzzing/fuzzing.h | 15 +- .../gtests/tests/fixtures/fuzzing/index.cc | 54 +- cpp_src/gtests/tests/fixtures/fuzzing/index.h | 41 +- cpp_src/gtests/tests/fixtures/fuzzing/ns.cc | 154 +- cpp_src/gtests/tests/fixtures/fuzzing/ns.h | 14 +- .../tests/fixtures/fuzzing/ns_scheme.cc | 240 +- .../gtests/tests/fixtures/fuzzing/ns_scheme.h | 188 +- .../tests/fixtures/fuzzing/query_generator.cc | 18 +- .../tests/fixtures/fuzzing/query_generator.h | 7 +- .../fixtures/fuzzing/random_generator.cc | 425 ++- .../tests/fixtures/fuzzing/random_generator.h | 182 +- .../gtests/tests/fixtures/fuzzing/types.cc | 90 + cpp_src/gtests/tests/fixtures/fuzzing/types.h | 29 + cpp_src/gtests/tests/fixtures/get_pk_api.h | 2 +- .../gtests/tests/fixtures/grpcclient_api.h | 11 +- .../tests/fixtures/item_move_semantics_api.h | 30 +- .../tests/fixtures/join_on_conditions_api.h | 8 +- .../gtests/tests/fixtures/join_selects_api.h | 7 +- .../tests/fixtures/msgpack_cproto_api.h | 16 +- cpp_src/gtests/tests/fixtures/queries_api.cc | 1256 ++++++++- cpp_src/gtests/tests/fixtures/queries_api.h | 1255 +-------- .../gtests/tests/fixtures/queries_verifier.h | 279 +- cpp_src/gtests/tests/fixtures/reindexer_api.h | 4 - .../gtests/tests/fixtures/replication_api.cc | 1 - .../gtests/tests/fixtures/replication_api.h | 2 +- .../tests/fixtures/replication_load_api.h | 5 +- .../gtests/tests/fixtures/rpcclient_api.cc | 1 - cpp_src/gtests/tests/fixtures/rpcclient_api.h | 3 +- .../tests/fixtures/runtime_indexes_api.h | 11 +- .../gtests/tests/fixtures/servercontrol.cc | 22 + cpp_src/gtests/tests/fixtures/servercontrol.h | 14 +- cpp_src/gtests/tests/fixtures/sharding_api.h | 38 +- .../gtests/tests/fixtures/storage_lazy_load.h | 7 +- .../gtests/tests/fixtures/systemhelpers.cc | 4 +- .../gtests/tests/fixtures/transaction_api.h | 4 +- cpp_src/gtests/tests/fixtures/ttl_index_api.h | 3 +- cpp_src/gtests/tests/fuzzing/fuzzing.cc | 84 +- cpp_src/gtests/tests/mocks/rpcserver_fake.cc | 9 +- cpp_src/gtests/tests/mocks/rpcserver_fake.h | 2 +- .../tests/unit/cascade_replication_test.cc | 99 + .../tests/unit/clusterization_async_test.cc | 4 +- .../tests/unit/clusterization_extras_test.cc | 2 + .../gtests/tests/unit/clusterization_test.cc | 1 + .../gtests/tests/unit/clusterproxy_test.cc | 45 +- .../gtests/tests/unit/composite_indexes_api.h | 12 +- .../tests/unit/composite_indexes_test.cc | 14 +- cpp_src/gtests/tests/unit/dsl_parser_test.cc | 116 +- .../gtests/tests/unit/equalposition_tests.cc | 15 +- cpp_src/gtests/tests/unit/ft/ft_generic.cc | 54 +- cpp_src/gtests/tests/unit/ft/ft_stress.cc | 10 +- cpp_src/gtests/tests/unit/join_test.cc | 184 +- cpp_src/gtests/tests/unit/namespace_test.cc | 299 +- cpp_src/gtests/tests/unit/queries_test.cc | 439 ++- .../tests/unit/replication_config_tests.cc | 2 +- cpp_src/gtests/tests/unit/replication_test.cc | 82 + cpp_src/gtests/tests/unit/rpcclient_test.cc | 124 +- cpp_src/gtests/tests/unit/rtree_test.cc | 13 +- .../gtests/tests/unit/runtime_indexes_test.cc | 3 + .../gtests/tests/unit/selector_plan_test.cc | 14 +- .../gtests/tests/unit/sharding_base_test.cc | 625 +++-- .../gtests/tests/unit/sharding_extras_test.cc | 32 +- cpp_src/gtests/tests/unit/snapshot_test.cc | 3 +- cpp_src/gtests/tests/unit/sql_parser_test.cc | 12 +- .../tests/unit/synccororeindexer_test.cc | 22 +- cpp_src/gtests/tests/unit/tolal_lru_cache.cc | 12 +- .../gtests/tests/unit/value_by_json_path.cc | 101 +- cpp_src/gtests/tools.h | 64 + cpp_src/net/connectinstatscollector.cc | 17 - cpp_src/net/connectinstatscollector.h | 15 +- cpp_src/net/connection.cc | 55 +- cpp_src/net/connection.h | 23 +- cpp_src/net/cproto/coroclientconnection.cc | 33 +- cpp_src/net/cproto/coroclientconnection.h | 2 +- cpp_src/net/cproto/dispatcher.cc | 30 - cpp_src/net/cproto/dispatcher.h | 39 +- cpp_src/net/cproto/serverconnection.cc | 306 +- cpp_src/net/cproto/serverconnection.h | 55 +- cpp_src/net/ev/ev.cc | 32 +- cpp_src/net/ev/ev.h | 12 +- cpp_src/net/http/router.cc | 11 +- cpp_src/net/http/router.h | 2 +- cpp_src/net/http/serverconnection.cc | 10 +- cpp_src/net/http/serverconnection.h | 10 +- cpp_src/net/iserverconnection.h | 10 +- cpp_src/net/listener.cc | 32 +- cpp_src/net/listener.h | 41 +- cpp_src/net/manualconnection.cc | 23 +- cpp_src/net/manualconnection.h | 8 +- cpp_src/net/socket.cc | 415 ++- cpp_src/net/socket.h | 101 +- cpp_src/readme.md | 6 +- cpp_src/replv3/updatesobserver.cc | 214 ++ cpp_src/replv3/updatesobserver.h | 100 + cpp_src/server/CMakeLists.txt | 15 +- cpp_src/server/clientsstats.cc | 1 + cpp_src/server/clientsstats.h | 2 +- cpp_src/server/config.cc | 19 +- cpp_src/server/config.h | 2 + cpp_src/server/contrib/server.md | 109 +- cpp_src/server/contrib/server.yml | 259 +- cpp_src/server/grpc/reindexerservice.cc | 8 +- cpp_src/server/httpserver.cc | 304 +- cpp_src/server/httpserver.h | 5 +- cpp_src/server/replv3/rpcupdatespusher.cc | 45 + cpp_src/server/replv3/rpcupdatespusher.h | 32 + cpp_src/server/rpcserver.cc | 273 +- cpp_src/server/rpcserver.h | 30 +- cpp_src/server/server.cc | 1 + cpp_src/server/server.h | 2 +- cpp_src/server/serverimpl.cc | 46 +- cpp_src/server/serverimpl.h | 1 + cpp_src/server/statscollect/istatswatcher.h | 8 +- cpp_src/server/statscollect/prometheus.cc | 20 +- cpp_src/server/statscollect/prometheus.h | 20 +- cpp_src/server/statscollect/statscollector.cc | 51 +- cpp_src/server/statscollect/statscollector.h | 24 +- cpp_src/tools/assertrx.cc | 2 +- cpp_src/tools/assertrx.h | 16 +- cpp_src/tools/compiletimemap.h | 64 +- cpp_src/tools/customlocal.cc | 27 +- cpp_src/tools/errors.h | 10 +- cpp_src/tools/filecontentwatcher.h | 44 +- cpp_src/tools/fsops.cc | 47 +- cpp_src/tools/fsops.h | 1 + cpp_src/tools/json2kv.h | 2 +- cpp_src/tools/jsonstring.h | 29 +- cpp_src/tools/lsn.cc | 12 +- cpp_src/tools/lsn.h | 98 +- cpp_src/tools/oscompat.h | 3 +- cpp_src/tools/random.cc | 13 - cpp_src/tools/random.h | 7 - cpp_src/tools/randompoint.h | 17 + cpp_src/tools/semversion.h | 1 + cpp_src/tools/serializer.cc | 16 - cpp_src/tools/serializer.h | 74 +- cpp_src/tools/stringstools.cc | 305 +- cpp_src/tools/stringstools.h | 47 +- cpp_src/tools/timetools.h | 1 + cpp_src/tools/varint.h | 44 +- cpp_src/tools/verifying_updater.h | 30 + .../double-conversion/double-conversion.cc | 4 +- cpp_src/vendor/gason/gason.cc | 2 +- cpp_src/vendor/hash/md5.cc | 10 +- cpp_src/vendor/itoa/itoa.cc | 3 +- cpp_src/vendor/murmurhash/MurmurHash3.cc | 4 +- cpp_src/vendor/yaml-cpp/binary.cpp | 2 + cpp_src/vendor/yaml-cpp/emitterutils.cpp | 1 + cpp_src/vendor/yaml-cpp/exp.cpp | 1 + cpp_src/vendor/yaml-cpp/node/detail/node.h | 4 + .../vendor/yaml-cpp/node/detail/node_data.h | 1 + .../vendor/yaml-cpp/node/detail/node_ref.h | 1 + cpp_src/vendor/yaml-cpp/node/impl.h | 9 +- cpp_src/vendor/yaml-cpp/node/node.h | 1 + cpp_src/vendor/yaml-cpp/node_data.cpp | 6 + cpp_src/vendor/yaml-cpp/stream.cpp | 1 + cpp_src/wal/walrecord.cc | 107 +- cpp_src/wal/walrecord.h | 22 + cpp_src/wal/walselecter.cc | 46 +- cpp_src/wal/waltracker.cc | 22 +- dependencies.sh | 32 +- describer.go | 48 +- dsl/dsl.go | 189 +- fulltext.md | 8 +- iterator.go | 37 +- query.go | 61 +- readme.md | 140 +- reindexer.go | 24 +- reindexer_impl.go | 268 +- result_serializer.go | 27 +- test/builtinserver_test.go | 25 +- test/dsl_test.go | 2479 ++++++++++++----- test/encdec_test.go | 177 ++ test/fields_autogen_test.go | 48 +- test/ft_stress_test.go | 11 - test/helpers/server.go | 1 + test/helpers/wait_sync.go | 266 +- test/join_test.go | 91 + test/multiple_json_paths_test.go | 532 ++++ test/queries_test.go | 87 +- test/query_test.go | 335 ++- test/reindexer_test.go | 23 +- test/replication_test.go | 4 +- test/replication_v3_compatibility_test.go | 512 ++++ test/storage_compatibility_test.go | 241 +- test/storage_test.go | 190 +- test/ttlindex_test.go | 4 +- test/update_test.go | 38 +- test/uuid_test.go | 8 +- tx.go | 7 +- 451 files changed, 20309 insertions(+), 10082 deletions(-) create mode 100644 bindings/builtin/windows_config.go.in create mode 100644 bindings/builtinserver/windows_config.go.in create mode 100644 cpp_src/cmd/reindexer_server/test/get_last_rx_version.py create mode 100644 cpp_src/core/namespace/incarnationtags.h rename cpp_src/core/{ => reindexer_impl}/reindexerimpl.cc (81%) rename cpp_src/core/{ => reindexer_impl}/reindexerimpl.h (82%) create mode 100644 cpp_src/core/reindexer_impl/rx_selector.cc create mode 100644 cpp_src/core/reindexer_impl/rx_selector.h create mode 100644 cpp_src/core/storage/leveldblogger.cc create mode 100644 cpp_src/core/storage/leveldblogger.h create mode 100644 cpp_src/gtests/tests/fixtures/fuzzing/types.cc create mode 100644 cpp_src/gtests/tests/fixtures/fuzzing/types.h delete mode 100644 cpp_src/net/cproto/dispatcher.cc create mode 100644 cpp_src/replv3/updatesobserver.cc create mode 100644 cpp_src/replv3/updatesobserver.h create mode 100644 cpp_src/server/replv3/rpcupdatespusher.cc create mode 100644 cpp_src/server/replv3/rpcupdatespusher.h delete mode 100644 cpp_src/tools/random.cc delete mode 100644 cpp_src/tools/random.h create mode 100644 cpp_src/tools/randompoint.h create mode 100644 cpp_src/tools/verifying_updater.h create mode 100644 test/multiple_json_paths_test.go create mode 100644 test/replication_v3_compatibility_test.go diff --git a/.clang-format b/.clang-format index f8238280d..a148e5429 100644 --- a/.clang-format +++ b/.clang-format @@ -6,7 +6,7 @@ AlignAfterOpenBracket: Align AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false AlignEscapedNewlinesLeft: true -AlignOperands: true +AlignOperands: true AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: false @@ -20,7 +20,7 @@ AlwaysBreakBeforeMultilineStrings: true AlwaysBreakTemplateDeclarations: true BinPackArguments: true BinPackParameters: true -BraceWrapping: +BraceWrapping: AfterClass: false AfterControlStatement: false AfterEnum: false @@ -45,7 +45,6 @@ ConstructorInitializerIndentWidth: 4 ContinuationIndentWidth: 4 Cpp11BracedListStyle: true DerivePointerAlignment: true -PointerAlignment: Left DisableFormat: false ExperimentalAutoDetectBinPacking: false ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] diff --git a/bindings.go b/bindings.go index 089f8248e..5857c5214 100644 --- a/bindings.go +++ b/bindings.go @@ -58,7 +58,7 @@ func (db *reindexerImpl) modifyItem(ctx context.Context, namespace string, ns *r rdSer := newSerializer(out.GetBuf()) rawQueryParams := rdSer.readRawQueryParams(func(nsid int) { - ns.cjsonState.ReadPayloadType(&rdSer.Serializer) + ns.cjsonState.ReadPayloadType(&rdSer.Serializer, db.binding, ns.name) }) if rawQueryParams.count == 0 { @@ -67,11 +67,9 @@ func (db *reindexerImpl) modifyItem(ctx context.Context, namespace string, ns *r resultp := rdSer.readRawtItemParams(rawQueryParams.shardId) - ns.cacheItems.Remove(cacheKey{resultp.id, resultp.shardid}) - if len(precepts) > 0 && (resultp.cptr != 0 || resultp.data != nil) && reflect.TypeOf(item).Kind() == reflect.Ptr { nsArrEntry := nsArrayEntry{ns, ns.cjsonState.Copy()} - if _, err := unpackItem(&nsArrEntry, &resultp, false, true, item); err != nil { + if _, err := unpackItem(db.binding, &nsArrEntry, &rawQueryParams, &resultp, false, true, item); err != nil { return 0, err } } @@ -122,49 +120,58 @@ func (db *reindexerImpl) getNS(namespace string) (*reindexerNamespace, error) { return ns, nil } -func unpackItem(ns *nsArrayEntry, params *rawResultItemParams, allowUnsafe bool, nonCacheableData bool, item interface{}) (interface{}, error) { +func unpackItem(bin bindings.RawBinding, ns *nsArrayEntry, rqparams *rawResultQueryParams, params *rawResultItemParams, allowUnsafe bool, nonCacheableData bool, item interface{}) (interface{}, error) { useCache := item == nil && (ns.deepCopyIface || allowUnsafe) && !nonCacheableData needCopy := ns.deepCopyIface && !allowUnsafe var err error + useCache = useCache && ns.cacheItems != nil && !params.version.IsEmpty() + var nsTag int64 + if useCache { + if nsTagData, ok := rqparams.nsIncarnationTags[params.shardid]; ok && len(nsTagData) > params.nsid { + nsTag = nsTagData[params.nsid] + } else { + useCache = false + } + } - if useCache && ns.cacheItems != nil && !params.version.IsEmpty() { - if citem, ok := ns.cacheItems.Get(cacheKey{params.id, params.shardid}); ok && citem.version == params.version { + if useCache { + cacheKey := cacheKey{id: params.id, shardID: params.shardid, nsTag: nsTag} + citem, found := ns.cacheItems.Get(cacheKey) + if found && citem.itemVersion == params.version.Counter && citem.shardingVersion == rqparams.shardingConfigVersion { item = citem.item } else { item = reflect.New(ns.rtype).Interface() - dec := ns.localCjsonState.NewDecoder(item, logger) + dec := ns.localCjsonState.NewDecoder(item, bin) if params.cptr != 0 { err = dec.DecodeCPtr(params.cptr, item) } else if params.data != nil { err = dec.Decode(params.data, item) } else { - panic(fmt.Errorf("Internal error while decoding item id %d from ns %s: cptr and data are both null", params.id, ns.name)) + panic(fmt.Errorf("rq: internal error while decoding item id %d from ns %s: cptr and data are both null", params.id, ns.name)) } if err != nil { return item, err } - if citem, ok := ns.cacheItems.Get(cacheKey{params.id, params.shardid}); ok { - if citem.version == params.version { - item = citem.item - } else if !params.version.IsCompatibleWith(citem.version) || params.version.IsNewerThen(citem.version) { - ns.cacheItems.Add(cacheKey{params.id, params.shardid}, &cacheItem{item: item, version: params.version}) - } - } else { - ns.cacheItems.Add(cacheKey{params.id, params.shardid}, &cacheItem{item: item, version: params.version}) + if !found || params.version.Counter > citem.itemVersion || citem.shardingVersion != rqparams.shardingConfigVersion { + ns.cacheItems.Add(cacheKey, &cacheItem{ + item: item, + itemVersion: params.version.Counter, + shardingVersion: rqparams.shardingConfigVersion, + }) } } } else { if item == nil { item = reflect.New(ns.rtype).Interface() } - dec := ns.localCjsonState.NewDecoder(item, logger) + dec := ns.localCjsonState.NewDecoder(item, bin) if params.cptr != 0 { err = dec.DecodeCPtr(params.cptr, item) } else if params.data != nil { err = dec.Decode(params.data, item) } else { - panic(fmt.Errorf("Internal error while decoding item id %d from ns %s: cptr and data are both null", params.id, ns.name)) + panic(fmt.Errorf("rq: internal error while decoding item id %d from ns %s: cptr and data are both null", params.id, ns.name)) } if err != nil { return item, err @@ -177,19 +184,19 @@ func unpackItem(ns *nsArrayEntry, params *rawResultItemParams, allowUnsafe bool, if deepCopy, ok := item.(DeepCopy); ok { item = deepCopy.DeepCopy() } else { - panic(fmt.Errorf("Internal error %s must implement DeepCopy interface", reflect.TypeOf(item).Name())) + panic(fmt.Errorf("rq: internal error %s must implement DeepCopy interface", reflect.TypeOf(item).Name())) } } return item, err } -func (db *reindexerImpl) rawResultToJson(rawResult []byte, jsonName string, totalName string, initJson []byte, initOffsets []int) (json []byte, offsets []int, explain []byte, err error) { +func (db *reindexerImpl) rawResultToJson(rawResult []byte, jsonName string, totalName string, initJson []byte, initOffsets []int, namespace string) (json []byte, offsets []int, explain []byte, err error) { ser := newSerializer(rawResult) rawQueryParams := ser.readRawQueryParams(func(nsid int) { var state cjson.State - state.ReadPayloadType(&ser.Serializer) + state.ReadPayloadType(&ser.Serializer, db.binding, namespace) }) explain = rawQueryParams.explainResults @@ -228,7 +235,7 @@ func (db *reindexerImpl) rawResultToJson(rawResult []byte, jsonName string, tota jsonBuf.Write(item.data) if (rawQueryParams.flags&bindings.ResultsWithJoined) != 0 && ser.GetVarUInt() != 0 { - panic("Sorry, not implemented: Can't return join query results as json") + panic("rq: sorry, not implemented: can not return join query results as json") } } jsonBuf.WriteString("]}") @@ -300,7 +307,7 @@ func (db *reindexerImpl) prepareQuery(ctx context.Context, q *Query, asJson bool result, err = db.binding.SelectQuery(ctx, ser.Bytes(), asJson, q.ptVersions, fetchCount) if err == nil && result.GetBuf() == nil { - panic(fmt.Errorf("result.Buffer is nil")) + panic(fmt.Errorf("rq: result.Buffer is nil")) } return } @@ -338,7 +345,7 @@ func (db *reindexerImpl) execToJsonQuery(ctx context.Context, q *Query, jsonRoot } defer result.Free() var explain []byte - q.json, q.jsonOffsets, explain, err = db.rawResultToJson(result.GetBuf(), jsonRoot, q.totalName, q.json, q.jsonOffsets) + q.json, q.jsonOffsets, explain, err = db.rawResultToJson(result.GetBuf(), jsonRoot, q.totalName, q.json, q.jsonOffsets, q.Namespace) if err != nil { return errJSONIterator(err) } @@ -388,16 +395,14 @@ func (db *reindexerImpl) deleteQuery(ctx context.Context, q *Query) (int, error) ser := newSerializer(result.GetBuf()) // skip total count rawQueryParams := ser.readRawQueryParams(func(nsid int) { - ns.cjsonState.ReadPayloadType(&ser.Serializer) + ns.cjsonState.ReadPayloadType(&ser.Serializer, db.binding, ns.name) }) for i := 0; i < rawQueryParams.count; i++ { - params := ser.readRawtItemParams(rawQueryParams.shardId) + _ = ser.readRawtItemParams(rawQueryParams.shardId) if (rawQueryParams.flags&bindings.ResultsWithJoined) != 0 && ser.GetVarUInt() != 0 { panic("Internal error: joined items in delete query result") } - // Update cache - ns.cacheItems.Remove(cacheKey{params.id, params.shardid}) } if !ser.Eof() { panic("Internal error: data after end of delete query result") @@ -429,16 +434,14 @@ func (db *reindexerImpl) updateQuery(ctx context.Context, q *Query) *Iterator { ser := newSerializer(result.GetBuf()) // skip total count rawQueryParams := ser.readRawQueryParams(func(nsid int) { - ns.cjsonState.ReadPayloadType(&ser.Serializer) + ns.cjsonState.ReadPayloadType(&ser.Serializer, db.binding, ns.name) }) for i := 0; i < rawQueryParams.count; i++ { - params := ser.readRawtItemParams(rawQueryParams.shardId) + _ = ser.readRawtItemParams(rawQueryParams.shardId) if (rawQueryParams.flags&bindings.ResultsWithJoined) != 0 && ser.GetVarUInt() != 0 { panic("Internal error: joined items in update query result") } - // Update cache - ns.cacheItems.Remove(cacheKey{params.id, params.shardid}) } if !ser.Eof() { @@ -547,6 +550,10 @@ func WithOpenTelemetry() interface{} { return bindings.OptionOpenTelemetry{EnableTracing: true} } +func WithStrictJoinHandlers() interface{} { + return bindings.OptionStrictJoinHandlers{EnableStrictJoinHandlers: true} +} + // WithReconnectionStrategy allows to configure the behavior during reconnect after error. // Strategy used for reconnect to server on connection error // AllowUnknownNodes allows to add dsn from cluster node, that was not set in client dsn list diff --git a/bindings/builtin/builtin.go b/bindings/builtin/builtin.go index 40b683af5..4cf448383 100644 --- a/bindings/builtin/builtin.go +++ b/bindings/builtin/builtin.go @@ -34,9 +34,11 @@ type Logger interface { // Separate mutexes for logger object itself and for reindexer_enable_logger call: // logMtx provides safe access to the logger // logEnableMtx provides atomic logic for (enable + set) and (disable + reset) procedures +// This logger is global to easily export it into CGO (however it may lead to some confusion if there are multiple builtin instances in the app) var logMtx sync.RWMutex var logEnableMtx sync.Mutex var logger Logger +var emptyLogger bindings.NullLogger var enableDebug bool @@ -235,7 +237,10 @@ func (binding *Builtin) Init(u []url.URL, options ...interface{}) error { options: C.uint16_t(connectOptions.Opts), } - caps := *bindings.DefaultBindingCapabilities().WithResultsWithShardIDs(true).WithQrIdleTimeouts(true) + caps := *bindings.DefaultBindingCapabilities(). + WithResultsWithShardIDs(true). + WithQrIdleTimeouts(true). + WithIncarnationTags(true) ccaps := C.BindingCapabilities{ caps: C.int64_t(caps.Value), } @@ -399,21 +404,6 @@ func (binding *Builtin) RenameNamespace(ctx context.Context, srcNs string, dstNs return err2go(C.reindexer_rename_namespace(binding.rx, str2c(srcNs), str2c(dstNs), ctxInfo.cCtx)) } -func (binding *Builtin) EnableStorage(ctx context.Context, path string) error { - l := len(path) - if l > 0 && path[l-1] != '/' { - path += "/" - } - - ctxInfo, err := binding.StartWatchOnCtx(ctx) - if err != nil { - return err - } - defer binding.ctxWatcher.StopWatchOnCtx(ctxInfo) - - return err2go(C.reindexer_enable_storage(binding.rx, str2c(path), ctxInfo.cCtx)) -} - func (binding *Builtin) AddIndex(ctx context.Context, namespace string, indexDef bindings.IndexDef) error { bIndexDef, err := json.Marshal(indexDef) if err != nil { @@ -630,6 +620,15 @@ func (binding *Builtin) DisableLogger() { binding.setLogger(nil) } +func (binding *Builtin) GetLogger() bindings.Logger { + logMtx.RLock() + defer logMtx.RUnlock() + if logger != nil { + return logger + } + return &emptyLogger +} + func (binding *Builtin) ReopenLogFiles() error { fmt.Println("builtin binding ReopenLogFiles method is dummy") return nil diff --git a/bindings/builtin/windows_config.go.in b/bindings/builtin/windows_config.go.in new file mode 100644 index 000000000..6901cc2a6 --- /dev/null +++ b/bindings/builtin/windows_config.go.in @@ -0,0 +1,9 @@ +// +build windows +//go:generate cmd /c cd ..\.. && mkdir build & cd build && cmake -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release .. && cmake --build . --target reindexer -- -j4 + +package builtin + +// #cgo CXXFLAGS: -std=c++17 -g -O2 -Wall -Wpedantic -Wextra -I../../cpp_src +// #cgo CFLAGS: -std=c99 -g -O2 -Wall -Wpedantic -Wno-unused-variable -I../../cpp_src +// #cgo LDFLAGS: -L${SRCDIR}/../../build/cpp_src/ @cgo_ld_flags@ -g -lshlwapi -ldbghelp -lws2_32 +import "C" diff --git a/bindings/builtinserver/builtinserver.go b/bindings/builtinserver/builtinserver.go index f369c6140..9b614d6b4 100644 --- a/bindings/builtinserver/builtinserver.go +++ b/bindings/builtinserver/builtinserver.go @@ -160,10 +160,6 @@ func (server *BuiltinServer) RenameNamespace(ctx context.Context, srcNs string, return server.builtin.RenameNamespace(ctx, srcNs, dstNs) } -func (server *BuiltinServer) EnableStorage(ctx context.Context, namespace string) error { - return server.builtin.EnableStorage(ctx, namespace) -} - func (server *BuiltinServer) AddIndex(ctx context.Context, namespace string, indexDef bindings.IndexDef) error { return server.builtin.AddIndex(ctx, namespace, indexDef) } @@ -249,6 +245,10 @@ func (server *BuiltinServer) DisableLogger() { server.builtin.DisableLogger() } +func (server *BuiltinServer) GetLogger() bindings.Logger { + return server.builtin.GetLogger() +} + func (server *BuiltinServer) ReopenLogFiles() error { return err2go(C.reopen_log_files(server.svc)) } diff --git a/bindings/builtinserver/config/config.go b/bindings/builtinserver/config/config.go index 538217a16..063e1eef9 100644 --- a/bindings/builtinserver/config/config.go +++ b/bindings/builtinserver/config/config.go @@ -13,11 +13,20 @@ type StorageConf struct { Autorepair bool `yaml:"autorepair"` } +const ServerThreadingDedicated = "dedicated" +const ServerThreadingShared = "shared" + type NetConf struct { - HTTPAddr string `yaml:"httpaddr"` - RPCAddr string `yaml:"rpcaddr"` - WebRoot string `yaml:"webroot"` - Security bool `yaml:"security"` + HTTPAddr string `yaml:"httpaddr"` + HTTPThreading string `yaml:"http_threading"` // "dedicated" or "shared" + RPCAddr string `yaml:"rpcaddr"` + RPCThreading string `yaml:"rpc_threading"` // "dedicated" or "shared" + UnixRPCAddr string `yaml:"urpcaddr"` + UnixRPCThreading string `yaml:"urpc_threading"` // "dedicated" or "shared" + WebRoot string `yaml:"webroot"` + Security bool `yaml:"security"` + HttpReadTimeoutSec int `yaml:"http_read_timeout,omitempty"` + HttpWriteTimeoutSec int `yaml:"http_write_timeout,omitempty"` } type LoggerConf struct { @@ -70,9 +79,11 @@ func DefaultServerConfig() *ServerConfig { Autorepair: false, }, Net: NetConf{ - HTTPAddr: "0.0.0.0:9088", - RPCAddr: "0.0.0.0:6534", - Security: false, + HTTPAddr: "0.0.0.0:9088", + HTTPThreading: "shared", + RPCAddr: "0.0.0.0:6534", + RPCThreading: "shared", + Security: false, }, Logger: LoggerConf{ ServerLog: "stdout", diff --git a/bindings/builtinserver/windows_config.go.in b/bindings/builtinserver/windows_config.go.in new file mode 100644 index 000000000..faafd140e --- /dev/null +++ b/bindings/builtinserver/windows_config.go.in @@ -0,0 +1,9 @@ +// +build windows +//go:generate cmd /c cd ..\.. && mkdir build && cd build && cmake -G "MinGW Makefiles" -DLINK_RESOURCES=On -DCMAKE_BUILD_TYPE=Release .. && cmake --build . --target reindexer reindexer_server_library -- -j4 ${CMAKE_BUILD_ARGS} + +package builtinserver + +// #cgo CXXFLAGS: -std=c++17 -g -O2 -Wall -Wpedantic -Wextra -I../../cpp_src +// #cgo CFLAGS: -std=c99 -g -O2 -Wall -Wpedantic -Wno-unused-variable -I../../cpp_src +// #cgo LDFLAGS: -L${SRCDIR}/../../build/cpp_src/ -L${SRCDIR}/../../build/cpp_src/server/ @cgo_ld_flags@ -g -lstdc++ -lshlwapi -ldbghelp -lws2_32 +import "C" diff --git a/bindings/consts.go b/bindings/consts.go index 8802e6c81..6c87d3229 100644 --- a/bindings/consts.go +++ b/bindings/consts.go @@ -2,7 +2,7 @@ package bindings const CInt32Max = int(^uint32(0) >> 1) -const ReindexerVersion = "v4.12.0" +const ReindexerVersion = "v4.13.0" // public go consts from type_consts.h and reindexer_ctypes.h const ( @@ -24,12 +24,14 @@ const ( INFO = 3 TRACE = 4 - AggSum = 0 - AggAvg = 1 - AggFacet = 2 - AggMin = 3 - AggMax = 4 - AggDistinct = 5 + AggSum = 0 + AggAvg = 1 + AggFacet = 2 + AggMin = 3 + AggMax = 4 + AggDistinct = 5 + AggCount = 6 + AggCountCached = 7 CollateNone = 0 CollateASCII = 1 @@ -83,6 +85,9 @@ const ( QueryUpdateFieldV2 = 25 QueryBetweenFieldsCondition = 26 QueryAlwaysFalseCondition = 27 + QueryAlwaysTrueCondition = 28 + QuerySubQueryCondition = 29 + QueryFieldSubQueryCondition = 30 LeftJoin = 0 InnerJoin = 1 @@ -110,6 +115,7 @@ const ( QueryResultExplain = 2 QueryResultShardingVersion = 3 QueryResultShardId = 4 + QueryResultIncarnationTags = 5 QueryStrictModeNotSet = 0 QueryStrictModeNone = 1 @@ -145,8 +151,9 @@ const ( ConnectOptAutorepair = 1 << 2 ConnectOptWarnVersion = 1 << 4 - BindingCapabilityQrIdleTimeouts = 1 - BindingCapabilityResultsWithShardIDs = 1 << 1 + BindingCapabilityQrIdleTimeouts = 1 + BindingCapabilityResultsWithShardIDs = 1 << 1 + BindingCapabilityNamespaceIncarnations = 1 << 2 ErrOK = 0 ErrParseSQL = 1 @@ -191,5 +198,7 @@ const ( ) const ( + ShardingNotSet = -1 ShardingProxyOff = -2 + NotSharded = -3 ) diff --git a/bindings/cproto/connection.go b/bindings/cproto/connection.go index f75ba3c6d..cf3d636fe 100644 --- a/bindings/cproto/connection.go +++ b/bindings/cproto/connection.go @@ -79,13 +79,13 @@ const ( ) type connFactory interface { - newConnection(ctx context.Context, params newConnParams) (connection, int64, error) + newConnection(ctx context.Context, params newConnParams, loggerOwner LoggerOwner) (connection, int64, error) } type connFactoryImpl struct{} -func (cf *connFactoryImpl) newConnection(ctx context.Context, params newConnParams) (connection, int64, error) { - return newConnection(ctx, params) +func (cf *connFactoryImpl) newConnection(ctx context.Context, params newConnParams, loggerOwner LoggerOwner) (connection, int64, error) { + return newConnection(ctx, params, loggerOwner) } type requestInfo struct { @@ -110,6 +110,7 @@ type connection interface { getConnection() net.Conn getSeqs() chan uint32 getRequestTimeout() uint32 + logMsg(level int, fmt string, msg ...interface{}) } type connectionImpl struct { @@ -136,7 +137,7 @@ type connectionImpl struct { requestTimeout time.Duration enableCompression bool requestDedicatedThread bool - logger Logger + loggerOwner LoggerOwner } type newConnParams struct { @@ -152,8 +153,7 @@ type newConnParams struct { func newConnection( ctx context.Context, - params newConnParams, -) ( + params newConnParams, loggerOwner LoggerOwner) ( connection, int64, error, @@ -168,6 +168,7 @@ func newConnection( requestTimeout: params.requestTimeout, enableCompression: params.enableCompression, requestDedicatedThread: params.requestDedicatedThread, + loggerOwner: loggerOwner, } for i := 0; i < queueSize; i++ { c.seqs <- uint32(i) @@ -203,10 +204,10 @@ func seqNumIsValid(seqNum uint32) bool { } func (c *connectionImpl) logMsg(level int, fmt string, msg ...interface{}) { - logMtx.RLock() - defer logMtx.RUnlock() - if logger != nil { - logger.Printf(level, fmt, msg) + if c.loggerOwner != nil { + if logger := c.loggerOwner.GetLogger(); logger != nil { + logger.Printf(level, fmt, msg) + } } } @@ -256,11 +257,18 @@ func (c *connectionImpl) deadlineTicker() { func (c *connectionImpl) connect(ctx context.Context, dsn *url.URL) (err error) { var d net.Dialer - c.conn, err = d.DialContext(ctx, "tcp", dsn.Host) - if err != nil { - return err + if dsn.Scheme == "cproto" { + if c.conn, err = d.DialContext(ctx, "tcp", dsn.Host); err != nil { + return err + } + c.conn.(*net.TCPConn).SetNoDelay(true) + } else { + d.LocalAddr = nil + if c.conn, err = d.DialContext(ctx, "unix", dsn.Host); err != nil { + return err + } } - c.conn.(*net.TCPConn).SetNoDelay(true) + c.rdBuf = bufio.NewReaderSize(c.conn, bufsCap) go c.writeLoop() diff --git a/bindings/cproto/connection.mock.go b/bindings/cproto/connection.mock.go index 0c79c9854..66954b4e2 100644 --- a/bindings/cproto/connection.mock.go +++ b/bindings/cproto/connection.mock.go @@ -58,7 +58,7 @@ func NewMockConnection(t *testing.T) *MockConnection { return mock } -func (mcf *MockConnFactory) newConnection(ctx context.Context, params newConnParams) (connection, int64, error) { +func (mcf *MockConnFactory) newConnection(ctx context.Context, params newConnParams, loggerOwner LoggerOwner) (connection, int64, error) { c, ok := mcf.expCalls["newConnection"] if !ok { mcf.t.Fatalf("unexpected call newConnection") @@ -69,7 +69,7 @@ func (mcf *MockConnFactory) newConnection(ctx context.Context, params newConnPar return ret0, ret1, ret2 } -func (rec *recMockConnFactory) newConnection(ctx context.Context, params newConnParams) *call { +func (rec *recMockConnFactory) newConnection(ctx context.Context, params newConnParams, loggerOwner LoggerOwner) *call { c := &call{ method: reflect.TypeOf((*MockConnFactory)(nil).newConnection), } @@ -115,13 +115,10 @@ func (mc *MockConnection) rpcCallNoResults(ctx context.Context, cmd int, netTime return nil } func (mc *MockConnection) rpcCallAsync(ctx context.Context, cmd int, netTimeout uint32, cmpl bindings.RawCompletion, args ...interface{}) { - return } func (mc *MockConnection) onError(err error) { - return } func (mc *MockConnection) rpcCallNoReply(ctx context.Context, cmd int, netTimeout uint32, seq uint32, args ...interface{}) { - return } func (mc *MockConnection) curError() error { c, ok := mc.expCalls["curError"] @@ -154,8 +151,7 @@ func (mc *MockConnection) getSeqs() chan uint32 { func (mc *MockConnection) getRequestTimeout() uint32 { return 0 } -func (mc *MockConnection) setLogger(logger Logger) { - return +func (mc *MockConnection) logMsg(level int, fmt string, msg ...interface{}) { } type call struct { diff --git a/bindings/cproto/cproto.go b/bindings/cproto/cproto.go index 5c66ac399..156bfbfa8 100644 --- a/bindings/cproto/cproto.go +++ b/bindings/cproto/cproto.go @@ -9,6 +9,8 @@ import ( "math/rand" "net" "net/url" + "runtime" + "strings" "sync" "sync/atomic" "time" @@ -53,16 +55,18 @@ const ( replicationTypeAsync = "async_replication" ) -var logger Logger -var logMtx sync.RWMutex +var emptyLogger bindings.NullLogger func init() { rand.Seed(time.Now().UnixNano()) bindings.RegisterBinding("cproto", new(NetCProto)) + if runtime.GOOS != "windows" { + bindings.RegisterBinding("ucproto", new(NetCProto)) + } } -type Logger interface { - Printf(level int, fmt string, msg ...interface{}) +type LoggerOwner interface { + GetLogger() bindings.Logger } type NetCProto struct { @@ -81,6 +85,8 @@ type NetCProto struct { appName string termCh chan struct{} lock sync.RWMutex + logger bindings.Logger + logMtx sync.RWMutex } type dsn struct { @@ -265,7 +271,10 @@ func (binding *NetCProto) Init(u []url.URL, options ...interface{}) (err error) connPoolSize := defConnPoolSize connPoolLBAlgorithm := defConnPoolLBAlgorithm binding.appName = defAppName - binding.caps = *bindings.DefaultBindingCapabilities().WithQrIdleTimeouts(true).WithResultsWithShardIDs(true) + binding.caps = *bindings.DefaultBindingCapabilities(). + WithQrIdleTimeouts(true). + WithResultsWithShardIDs(true). + WithIncarnationTags(true) for _, option := range options { switch v := option.(type) { @@ -319,6 +328,16 @@ func (binding *NetCProto) Init(u []url.URL, options ...interface{}) (err error) binding.dsn.connFactory = &connFactoryImpl{} binding.dsn.urls = u + for i := 0; i < len(binding.dsn.urls); i++ { + if binding.dsn.urls[i].Scheme == "ucproto" { + addrs := strings.Split(binding.dsn.urls[i].Path, ":") + if len(addrs) != 2 { + return fmt.Errorf("rq: unexpected URL format for ucproto: '%s'. Expecting ':/", binding.dsn.urls[i].Path) + } + binding.dsn.urls[i].Host = addrs[0] + binding.dsn.urls[i].Path = addrs[1] + } + } binding.connectDSN(context.Background(), connPoolSize, connPoolLBAlgorithm) binding.termCh = make(chan struct{}) go binding.pinger() @@ -353,6 +372,7 @@ func (binding *NetCProto) newPool(ctx context.Context, connPoolSize int, connPoo requestDedicatedThread: binding.dedicatedThreads.DedicatedThreads, caps: binding.caps, }, + binding, ) if serverStartTS > 0 { @@ -612,23 +632,28 @@ func (binding *NetCProto) GetReplicationStat(f func(ctx context.Context) (*bindi } func (binding *NetCProto) EnableLogger(log bindings.Logger) { - logMtx.Lock() - defer logMtx.Unlock() - logger = log + binding.logMtx.Lock() + defer binding.logMtx.Unlock() + binding.logger = log } func (binding *NetCProto) DisableLogger() { - logMtx.Lock() - defer logMtx.Unlock() - logger = nil + binding.logMtx.Lock() + defer binding.logMtx.Unlock() + binding.logger = nil } -func (binding *NetCProto) ReopenLogFiles() error { - fmt.Println("cproto binding ReopenLogFiles method is dummy") - return nil + +func (binding *NetCProto) GetLogger() bindings.Logger { + binding.logMtx.RLock() + defer binding.logMtx.RUnlock() + if binding.logger != nil { + return binding.logger + } + return &emptyLogger } -func (binding *NetCProto) EnableStorage(ctx context.Context, path string) error { - fmt.Println("cproto binding EnableStorage method is dummy") +func (binding *NetCProto) ReopenLogFiles() error { + fmt.Println("cproto binding ReopenLogFiles method is dummy") return nil } @@ -687,10 +712,10 @@ func (binding *NetCProto) getAllConns() []connection { } func (binding *NetCProto) logMsg(level int, fmt string, msg ...interface{}) { - logMtx.RLock() - defer logMtx.RUnlock() - if logger != nil { - logger.Printf(level, fmt, msg) + binding.logMtx.RLock() + defer binding.logMtx.RUnlock() + if binding.logger != nil { + binding.logger.Printf(level, fmt, msg) } } diff --git a/bindings/cproto/cproto_unit_test.go b/bindings/cproto/cproto_unit_test.go index 0aca37b8f..b19b04066 100644 --- a/bindings/cproto/cproto_unit_test.go +++ b/bindings/cproto/cproto_unit_test.go @@ -210,7 +210,7 @@ func TestConnectDSN(t *testing.T) { appName: fx.appName, enableCompression: fx.compression.EnableCompression, } - fx.mockConnFactory.expect().newConnection(ctx, params).Return(conn, int64(0), nil) + fx.mockConnFactory.expect().newConnection(ctx, params, nil).Return(conn, int64(0), nil) err := fx.connectDSN(ctx, connCount, bindings.LBRoundRobin) require.NoError(t, err) @@ -233,7 +233,7 @@ func TestGetConn(t *testing.T) { appName: fx.appName, enableCompression: fx.compression.EnableCompression, } - fx.mockConnFactory.expect().newConnection(ctx, params).Return(conn, int64(0), nil) + fx.mockConnFactory.expect().newConnection(ctx, params, nil).Return(conn, int64(0), nil) } fx.mockConns[1].expect().hasError().Return(true) @@ -264,7 +264,7 @@ func TestGetConn(t *testing.T) { appName: fx.appName, enableCompression: fx.compression.EnableCompression, } - fx.mockConnFactory.expect().newConnection(ctx, params).Return(conn, int64(0), nil) + fx.mockConnFactory.expect().newConnection(ctx, params, nil).Return(conn, int64(0), nil) } fx.mockConns[1].expect().hasError().Return(true) diff --git a/bindings/cproto/netbuf.go b/bindings/cproto/netbuf.go index 873fcc2b3..a4b6ccb3c 100644 --- a/bindings/cproto/netbuf.go +++ b/bindings/cproto/netbuf.go @@ -2,7 +2,6 @@ package cproto import ( "context" - "fmt" "sync" "github.com/restream/reindexer/v4/bindings" @@ -98,7 +97,7 @@ func (buf *NetBuffer) close() { buf.reqID = -1 buf.uid = -1 if err != nil { - fmt.Printf("rx: query close error: %v\n", err) + buf.conn.logMsg(bindings.ERROR, "rq: query close error: %v\n", err) } closeBuf.Free() } diff --git a/bindings/interface.go b/bindings/interface.go index 7ce71d529..bbaa2e20a 100644 --- a/bindings/interface.go +++ b/bindings/interface.go @@ -136,6 +136,16 @@ func (bc *BindingCapabilities) WithResultsWithShardIDs(value bool) *BindingCapab return bc } +// Enable namespaces timestamps support +func (bc *BindingCapabilities) WithIncarnationTags(value bool) *BindingCapabilities { + if value { + bc.Value |= int64(BindingCapabilityNamespaceIncarnations) + } else { + bc.Value &= ^int64(BindingCapabilityNamespaceIncarnations) + } + return bc +} + // go interface to reindexer_c.h interface type RawBuffer interface { GetBuf() []byte @@ -149,7 +159,7 @@ type TxCtx struct { UserCtx context.Context } -// FetchMore interface for partial loading results (used in cproto) +// FetchMore interface for partial loading results (used in cproto/ucproto) type FetchMore interface { Fetch(ctx context.Context, offset, limit int, asJson bool) (err error) } @@ -159,6 +169,12 @@ type Logger interface { Printf(level int, fmt string, msg ...interface{}) } +type NullLogger struct { +} + +func (NullLogger) Printf(level int, fmt string, msg ...interface{}) { +} + func NewError(text string, code int) error { return Error{text, code} } @@ -202,7 +218,6 @@ type RawBinding interface { DropNamespace(ctx context.Context, namespace string) error TruncateNamespace(ctx context.Context, namespace string) error RenameNamespace(ctx context.Context, srcNs string, dstNs string) error - EnableStorage(ctx context.Context, namespace string) error AddIndex(ctx context.Context, namespace string, indexDef IndexDef) error SetSchema(ctx context.Context, namespace string, schema SchemaDef) error UpdateIndex(ctx context.Context, namespace string, indexDef IndexDef) error @@ -225,6 +240,7 @@ type RawBinding interface { Commit(ctx context.Context, namespace string) error EnableLogger(logger Logger) DisableLogger() + GetLogger() Logger ReopenLogFiles() error Ping(ctx context.Context) error Finalize() error @@ -282,7 +298,7 @@ const ( LBPowerOfTwoChoices ) -// OptionConnPoolLoadBalancing sets algorithm, which will be used to choose connection for cproto requests' balancing +// OptionConnPoolLoadBalancing sets algorithm, which will be used to choose connection for cproto/ucproto requests' balancing type OptionConnPoolLoadBalancing struct { Algorithm LoadBalancingAlgorithm } @@ -347,6 +363,12 @@ type OptionOpenTelemetry struct { EnableTracing bool } +// OptionStrictJoinHandlers - enables join handlers check. +// If enabled, queries without required join handlers or Joinable interface will return error after execution. +type OptionStrictJoinHandlers struct { + EnableStrictJoinHandlers bool +} + // Strategy - Strategy used for reconnect to server on connection error // AllowUnknownNodes - Allow add dsn from cluster node, that not exist in original dsn list type OptionReconnectionStrategy struct { diff --git a/changelog.md b/changelog.md index 1047b8f73..9e059577a 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,21 @@ +# Version 4.13.0 *beta* (22.12.2023) + +## Core +- [ref] Deprecated `EnableStorage` API was removed + +## Replication +- [fea] Added compatibility mode for the replication. If reindexer v4 was built with `ENABLE_V3_FOLLOWERS` cmake option, it is able to replicate to the reindexer v3 followers (v3.21.0 or newer) + +## Sharding +- [fea] Sharding configuration [command](sharding.md#runtime-sharding-configuration) is now works with RAFT-cluster shards (on the empty namespaces) +- [fix] Fixed #config-namespace rollback on the sharding configuration errors + +## Go connector +- [fea] Go object cache (LRU) is now able to work with sharded clusters, RAFT-clusters and async replicas + +## Ported +- [fea/fix] Ported all the fixes from [v3.20.0](https://github.com/Restream/reindexer/releases/tag/v3.20.0) and [v3.21.0](https://github.com/Restream/reindexer/releases/tag/v3.21.0) + # Version 4.12.0 *beta* (13.10.2023) ## Sharding - [fea] Added [commands](sharding.md#runtime-sharding-configuration) for the runtime sharding configuration (on the empty namespaces) diff --git a/cjson/creflect.go b/cjson/creflect.go index aa047769a..925f17a6c 100644 --- a/cjson/creflect.go +++ b/cjson/creflect.go @@ -114,42 +114,42 @@ const hexChars = "0123456789abcdef" func createUuid(v [2]uint64) string { buf := make([]byte, 36) - buf[0] = hexChars[(v[0] >> 60) & 0xF]; - buf[1] = hexChars[(v[0] >> 56) & 0xF]; - buf[2] = hexChars[(v[0] >> 52) & 0xF]; - buf[3] = hexChars[(v[0] >> 48) & 0xF]; - buf[4] = hexChars[(v[0] >> 44) & 0xF]; - buf[5] = hexChars[(v[0] >> 40) & 0xF]; - buf[6] = hexChars[(v[0] >> 36) & 0xF]; - buf[7] = hexChars[(v[0] >> 32) & 0xF]; - buf[8] = '-'; - buf[9] = hexChars[(v[0] >> 28) & 0xF]; - buf[10] = hexChars[(v[0] >> 24) & 0xF]; - buf[11] = hexChars[(v[0] >> 20) & 0xF]; - buf[12] = hexChars[(v[0] >> 16) & 0xF]; - buf[13] = '-'; - buf[14] = hexChars[(v[0] >> 12) & 0xF]; - buf[15] = hexChars[(v[0] >> 8) & 0xF]; - buf[16] = hexChars[(v[0] >> 4) & 0xF]; - buf[17] = hexChars[v[0] & 0xF]; - buf[18] = '-'; - buf[19] = hexChars[(v[1] >> 60) & 0xF]; - buf[20] = hexChars[(v[1] >> 56) & 0xF]; - buf[21] = hexChars[(v[1] >> 52) & 0xF]; - buf[22] = hexChars[(v[1] >> 48) & 0xF]; - buf[23] = '-'; - buf[24] = hexChars[(v[1] >> 44) & 0xF]; - buf[25] = hexChars[(v[1] >> 40) & 0xF]; - buf[26] = hexChars[(v[1] >> 36) & 0xF]; - buf[27] = hexChars[(v[1] >> 32) & 0xF]; - buf[28] = hexChars[(v[1] >> 28) & 0xF]; - buf[29] = hexChars[(v[1] >> 24) & 0xF]; - buf[30] = hexChars[(v[1] >> 20) & 0xF]; - buf[31] = hexChars[(v[1] >> 16) & 0xF]; - buf[32] = hexChars[(v[1] >> 12) & 0xF]; - buf[33] = hexChars[(v[1] >> 8) & 0xF]; - buf[34] = hexChars[(v[1] >> 4) & 0xF]; - buf[35] = hexChars[v[1] & 0xF]; + buf[0] = hexChars[(v[0]>>60)&0xF] + buf[1] = hexChars[(v[0]>>56)&0xF] + buf[2] = hexChars[(v[0]>>52)&0xF] + buf[3] = hexChars[(v[0]>>48)&0xF] + buf[4] = hexChars[(v[0]>>44)&0xF] + buf[5] = hexChars[(v[0]>>40)&0xF] + buf[6] = hexChars[(v[0]>>36)&0xF] + buf[7] = hexChars[(v[0]>>32)&0xF] + buf[8] = '-' + buf[9] = hexChars[(v[0]>>28)&0xF] + buf[10] = hexChars[(v[0]>>24)&0xF] + buf[11] = hexChars[(v[0]>>20)&0xF] + buf[12] = hexChars[(v[0]>>16)&0xF] + buf[13] = '-' + buf[14] = hexChars[(v[0]>>12)&0xF] + buf[15] = hexChars[(v[0]>>8)&0xF] + buf[16] = hexChars[(v[0]>>4)&0xF] + buf[17] = hexChars[v[0]&0xF] + buf[18] = '-' + buf[19] = hexChars[(v[1]>>60)&0xF] + buf[20] = hexChars[(v[1]>>56)&0xF] + buf[21] = hexChars[(v[1]>>52)&0xF] + buf[22] = hexChars[(v[1]>>48)&0xF] + buf[23] = '-' + buf[24] = hexChars[(v[1]>>44)&0xF] + buf[25] = hexChars[(v[1]>>40)&0xF] + buf[26] = hexChars[(v[1]>>36)&0xF] + buf[27] = hexChars[(v[1]>>32)&0xF] + buf[28] = hexChars[(v[1]>>28)&0xF] + buf[29] = hexChars[(v[1]>>24)&0xF] + buf[30] = hexChars[(v[1]>>20)&0xF] + buf[31] = hexChars[(v[1]>>16)&0xF] + buf[32] = hexChars[(v[1]>>12)&0xF] + buf[33] = hexChars[(v[1]>>8)&0xF] + buf[34] = hexChars[(v[1]>>4)&0xF] + buf[35] = hexChars[v[1]&0xF] return string(buf) } @@ -221,6 +221,15 @@ func (pl *payloadIface) getArrayLen(field int) int { func (pl *payloadIface) getValue(field int, idx int, v reflect.Value) { k := v.Type().Kind() + if k == reflect.Slice { + el := reflect.New(v.Type().Elem()).Elem() + extSlice := reflect.Append(v, el) + v.Set(extSlice) + v = v.Index(v.Len() - 1) + k = v.Type().Kind() + } else if k == reflect.Array { + panic(fmt.Errorf("can not put single indexed value into the fixed size array")) + } switch pl.t.Fields[field].Type { case valueBool: v.SetBool(pl.getBool(field, idx)) @@ -249,7 +258,7 @@ func (pl *payloadIface) getValue(field int, idx int, v reflect.Value) { case valueUuid: v.SetString(pl.getUuid(field, idx)) default: - panic(fmt.Errorf("Unknown key value type %d", pl.t.Fields[field].Type)) + panic(fmt.Errorf("unknown key value type '%d'", pl.t.Fields[field].Type)) } } @@ -261,218 +270,362 @@ func (pl *payloadIface) getArray(field int, startIdx int, cnt int, v reflect.Val ptr := pl.ptr(field, startIdx, pl.t.Fields[field].Type) l := pl.getArrayLen(field) - startIdx + i := 0 switch pl.t.Fields[field].Type { case valueInt: - pi := (*[1 << 27]Cint)(ptr)[:l:l] - pu := (*[1 << 27]Cunsigned)(ptr)[:l:l] + pi := (*[1 << 27]Cint)(ptr)[:cnt:cnt] + pu := (*[1 << 27]Cunsigned)(ptr)[:cnt:cnt] switch a := v.Addr().Interface().(type) { case *[]int: - *a = make([]int, cnt, cnt) - for i := 0; i < cnt; i++ { - (*a)[i] = int(pi[i]) + if len(*a) == 0 { + *a = make([]int, cnt) + } else { + i = len(*a) + var tmp []int + tmp, *a = *a, make([]int, len(*a)+cnt) + copy(*a, tmp) + } + for j := 0; j < cnt; i, j = i+1, j+1 { + (*a)[i] = int(pi[j]) } case *[]uint: - *a = make([]uint, cnt, cnt) - for i := 0; i < cnt; i++ { - (*a)[i] = uint(pu[i]) + if len(*a) == 0 { + *a = make([]uint, cnt) + } else { + i = len(*a) + var tmp []uint + tmp, *a = *a, make([]uint, len(*a)+cnt) + copy(*a, tmp) + } + for j := 0; j < cnt; i, j = i+1, j+1 { + (*a)[i] = uint(pu[j]) } case *[]int16: - *a = make([]int16, cnt, cnt) - for i := 0; i < cnt; i++ { - (*a)[i] = int16(pi[i]) + if len(*a) == 0 { + *a = make([]int16, cnt) + } else { + i = len(*a) + var tmp []int16 + tmp, *a = *a, make([]int16, len(*a)+cnt) + copy(*a, tmp) + } + for j := 0; j < cnt; i, j = i+1, j+1 { + (*a)[i] = int16(pi[j]) } case *[]uint16: - *a = make([]uint16, cnt, cnt) - for i := 0; i < cnt; i++ { - (*a)[i] = uint16(pu[i]) + if len(*a) == 0 { + *a = make([]uint16, cnt) + } else { + i = len(*a) + var tmp []uint16 + tmp, *a = *a, make([]uint16, len(*a)+cnt) + copy(*a, tmp) + } + for j := 0; j < cnt; i, j = i+1, j+1 { + (*a)[i] = uint16(pu[j]) } case *[]int32: - *a = make([]int32, cnt, cnt) - for i := 0; i < cnt; i++ { - (*a)[i] = int32(pi[i]) + if len(*a) == 0 { + *a = make([]int32, cnt) + } else { + i = len(*a) + var tmp []int32 + tmp, *a = *a, make([]int32, len(*a)+cnt) + copy(*a, tmp) + } + for j := 0; j < cnt; i, j = i+1, j+1 { + (*a)[i] = int32(pi[j]) } case *[]uint32: - *a = make([]uint32, cnt, cnt) - for i := 0; i < cnt; i++ { - (*a)[i] = uint32(pu[i]) + if len(*a) == 0 { + *a = make([]uint32, cnt) + } else { + i = len(*a) + var tmp []uint32 + tmp, *a = *a, make([]uint32, len(*a)+cnt) + copy(*a, tmp) + } + for j := 0; j < cnt; i, j = i+1, j+1 { + (*a)[i] = uint32(pu[j]) } case *[]int8: - *a = make([]int8, cnt, cnt) - for i := 0; i < cnt; i++ { - (*a)[i] = int8(pi[i]) + if len(*a) == 0 { + *a = make([]int8, cnt) + } else { + i = len(*a) + var tmp []int8 + tmp, *a = *a, make([]int8, len(*a)+cnt) + copy(*a, tmp) + } + for j := 0; j < cnt; i, j = i+1, j+1 { + (*a)[i] = int8(pi[j]) } case *[]uint8: - *a = make([]uint8, cnt, cnt) - for i := 0; i < cnt; i++ { - (*a)[i] = uint8(pu[i]) + if len(*a) == 0 { + *a = make([]uint8, cnt) + } else { + i = len(*a) + var tmp []uint8 + tmp, *a = *a, make([]uint8, len(*a)+cnt) + copy(*a, tmp) + } + for j := 0; j < cnt; i, j = i+1, j+1 { + (*a)[i] = uint8(pu[j]) } case *[]bool: - *a = make([]bool, cnt, cnt) - for i := 0; i < cnt; i++ { - (*a)[i] = bool(pi[i] != 0) + if len(*a) == 0 { + *a = make([]bool, cnt) + } else { + i = len(*a) + var tmp []bool + tmp, *a = *a, make([]bool, len(*a)+cnt) + copy(*a, tmp) + } + for j := 0; j < cnt; i, j = i+1, j+1 { + (*a)[i] = bool(pi[j] != 0) } default: - slice := reflect.MakeSlice(v.Type(), cnt, cnt) + var slice reflect.Value + if v.Len() == 0 { + slice = reflect.MakeSlice(v.Type(), cnt, cnt) + } else { + i = v.Len() + slice = reflect.Append(v, reflect.MakeSlice(v.Type(), cnt, cnt)) + } switch v.Type().Elem().Kind() { case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - for i := 0; i < cnt; i++ { + for j := 0; j < cnt; i, j = i+1, j+1 { sv := slice.Index(i) if sv.Type().Kind() == reflect.Ptr { el := reflect.New(reflect.New(sv.Type().Elem()).Elem().Type()) - el.Elem().SetUint(uint64(pu[i])) + el.Elem().SetUint(uint64(pu[j])) sv.Set(el) } else { - sv.SetUint(uint64(pu[i])) + sv.SetUint(uint64(pu[j])) } } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - for i := 0; i < cnt; i++ { + for j := 0; j < cnt; i, j = i+1, j+1 { sv := slice.Index(i) if sv.Type().Kind() == reflect.Ptr { el := reflect.New(reflect.New(sv.Type().Elem()).Elem().Type()) - el.Elem().SetInt(int64(pi[i])) + el.Elem().SetInt(int64(pi[j])) sv.Set(el) } else { - sv.SetInt(int64(pi[i])) + sv.SetInt(int64(pi[j])) } } default: - panic(fmt.Errorf("Can't set []int to []%s", v.Type().Elem().Kind().String())) + panic(fmt.Errorf("can not convert '[]%s' to '[]int'", v.Type().Elem().Kind().String())) } v.Set(slice) } case valueInt64: switch a := v.Addr().Interface().(type) { case *[]int64: - pi := (*[1 << 27]int64)(ptr)[:l:l] - *a = make([]int64, cnt, cnt) - copy(*a, pi) + pi := (*[1 << 27]int64)(ptr)[:cnt:cnt] + if len(*a) == 0 { + *a = make([]int64, cnt) + copy(*a, pi) + } else { + *a = append(*a, pi...) + } case *[]uint64: - pi := (*[1 << 27]uint64)(ptr)[:l:l] - *a = make([]uint64, cnt, cnt) - copy(*a, pi) + pi := (*[1 << 27]uint64)(ptr)[:cnt:cnt] + if len(*a) == 0 { + *a = make([]uint64, cnt) + copy(*a, pi) + } else { + *a = append(*a, pi...) + } case *[]int: - pi := (*[1 << 27]int64)(ptr)[:l:l] - *a = make([]int, cnt, cnt) - for i := 0; i < cnt; i++ { - (*a)[i] = int(pi[i]) + pi := (*[1 << 27]int64)(ptr)[:cnt:cnt] + i := len(*a) + if i == 0 { + *a = make([]int, cnt) + } else { + var tmp []int + tmp, *a = *a, make([]int, len(*a)+cnt) + copy(*a, tmp) + } + for j := 0; j < cnt; i, j = i+1, j+1 { + (*a)[i] = int(pi[j]) } case *[]uint: - pi := (*[1 << 27]uint64)(ptr)[:l:l] - *a = make([]uint, cnt, cnt) - for i := 0; i < cnt; i++ { - (*a)[i] = uint(pi[i]) + pi := (*[1 << 27]uint64)(ptr)[:cnt] + i := len(*a) + if i == 0 { + *a = make([]uint, cnt) + } else { + var tmp []uint + tmp, *a = *a, make([]uint, len(*a)+cnt) + copy(*a, tmp) + } + for j := 0; j < cnt; i, j = i+1, j+1 { + (*a)[i] = uint(pi[j]) } default: - slice := reflect.MakeSlice(v.Type(), cnt, cnt) + var slice reflect.Value + i := v.Len() + if i == 0 { + slice = reflect.MakeSlice(v.Type(), cnt, cnt) + } else { + slice = reflect.Append(v, reflect.MakeSlice(v.Type(), cnt, cnt)) + } switch v.Type().Elem().Kind() { case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - pi := (*[1 << 27]uint64)(ptr)[:l:l] - for i := 0; i < cnt; i++ { + pi := (*[1 << 27]uint64)(ptr)[:cnt:cnt] + for j := 0; j < cnt; i, j = i+1, j+1 { sv := slice.Index(i) if sv.Type().Kind() == reflect.Ptr { el := reflect.New(reflect.New(sv.Type().Elem()).Elem().Type()) - el.Elem().SetUint(uint64(pi[i])) + el.Elem().SetUint(uint64(pi[j])) sv.Set(el) } else { - sv.SetUint(uint64(pi[i])) + sv.SetUint(uint64(pi[j])) } } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - pi := (*[1 << 27]int64)(ptr)[:l:l] - for i := 0; i < cnt; i++ { + pi := (*[1 << 27]int64)(ptr)[:cnt:cnt] + for j := 0; j < cnt; i, j = i+1, j+1 { sv := slice.Index(i) if sv.Type().Kind() == reflect.Ptr { el := reflect.New(reflect.New(sv.Type().Elem()).Elem().Type()) - el.Elem().SetInt(int64(pi[i])) + el.Elem().SetInt(int64(pi[j])) sv.Set(el) } else { - sv.SetInt(int64(pi[i])) + sv.SetInt(int64(pi[j])) } } default: - panic(fmt.Errorf("Can't set []int64 to []%s", v.Type().Elem().Kind().String())) + panic(fmt.Errorf("can not convert '[]%s' to '[]int64'", v.Type().Elem().Kind().String())) } v.Set(slice) } case valueDouble: - pi := (*[1 << 27]Cdouble)(ptr)[:l:l] + pi := (*[1 << 27]Cdouble)(ptr)[:cnt:cnt] if v.Kind() == reflect.Array { if v.Len() < cnt { - panic(fmt.Errorf("Can't set %d values to array of %d elements", cnt, v.Len())) + panic(fmt.Errorf("can not set %d values to array of %d elements", cnt, v.Len())) } switch v.Type().Elem().Kind() { case reflect.Float32: - for i := 0; i < cnt; i++ { + for i = 0; i < cnt; i++ { v.Index(i).SetFloat(float64(float32(pi[i]))) } case reflect.Float64: - for i := 0; i < cnt; i++ { + for i = 0; i < cnt; i++ { v.Index(i).SetFloat(float64(pi[i])) } default: - panic(fmt.Errorf("Can't set []double to []%s", v.Type().Elem().Kind().String())) + panic(fmt.Errorf("can not convert '[]%s' to '[]double'", v.Type().Elem().Kind().String())) } } else { switch a := v.Addr().Interface().(type) { case *[]float64: - *a = make([]float64, cnt, cnt) - for i := 0; i < cnt; i++ { - (*a)[i] = float64(pi[i]) + if len(*a) == 0 { + *a = make([]float64, cnt) + } else { + i = len(*a) + var tmp []float64 + tmp, *a = *a, make([]float64, len(*a)+cnt) + copy(*a, tmp) + } + for j := 0; j < cnt; i, j = i+1, j+1 { + (*a)[i] = float64(pi[j]) } case *[]float32: - *a = make([]float32, cnt, cnt) - for i := 0; i < cnt; i++ { - (*a)[i] = float32(pi[i]) + if len(*a) == 0 { + *a = make([]float32, cnt) + } else { + i = len(*a) + var tmp []float32 + tmp, *a = *a, make([]float32, len(*a)+cnt) + copy(*a, tmp) + } + for j := 0; j < cnt; i, j = i+1, j+1 { + (*a)[i] = float32(pi[j]) } default: - slice := reflect.MakeSlice(v.Type(), cnt, cnt) - for i := 0; i < cnt; i++ { + var slice reflect.Value + if v.Len() == 0 { + slice = reflect.MakeSlice(v.Type(), cnt, cnt) + } else { + i = v.Len() + slice = reflect.Append(v, reflect.MakeSlice(v.Type(), cnt, cnt)) + } + for j := 0; j < cnt; i, j = i+1, j+1 { sv := slice.Index(i) if sv.Type().Kind() == reflect.Ptr { el := reflect.New(reflect.New(sv.Type().Elem()).Elem().Type()) - el.Elem().SetFloat(float64(pi[i])) + el.Elem().SetFloat(float64(pi[j])) sv.Set(el) } else { - sv.SetFloat(float64(pi[i])) + sv.SetFloat(float64(pi[j])) } } v.Set(slice) } } case valueBool: - pb := (*[1 << 27]Cbool)(ptr)[:l:l] + pb := (*[1 << 27]Cbool)(ptr)[:cnt:cnt] switch a := v.Addr().Interface().(type) { case *[]bool: - *a = make([]bool, cnt, cnt) - for i := 0; i < cnt; i++ { - (*a)[i] = bool(pb[i] != 0) + if len(*a) == 0 { + *a = make([]bool, cnt) + } else { + i = len(*a) + var tmp []bool + tmp, *a = *a, make([]bool, len(*a)+cnt) + copy(*a, tmp) + } + for j := 0; j < cnt; i, j = i+1, j+1 { + (*a)[i] = bool(pb[j] != 0) } default: - slice := reflect.MakeSlice(v.Type(), cnt, cnt) - for i := 0; i < cnt; i++ { + var slice reflect.Value + if v.Len() == 0 { + slice = reflect.MakeSlice(v.Type(), cnt, cnt) + } else { + i = v.Len() + slice = reflect.Append(v, reflect.MakeSlice(v.Type(), cnt, cnt)) + } + for j := 0; j < cnt; i, j = i+1, j+1 { sv := slice.Index(i) if sv.Type().Kind() == reflect.Ptr { el := reflect.New(reflect.New(sv.Type().Elem()).Elem().Type()) - el.Elem().SetBool(bool(pb[i] != 0)) + el.Elem().SetBool(bool(pb[j] != 0)) sv.Set(el) } else { - sv.SetBool(bool(pb[i] != 0)) + sv.SetBool(bool(pb[j] != 0)) } } v.Set(slice) } case valueString: if a, ok := v.Addr().Interface().(*[]string); ok { - *a = make([]string, cnt, cnt) - for i := 0; i < cnt; i++ { - (*a)[i] = pl.getString(field, i+startIdx) + if len(*a) == 0 { + *a = make([]string, cnt) + } else { + i = len(*a) + var tmp []string + tmp, *a = *a, make([]string, len(*a)+cnt) + copy(*a, tmp) + } + for j := 0; j < cnt; i, j = i+1, j+1 { + (*a)[i] = pl.getString(field, j+startIdx) } } else { - slice := reflect.MakeSlice(v.Type(), cnt, cnt) - for i := 0; i < cnt; i++ { - s := pl.getString(field, i+startIdx) + var slice reflect.Value + if v.Len() == 0 { + slice = reflect.MakeSlice(v.Type(), cnt, cnt) + } else { + i = v.Len() + slice = reflect.Append(v, reflect.MakeSlice(v.Type(), cnt, cnt)) + } + for j := 0; j < cnt; i, j = i+1, j+1 { + s := pl.getString(field, j+startIdx) sv := slice.Index(i) if sv.Type().Kind() == reflect.Ptr { el := reflect.New(reflect.New(sv.Type().Elem()).Elem().Type()) @@ -485,28 +638,41 @@ func (pl *payloadIface) getArray(field int, startIdx int, cnt int, v reflect.Val v.Set(slice) } case valueUuid: - pi := (*[1 << 27]uint64)(ptr)[:l * 2:l * 2] + pi := (*[1 << 27]uint64)(ptr)[: l*2 : l*2] if a, ok := v.Addr().Interface().(*[]string); ok { - *a = make([]string, cnt, cnt) - for i := 0; i < cnt; i++ { - (*a)[i] = createUuid([2]uint64{pi[i * 2], pi[i * 2 + 1]}) + if len(*a) == 0 { + *a = make([]string, cnt) + } else { + i = len(*a) + var tmp []string + tmp, *a = *a, make([]string, len(*a)+cnt) + copy(*a, tmp) + } + for j := 0; j < cnt; i, j = i+1, j+1 { + (*a)[i] = createUuid([2]uint64{pi[j*2], pi[j*2+1]}) } } else { - slice := reflect.MakeSlice(v.Type(), cnt, cnt) - for i := 0; i < cnt; i++ { + var slice reflect.Value + if v.Len() == 0 { + slice = reflect.MakeSlice(v.Type(), cnt, cnt) + } else { + i = v.Len() + slice = reflect.Append(v, reflect.MakeSlice(v.Type(), cnt, cnt)) + } + for j := 0; j < cnt; i, j = i+1, j+1 { sv := slice.Index(i) if sv.Type().Kind() == reflect.Ptr { el := reflect.New(reflect.New(sv.Type().Elem()).Elem().Type()) - el.Elem().SetString(createUuid([2]uint64{pi[i * 2], pi[i * 2 + 1]})) + el.Elem().SetString(createUuid([2]uint64{pi[j*2], pi[j*2+1]})) sv.Set(el) } else { - sv.SetString(createUuid([2]uint64{pi[i * 2], pi[i * 2 + 1]})) + sv.SetString(createUuid([2]uint64{pi[j*2], pi[j*2+1]})) } } v.Set(slice) } default: - panic(fmt.Errorf("Got C array with elements of unknown C type %d in field '%s' for go type '%s'", pl.t.Fields[field].Type, pl.t.Fields[field].Name, v.Type().Elem().Kind().String())) + panic(fmt.Errorf("got C array with elements of unknown C type %d in field '%s' for go type '%s'", pl.t.Fields[field].Type, pl.t.Fields[field].Name, v.Type().Elem().Kind().String())) } } diff --git a/cjson/decoder.go b/cjson/decoder.go index dc9667ced..23ffe5dfc 100644 --- a/cjson/decoder.go +++ b/cjson/decoder.go @@ -8,6 +8,8 @@ import ( "strconv" "time" "unsafe" + + "github.com/restream/reindexer/v4/bindings" ) var ( @@ -15,15 +17,15 @@ var ( ifaceSliceType = reflect.TypeOf(ifaceSlice) ) -type Logger interface { - Printf(level int, fmt string, msg ...interface{}) +type LoggerOwner interface { + GetLogger() bindings.Logger } type Decoder struct { - ser *Serializer - state *State - ctagsCache *ctagsCache - logger Logger + ser *Serializer + state *State + ctagsCache *ctagsCache + loggerOwner LoggerOwner } const MaxIndexes = 256 @@ -119,7 +121,7 @@ func skipTag(rdser *Serializer, tagType int) { case TAG_UUID: rdser.GetUuid() default: - panic(fmt.Errorf("Can't skip tagType %s", tagTypeName(tagType))) + panic(fmt.Errorf("can not skip tagType '%s'", tagTypeName(tagType))) } } @@ -132,7 +134,7 @@ func asInt(rdser *Serializer, tagType int) int64 { case TAG_DOUBLE: return int64(rdser.GetDouble()) default: - panic(fmt.Errorf("Can't convert tagType %s to int", tagTypeName(tagType))) + panic(fmt.Errorf("can not convert tagType '%s' to 'int'", tagTypeName(tagType))) } } @@ -143,7 +145,7 @@ func asFloat(rdser *Serializer, tagType int) float64 { case TAG_DOUBLE: return rdser.GetDouble() default: - panic(fmt.Errorf("Can't convert tagType %s to float", tagTypeName(tagType))) + panic(fmt.Errorf("can not convert tagType '%s' to 'float'", tagTypeName(tagType))) } } @@ -154,7 +156,7 @@ func asString(rdser *Serializer, tagType int) string { case TAG_UUID: return rdser.GetUuid() default: - panic(fmt.Errorf("Can't convert tagType %s to string", tagTypeName(tagType))) + panic(fmt.Errorf("can not convert tagType '%s' to 'string'", tagTypeName(tagType))) } } @@ -181,43 +183,105 @@ func asIface(rdser *Serializer, tagType int) interface{} { case TAG_UUID: return rdser.GetUuid() default: - panic(fmt.Errorf("Can't convert tagType %s to iface", tagTypeName(tagType))) + panic(fmt.Errorf("can not convert tagType '%s' to 'interface'", tagTypeName(tagType))) } } -func mkSlice(v *reflect.Value, count int) { +func mkSlice(v *reflect.Value, count int) (offset int) { + offset = v.Len() switch a := v.Addr().Interface().(type) { case *[]string: - *a = make([]string, count, count) + if offset == 0 { + *a = make([]string, count) + } else { + *a = append(*a, make([]string, count)...) + } case *[]int: - *a = make([]int, count, count) + if offset == 0 { + *a = make([]int, count) + } else { + *a = append(*a, make([]int, count)...) + } case *[]int64: - *a = make([]int64, count, count) + if offset == 0 { + *a = make([]int64, count) + } else { + *a = append(*a, make([]int64, count)...) + } case *[]int32: - *a = make([]int32, count, count) + if offset == 0 { + *a = make([]int32, count) + } else { + *a = append(*a, make([]int32, count)...) + } case *[]int16: - *a = make([]int16, count, count) + if offset == 0 { + *a = make([]int16, count) + } else { + *a = append(*a, make([]int16, count)...) + } case *[]int8: - *a = make([]int8, count, count) + if offset == 0 { + *a = make([]int8, count) + } else { + *a = append(*a, make([]int8, count)...) + } case *[]uint: - *a = make([]uint, count, count) + if offset == 0 { + *a = make([]uint, count) + } else { + *a = append(*a, make([]uint, count)...) + } case *[]uint64: - *a = make([]uint64, count, count) + if offset == 0 { + *a = make([]uint64, count) + } else { + *a = append(*a, make([]uint64, count)...) + } case *[]uint32: - *a = make([]uint32, count, count) + if offset == 0 { + *a = make([]uint32, count) + } else { + *a = append(*a, make([]uint32, count)...) + } case *[]uint16: - *a = make([]uint16, count, count) + if offset == 0 { + *a = make([]uint16, count) + } else { + *a = append(*a, make([]uint16, count)...) + } case *[]uint8: - *a = make([]uint8, count, count) + if offset == 0 { + *a = make([]uint8, count) + } else { + *a = append(*a, make([]uint8, count)...) + } case *[]float64: - *a = make([]float64, count, count) + if offset == 0 { + *a = make([]float64, count) + } else { + *a = append(*a, make([]float64, count)...) + } case *[]float32: - *a = make([]float32, count, count) + if offset == 0 { + *a = make([]float32, count) + } else { + *a = append(*a, make([]float32, count)...) + } case *[]bool: - *a = make([]bool, count, count) + if offset == 0 { + *a = make([]bool, count) + } else { + *a = append(*a, make([]bool, count)...) + } default: - v.Set(reflect.MakeSlice(v.Type(), count, count)) + if offset == 0 { + v.Set(reflect.MakeSlice(v.Type(), count, count)) + } else { + v.Set(reflect.AppendSlice(*v, reflect.MakeSlice(v.Type(), count, count))) + } } + return } func mkValue(ctagType int) (v reflect.Value) { @@ -235,7 +299,7 @@ func mkValue(ctagType int) (v reflect.Value) { case TAG_ARRAY: v = reflect.New(ifaceSliceType).Elem() default: - panic(fmt.Errorf("Invalid ctagType=%d", ctagType)) + panic(fmt.Errorf("invalid ctagType=%d", ctagType)) } return v } @@ -249,22 +313,25 @@ func (dec *Decoder) decodeSlice(pl *payloadIface, rdser *Serializer, v *reflect. var ptr unsafe.Pointer k := v.Kind() + offset := 0 switch k { case reflect.Slice: - mkSlice(v, count) + offset = mkSlice(v, count) ptr = unsafe.Pointer(v.Pointer()) case reflect.Interface: origV = *v *v = reflect.ValueOf(reflect.New(ifaceSliceType).Interface()).Elem() - mkSlice(v, count) + offset = mkSlice(v, count) ptr = unsafe.Pointer(v.Pointer()) case reflect.Array: if v.Len() < count { - panic(fmt.Errorf("Array bounds overflow need %d, len=%d", count, v.Len())) + panic(fmt.Errorf("array bounds overflow. Required %d, but array len is %d", count, v.Len())) } ptr = unsafe.Pointer(v.Index(0).Addr().Pointer()) + // offset is 0 + // No concatenation for the fixed size arrays default: - panic(fmt.Errorf("Can't set array to %s", v.Type().Kind().String())) + panic(fmt.Errorf("can not convert '%s' to 'array'", v.Type().Kind().String())) } if subtag != TAG_OBJECT { @@ -277,12 +344,12 @@ func (dec *Decoder) decodeSlice(pl *payloadIface, rdser *Serializer, v *reflect. switch k { case reflect.Int: if !isPtr { - sl := (*[1 << 28]int)(ptr)[:count:count] + sl := (*[1 << 28]int)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { sl[i] = int(asInt(rdser, subtag)) } } else { - sl := (*[1 << 28]*int)(ptr)[:count:count] + sl := (*[1 << 28]*int)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { u := int(asInt(rdser, subtag)) sl[i] = &u @@ -290,12 +357,12 @@ func (dec *Decoder) decodeSlice(pl *payloadIface, rdser *Serializer, v *reflect. } case reflect.Uint: if !isPtr { - sl := (*[1 << 28]uint)(ptr)[:count:count] + sl := (*[1 << 28]uint)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { sl[i] = uint(asInt(rdser, subtag)) } } else { - sl := (*[1 << 28]*uint)(ptr)[:count:count] + sl := (*[1 << 28]*uint)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { u := uint(asInt(rdser, subtag)) sl[i] = &u @@ -303,12 +370,12 @@ func (dec *Decoder) decodeSlice(pl *payloadIface, rdser *Serializer, v *reflect. } case reflect.Int64: if !isPtr { - sl := (*[1 << 27]int64)(ptr)[:count:count] + sl := (*[1 << 27]int64)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { sl[i] = int64(asInt(rdser, subtag)) } } else { - sl := (*[1 << 28]*int64)(ptr)[:count:count] + sl := (*[1 << 28]*int64)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { u := int64(asInt(rdser, subtag)) sl[i] = &u @@ -316,12 +383,12 @@ func (dec *Decoder) decodeSlice(pl *payloadIface, rdser *Serializer, v *reflect. } case reflect.Uint64: if !isPtr { - sl := (*[1 << 27]uint64)(ptr)[:count:count] + sl := (*[1 << 27]uint64)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { sl[i] = uint64(asInt(rdser, subtag)) } } else { - sl := (*[1 << 28]*uint64)(ptr)[:count:count] + sl := (*[1 << 28]*uint64)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { u := uint64(asInt(rdser, subtag)) sl[i] = &u @@ -329,12 +396,12 @@ func (dec *Decoder) decodeSlice(pl *payloadIface, rdser *Serializer, v *reflect. } case reflect.Int32: if !isPtr { - sl := (*[1 << 28]int32)(ptr)[:count:count] + sl := (*[1 << 28]int32)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { sl[i] = int32(asInt(rdser, subtag)) } } else { - sl := (*[1 << 28]*int32)(ptr)[:count:count] + sl := (*[1 << 28]*int32)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { u := int32(asInt(rdser, subtag)) sl[i] = &u @@ -342,12 +409,12 @@ func (dec *Decoder) decodeSlice(pl *payloadIface, rdser *Serializer, v *reflect. } case reflect.Uint32: if !isPtr { - sl := (*[1 << 28]uint32)(ptr)[:count:count] + sl := (*[1 << 28]uint32)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { sl[i] = uint32(asInt(rdser, subtag)) } } else { - sl := (*[1 << 28]*uint32)(ptr)[:count:count] + sl := (*[1 << 28]*uint32)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { u := uint32(asInt(rdser, subtag)) sl[i] = &u @@ -355,12 +422,12 @@ func (dec *Decoder) decodeSlice(pl *payloadIface, rdser *Serializer, v *reflect. } case reflect.Int16: if !isPtr { - sl := (*[1 << 29]int16)(ptr)[:count:count] + sl := (*[1 << 29]int16)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { sl[i] = int16(asInt(rdser, subtag)) } } else { - sl := (*[1 << 28]*int16)(ptr)[:count:count] + sl := (*[1 << 28]*int16)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { u := int16(asInt(rdser, subtag)) sl[i] = &u @@ -368,12 +435,12 @@ func (dec *Decoder) decodeSlice(pl *payloadIface, rdser *Serializer, v *reflect. } case reflect.Uint16: if !isPtr { - sl := (*[1 << 29]uint16)(ptr)[:count:count] + sl := (*[1 << 29]uint16)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { sl[i] = uint16(asInt(rdser, subtag)) } } else { - sl := (*[1 << 28]*uint16)(ptr)[:count:count] + sl := (*[1 << 28]*uint16)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { u := uint16(asInt(rdser, subtag)) sl[i] = &u @@ -381,12 +448,12 @@ func (dec *Decoder) decodeSlice(pl *payloadIface, rdser *Serializer, v *reflect. } case reflect.Int8: if !isPtr { - sl := (*[1 << 30]int8)(ptr)[:count:count] + sl := (*[1 << 30]int8)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { sl[i] = int8(asInt(rdser, subtag)) } } else { - sl := (*[1 << 28]*int8)(ptr)[:count:count] + sl := (*[1 << 28]*int8)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { u := int8(asInt(rdser, subtag)) sl[i] = &u @@ -394,12 +461,12 @@ func (dec *Decoder) decodeSlice(pl *payloadIface, rdser *Serializer, v *reflect. } case reflect.Uint8: if !isPtr { - sl := (*[1 << 30]uint8)(ptr)[:count:count] + sl := (*[1 << 30]uint8)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { sl[i] = uint8(asInt(rdser, subtag)) } } else { - sl := (*[1 << 28]*uint8)(ptr)[:count:count] + sl := (*[1 << 28]*uint8)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { u := uint8(asInt(rdser, subtag)) sl[i] = &u @@ -407,12 +474,12 @@ func (dec *Decoder) decodeSlice(pl *payloadIface, rdser *Serializer, v *reflect. } case reflect.Float32: if !isPtr { - sl := (*[1 << 28]float32)(ptr)[:count:count] + sl := (*[1 << 28]float32)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { sl[i] = float32(asFloat(rdser, subtag)) } } else { - sl := (*[1 << 28]*float32)(ptr)[:count:count] + sl := (*[1 << 28]*float32)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { f := float32(asFloat(rdser, subtag)) sl[i] = &f @@ -420,12 +487,12 @@ func (dec *Decoder) decodeSlice(pl *payloadIface, rdser *Serializer, v *reflect. } case reflect.Float64: if !isPtr { - sl := (*[1 << 27]float64)(ptr)[:count:count] + sl := (*[1 << 27]float64)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { sl[i] = asFloat(rdser, subtag) } } else { - sl := (*[1 << 28]*float64)(ptr)[:count:count] + sl := (*[1 << 28]*float64)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { f := asFloat(rdser, subtag) sl[i] = &f @@ -433,12 +500,12 @@ func (dec *Decoder) decodeSlice(pl *payloadIface, rdser *Serializer, v *reflect. } case reflect.Bool: if !isPtr { - sl := (*[1 << 27]bool)(ptr)[:count:count] + sl := (*[1 << 27]bool)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { sl[i] = rdser.GetVarUInt() != 0 } } else { - sl := (*[1 << 28]*bool)(ptr)[:count:count] + sl := (*[1 << 28]*bool)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { b := rdser.GetVarUInt() != 0 sl[i] = &b @@ -446,27 +513,27 @@ func (dec *Decoder) decodeSlice(pl *payloadIface, rdser *Serializer, v *reflect. } case reflect.String: if !isPtr { - sl := (*[1 << 27]string)(ptr)[:count:count] + sl := (*[1 << 27]string)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { sl[i] = asString(rdser, subtag) } } else { - sl := (*[1 << 28]*string)(ptr)[:count:count] + sl := (*[1 << 28]*string)(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { s := asString(rdser, subtag) sl[i] = &s } } case reflect.Interface: - sl := (*[1 << 27]interface{})(ptr)[:count:count] + sl := (*[1 << 27]interface{})(ptr)[offset : offset+count : offset+count] for i := 0; i < count; i++ { sl[i] = asIface(rdser, subtag) } default: - panic(fmt.Errorf("Internal error - can't decode array of type %s", v.Type().Elem().Kind().String())) + panic(fmt.Errorf("internal error - can't decode array of type %s", v.Type().Elem().Kind().String())) } } else { - for i := 0; i < count; i++ { + for i := offset; i < offset+count; i++ { dec.decodeValue(pl, rdser, v.Index(i), fieldsoutcnt, cctagsPath) } } @@ -550,7 +617,7 @@ func (dec *Decoder) decodeValue(pl *payloadIface, rdser *Serializer, v reflect.V return dec.skipStruct(pl, rdser, fieldsoutcnt, ctag) } } else { - panic(fmt.Errorf("Err: intf=%s, name='%s' %s", v.Type().Name(), dec.state.tagsMatcher.tag2name(ctagName), ctag.Dump())) + panic(fmt.Errorf("err: intf=%s, name='%s' %s", v.Type().Name(), dec.state.tagsMatcher.tag2name(ctagName), ctag.Dump())) } k = v.Kind() if k == reflect.Ptr { @@ -595,20 +662,50 @@ func (dec *Decoder) decodeValue(pl *payloadIface, rdser *Serializer, v reflect.V case k == reflect.String: v.SetString(str) case k == reflect.Slice, k == reflect.Array: - b, e := base64.StdEncoding.DecodeString(str) - if e != nil { - panic(fmt.Errorf("Can't base64 decode %s", str)) + elemK := v.Type().Elem().Kind() + if elemK == reflect.String { + if k == reflect.Slice { + el := reflect.New(v.Type().Elem()).Elem() + el.SetString(str) + extSlice := reflect.Append(v, el) + v.Set(extSlice) + } else { + panic(fmt.Errorf("can not put single value into the fixed size array of strings '%s'", str)) + } + } else if elemK == reflect.Interface { + if k == reflect.Slice { + el := reflect.New(v.Type().Elem()).Elem() + el.Set(reflect.ValueOf(str)) + extSlice := reflect.Append(v, el) + v.Set(extSlice) + } else { + panic(fmt.Errorf("can not put single value into the fixed size array of interfaces '%s'", str)) + } + } else { + b, e := base64.StdEncoding.DecodeString(str) + if e != nil { + panic(fmt.Errorf("can not decode base64 '%s': %v", str, e)) + } + v.SetBytes(b) } - v.SetBytes(b) case k == reflect.Interface: v.Set(reflect.ValueOf(str)) case k == reflect.Struct && v.Type().String() == "time.Time": tm, _ := time.Parse(time.RFC3339Nano, str) v.Set(reflect.ValueOf(tm)) default: - panic(fmt.Errorf("Can't set string to %s", v.Type().Kind().String())) + panic(fmt.Errorf("can not convert '%s' to 'string'", v.Type().Kind().String())) } default: + if k == reflect.Slice { + el := reflect.New(v.Type().Elem()).Elem() + extSlice := reflect.Append(v, el) + v.Set(extSlice) + v = v.Index(v.Len() - 1) + k = v.Type().Kind() + } else if k == reflect.Array { + panic(fmt.Errorf("can not put single value into the fixed size array")) + } switch k { case reflect.Float32, reflect.Float64: v.SetFloat(asFloat(rdser, ctagType)) @@ -639,7 +736,7 @@ func (dec *Decoder) decodeValue(pl *payloadIface, rdser *Serializer, v reflect.V case reflect.String: mv.SetMapIndex(reflect.ValueOf(name), v) default: - panic(fmt.Errorf("Unsuported map key type %s", mv.Type().Key().Kind().String())) + panic(fmt.Errorf("unsuported map key type '%s'", mv.Type().Key().Kind().String())) } } @@ -658,21 +755,30 @@ func (dec *Decoder) DecodeCPtr(cptr uintptr, dest interface{}) (err error) { defer func() { if ret := recover(); ret != nil { - if dec.logger != nil { - dec.logger.Printf(1, - "Interface: %#v\nRead position: %d\nTags(v%d): %v\nPayload Type: %+v\nPayload Value: %v\nTags cache: %+v\nData dump:\n%s\nError: %v\n", - dest, - ser.Pos(), - dec.state.Version, - dec.state.tagsMatcher.Names, - dec.state.payloadType.Fields, - pl.getAsMap(), - dec.state.structCache, - hex.Dump(ser.Bytes()), - ret, - ) - } - err = ret.(error) + if dec.loggerOwner != nil { + if logger := dec.loggerOwner.GetLogger(); logger != nil { + logger.Printf(bindings.ERROR, + "Interface: %#v\nRead position: %d\nTags(v%d): %v\nPayload Type: %+v\nPayload Value: %v\nTags cache: %+v\nData dump:\n%s\nError: %v\nTagsMatcher: { state_token: 0x%08X, version: %d }\n", + dest, + ser.Pos(), + dec.state.Version, + dec.state.tagsMatcher.Names, + dec.state.payloadType.Fields, + pl.getAsMap(), + dec.state.structCache, + hex.Dump(ser.Bytes()), + ret, + uint32(dec.state.StateToken), + dec.state.Version, + ) + } + } + switch v := ret.(type) { + case error: + err = v + default: + err = fmt.Errorf("rq: DecodeCPtr error: %v", ret) + } } }() @@ -681,7 +787,7 @@ func (dec *Decoder) DecodeCPtr(cptr uintptr, dest interface{}) (err error) { dec.decodeValue(pl, ser, reflect.ValueOf(dest), fieldsoutcnt, ctagsPath) if !ser.Eof() { - panic(fmt.Errorf("Internal error - left unparsed data")) + panic(fmt.Errorf("internal error - left unparsed data")) } return err } @@ -695,19 +801,28 @@ func (dec *Decoder) Decode(cjson []byte, dest interface{}) (err error) { defer func() { if ret := recover(); ret != nil { - if dec.logger != nil { - dec.logger.Printf(1, - "Interface: %#v\nRead position: %d\nTags(v%d): %v\nTags cache: %+v\nData dump:\n%s\nError: %v\n", - dest, - ser.Pos(), - dec.state.Version, - dec.state.tagsMatcher.Names, - dec.state.structCache, - hex.Dump(ser.Bytes()), - ret, - ) - } - err = ret.(error) + if dec.loggerOwner != nil { + if logger := dec.loggerOwner.GetLogger(); logger != nil { + logger.Printf(bindings.ERROR, + "Interface: %#v\nRead position: %d\nTags(v%d): %v\nTags cache: %+v\nData dump:\n%s\nError: %v\nTagsMatcher: { state_token: 0x%08X, version: %d }\n", + dest, + ser.Pos(), + dec.state.Version, + dec.state.tagsMatcher.Names, + dec.state.structCache, + hex.Dump(ser.Bytes()), + ret, + uint32(dec.state.StateToken), + dec.state.Version, + ) + } + } + switch v := ret.(type) { + case error: + err = v + default: + err = fmt.Errorf("rq: Decode error: %v", ret) + } } }() diff --git a/cjson/state.go b/cjson/state.go index ef6080144..f43f7d2d4 100644 --- a/cjson/state.go +++ b/cjson/state.go @@ -36,7 +36,7 @@ func (state *State) Copy() State { } } -func (state *State) ReadPayloadType(s *Serializer) State { +func (state *State) ReadPayloadType(s *Serializer, loggerOwner LoggerOwner, ns string) State { state.lock.Lock() defer state.lock.Unlock() stateToken := int32(s.GetVarUInt()) @@ -44,6 +44,12 @@ func (state *State) ReadPayloadType(s *Serializer) State { skip := state.Version >= version && state.StateToken == stateToken if !skip { + if loggerOwner != nil { + if logger := loggerOwner.GetLogger(); logger != nil { + logger.Printf(3, "rq: '%s' TagsMatcher was updated: { state_token: 0x%08X, version: %d } -> { state_token: 0x%08X, version: %d }", + ns, state.StateToken, state.Version, uint32(stateToken), version) + } + } state.StateData = &StateData{Version: version, StateToken: stateToken} } state.tagsMatcher.Read(s, skip) @@ -57,10 +63,10 @@ func (state *State) NewEncoder() Encoder { } } -func (state *State) NewDecoder(item interface{}, logger Logger) Decoder { +func (state *State) NewDecoder(item interface{}, loggerOwner LoggerOwner) Decoder { dec := Decoder{ - state: state, - logger: logger, + state: state, + loggerOwner: loggerOwner, } dec.state.sCacheLock.RLock() diff --git a/cpp_src/CMakeLists.txt b/cpp_src/CMakeLists.txt index 0ccbbe730..9291af86e 100644 --- a/cpp_src/CMakeLists.txt +++ b/cpp_src/CMakeLists.txt @@ -19,6 +19,11 @@ option (WITH_ASAN "Enable AddressSanitized build" OFF) option (WITH_TSAN "Enable ThreadSanitized build" OFF) option (WITH_GCOV "Enable instrumented code coverage build" OFF) option (WITH_STDLIB_DEBUG "Enable compiler's debug flags for stdlib behaviour validation (gcc/clang)" OFF) + +if (WIN32) + option (WITH_CPPTRACE "Enable CppTrace" ON) +endif() + option (ENABLE_LIBUNWIND "Enable libunwind" ON) option (ENABLE_TCMALLOC "Enable tcmalloc extensions" ON) option (ENABLE_JEMALLOC "Enable jemalloc extensions" ON) @@ -26,6 +31,8 @@ option (ENABLE_ROCKSDB "Enable rocksdb storage" ON) option (ENABLE_GRPC "Enable GRPC service" OFF) option (ENABLE_SSE "Enable SSE instructions" ON) option (ENABLE_SERVER_AS_PROCESS_IN_TEST "Enable run reindexer server as separate process in tests" OFF) +option (ENABLE_V3_FOLLOWERS "Enable compatibility mode with reindexer v3 followers. This is temporary flag and will be removed in further releases" OFF) + if (NOT GRPC_PACKAGE_PROVIDER) set (GRPC_PACKAGE_PROVIDER "CONFIG") @@ -37,7 +44,7 @@ else() option (LINK_RESOURCES "Link web resources as binary data" ON) endif() -set (REINDEXER_VERSION_DEFAULT "4.12.0") +set (REINDEXER_VERSION_DEFAULT "4.13.0") if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "RelWithDebInfo") @@ -54,25 +61,28 @@ include (TargetArch) target_architecture(COMPILER_TARGET_ARCH) # Configure compile options -string(REPLACE "-DNDEBUG" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") -string(REPLACE "-O2" "-O3" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") -string(REPLACE "-O2" "-O3" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") -if (NOT ${COMPILER_TARGET_ARCH} STREQUAL "e2k") - string(REPLACE "-g" "-g1" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") -else() - string(REPLACE "-g" "-g0" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") -endif() - +if (MSVC) + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O2 -Zi") + set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O2 -Zi") + set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG -Zi") + set(CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG -Zi") +else () + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g1") + set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O3 -g1") + set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG") + set(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG") +endif () if (${COMPILER_TARGET_ARCH} STREQUAL "e2k") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g0") add_definitions(-D__E2K__) add_definitions(-D__LCC__) -endif() +endif () if (NOT MSVC AND NOT APPLE) check_linker_flag (-gz cxx_linker_supports_gz) if (cxx_linker_supports_gz) - set (CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} -gz") - endif () + set (CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO} -gz") + endif () endif () if (MSVC) @@ -90,6 +100,14 @@ endif () set (EXTRA_FLAGS "") +if (WITH_ASAN AND WITH_TSAN) + message(FATAL_ERROR "You cannot use the ASAN and TSAN options at the same time, CMake will exit.") +endif() + +if (ENABLE_V3_FOLLOWERS) + add_definitions(-DREINDEX_WITH_V3_FOLLOWERS) +endif () + if (WITH_ASAN) set (EXTRA_FLAGS "-fsanitize=address") add_definitions(-DREINDEX_WITH_ASAN) @@ -134,6 +152,7 @@ file ( ${REINDEXER_SOURCE_PATH}/wal/* ${REINDEXER_SOURCE_PATH}/coroutine/* ${REINDEXER_SOURCE_PATH}/cluster/* + ${REINDEXER_SOURCE_PATH}/replv3/* ) string(REGEX REPLACE "([][+*()^])" "\\\\\\1" BASE_CORO_CONTEXT_DIR "${REINDEXER_SOURCE_PATH}/vendor/koishi") @@ -272,6 +291,9 @@ else() list(APPEND SRCS ${KOISHI_PATH}/fcontext/fcontext.c ${KOISHI_PATH}/fcontext/fcontext.hpp) endif() +# Static LevelDB v1.23 is built with -fno-rtti by default. To inherit our logger from leveldb's logger, this file must be built with -fno-rtti to +set_source_files_properties(${REINDEXER_SOURCE_PATH}/core/storage/leveldblogger.cc PROPERTIES COMPILE_FLAGS "-fno-rtti") + list(APPEND REINDEXER_LIBRARIES reindexer) add_library(${TARGET} STATIC ${HDRS} ${SRCS} ${VENDORS}) add_definitions(-DREINDEX_CORE_BUILD=1) @@ -431,6 +453,25 @@ set(THREADS_PREFER_PTHREAD_FLAG TRUE) find_package(Threads REQUIRED ON) list(APPEND REINDEXER_LIBRARIES ${CMAKE_THREAD_LIBS_INIT} ) +if (WITH_CPPTRACE) + ExternalProject_Add( + cpptrace_lib + GIT_REPOSITORY "https://github.com/jeremy-rifkin/cpptrace.git" + GIT_TAG "main" + CMAKE_ARGS -DCMAKE_INSTALL_LIBDIR=${CMAKE_CURRENT_BINARY_DIR} + -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR} + -DCPPTRACE_STATIC=On + -DCPPTRACE_GET_SYMBOLS_WITH_DBGHELP=On + -DCPPTRACE_UNWIND_WITH_DBGHELP=On + -DCPPTRACE_DEMANGLE_WITH_WINAPI=On + ) + add_definitions(-DREINDEX_WITH_CPPTRACE) + add_dependencies(reindexer cpptrace_lib) + list(APPEND REINDEXER_LIBRARIES cpptrace ${REINDEXER_LIBRARIES}) + +endif() + + # librt find_library(LIBRT rt) if(LIBRT) @@ -655,7 +696,6 @@ if (GO_BUILTIN_EXPORT_PKG_PATH AND NOT IS_ABSOLUTE ${GO_BUILTIN_EXPORT_PKG_PATH} set (GO_BUILTIN_EXPORT_PKG_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${GO_BUILTIN_EXPORT_PKG_PATH}") endif () -if (NOT WIN32) function(generate_libs_list INPUT_LIBS OUTPUT_LIST_NAME) set (flibs ${${OUTPUT_LIST_NAME}}) foreach(lib ${INPUT_LIBS}) @@ -678,6 +718,8 @@ if (NOT WIN32) set (${OUTPUT_LIST_NAME} ${flibs} PARENT_SCOPE) endfunction (generate_libs_list) + +if (NOT WIN32) if (GO_BUILTIN_EXPORT_PKG_PATH AND EXISTS "${GO_BUILTIN_EXPORT_PKG_PATH}/posix_config.go.in") ProcessorCount (cgo_proc_count) set (cgo_cxx_flags "-I../../cpp_src ${EXTRA_FLAGS}") @@ -698,12 +740,14 @@ if (NOT WIN32) SET(CMAKE_INSTALL_DEFAULT_COMPONENT_NAME "server") SET(DIST_INCLUDE_FILES "tools/errors.h" "tools/serializer.h" "tools/varint.h" "tools/stringstools.h" "tools/customhash.h" "tools/assertrx.h" "tools/jsonstring.h" + "tools/verifying_updater.h" "core/reindexer.h" "core/type_consts.h" "core/item.h" "core/payload/payloadvalue.h" "core/payload/payloadiface.h" "core/indexopts.h" "core/namespacedef.h" "core/keyvalue/variant.h" "core/keyvalue/geometry.h" "core/sortingprioritiestable.h" "core/rdxcontext.h" "core/activity_context.h" "core/activity.h" "core/activitylog.h" "core/type_consts_helpers.h" "core/payload/fieldsset.h" "core/payload/payloadtype.h" "core/cbinding/reindexer_c.h" "core/cbinding/reindexer_ctypes.h" "core/transaction/transaction.h" "core/payload/payloadfieldtype.h" "core/reindexerconfig.h" "core/query/query.h" "core/query/queryentry.h" "core/queryresults/queryresults.h" "core/indexdef.h" "core/queryresults/aggregationresult.h" "core/queryresults/itemref.h" "core/namespace/stringsholder.h" "core/keyvalue/key_string.h" "core/keyvalue/uuid.h" "core/key_value_type.h" + "core/namespace/incarnationtags.h" "core/itemimplrawdata.h" "core/expressiontree.h" "tools/lsn.h" "core/cjson/tagspath.h" "core/cjson/ctag.h" "estl/cow.h" "core/shardedmeta.h" "estl/overloaded.h" "estl/one_of.h" "core/queryresults/localqueryresults.h" @@ -778,6 +822,18 @@ if (NOT WIN32) install(DIRECTORY ${PROJECT_BINARY_DIR}/pkgconfig DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT dev) install(FILES ${PROJECT_BINARY_DIR}/pkgconfig/reindexer-config.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/reindexer COMPONENT dev) install(FILES ${PROJECT_BINARY_DIR}/pkgconfig/reindexer-config-version.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/reindexer COMPONENT dev) +else() + if (GO_BUILTIN_EXPORT_PKG_PATH AND EXISTS "${GO_BUILTIN_EXPORT_PKG_PATH}/windows_config.go.in") + set (cgo_ld_flags "-L\${SRCDIR}/../../build/cpp_src/ ${EXTRA_FLAGS}") + generate_libs_list("${REINDEXER_LIBRARIES}" cgo_ld_flags) + string(REPLACE ";" "" cgo_ld_flags "${cgo_ld_flags}") + configure_file ( + "${GO_BUILTIN_EXPORT_PKG_PATH}/windows_config.go.in" + "${GO_BUILTIN_EXPORT_PKG_PATH}/builtin_windows.go" + @ONLY + ) + unset (cgo_ld_flags) + endif () endif () add_subdirectory(server) diff --git a/cpp_src/client/coroqueryresults.cc b/cpp_src/client/coroqueryresults.cc index 14c4aff93..008654841 100644 --- a/cpp_src/client/coroqueryresults.cc +++ b/cpp_src/client/coroqueryresults.cc @@ -36,7 +36,7 @@ const std::vector &CoroQueryResults::GetAggregationResults() if (!i_.queryParams_.aggResults.has_value()) { parseExtraData(); if (!i_.queryParams_.aggResults.has_value()) { - throw Error(errLogic, "Lazy aggregations in QueryResults was not initialized"); + throw Error(errLogic, "Lazy aggregations in QueryResults were not initialized"); } } return i_.queryParams_.aggResults.value(); @@ -60,10 +60,10 @@ void CoroQueryResults::Bind(std::string_view rawResult, RPCQrId id, const Query if (q) { QueryData data; - data.joinedSize = uint16_t(q->joinQueries_.size()); - data.mergedJoinedSizes.reserve(q->mergeQueries_.size()); - for (const auto &mq : q->mergeQueries_) { - data.mergedJoinedSizes.emplace_back(mq.joinQueries_.size()); + data.joinedSize = uint16_t(q->GetJoinQueries().size()); + data.mergedJoinedSizes.reserve(q->GetMergeQueries().size()); + for (const auto &mq : q->GetMergeQueries()) { + data.mergedJoinedSizes.emplace_back(mq.GetJoinQueries().size()); } i_.qData_.emplace(std::move(data)); } else { @@ -138,6 +138,7 @@ void CoroQueryResults::parseExtraData() { ResultSerializer ser(rawResult); i_.queryParams_.aggResults.emplace(); i_.queryParams_.explainResults.emplace(); + i_.queryParams_.nsIncarnationTags.clear(); ser.GetExtraParams(i_.queryParams_, ResultSerializer::Options{}); } else { throw Error(errLogic, "Unable to parse clients explain or aggregation results (lazy parsing mode was expected)"); diff --git a/cpp_src/client/coroqueryresults.h b/cpp_src/client/coroqueryresults.h index 55583afb6..5c5f85d75 100644 --- a/cpp_src/client/coroqueryresults.h +++ b/cpp_src/client/coroqueryresults.h @@ -4,6 +4,7 @@ #include #include "client/item.h" #include "client/resultserializer.h" +#include "core/namespace/incarnationtags.h" #include "tools/lsn.h" namespace reindexer { @@ -110,10 +111,13 @@ class CoroQueryResults { bool HaveRank() const noexcept { return i_.queryParams_.flags & kResultsWithRank; } bool NeedOutputRank() const noexcept { return i_.queryParams_.flags & kResultsNeedOutputRank; } bool NeedOutputShardId() const noexcept { return i_.fetchFlags_ & kResultsNeedOutputShardId; } + int64_t GetShardingConfigVersion() const noexcept { return i_.queryParams_.shardingConfigVersion; } const std::string& GetExplainResults(); const std::vector& GetAggregationResults(); Error Status() const noexcept { return i_.status_; } h_vector GetNamespaces() const; + const NsShardsIncarnationTags& GetIncarnationTags() const& noexcept { return i_.queryParams_.nsIncarnationTags; } + const NsShardsIncarnationTags& GetIncarnationTags() && = delete; size_t GetNamespacesCount() const noexcept { return i_.nsArray_.size(); } bool IsCacheEnabled() const noexcept { return i_.queryParams_.flags & kResultsWithItemID; } int GetMergedNSCount() const noexcept { return i_.nsArray_.size(); } @@ -202,7 +206,6 @@ class CoroQueryResults { bool lazyMode_ = false; bool isBound_ = false; Error status_; - int64_t shardingConfigVersion_ = -1; std::chrono::steady_clock::time_point sessionTs_; std::optional qData_; }; diff --git a/cpp_src/client/cororeindexer.cc b/cpp_src/client/cororeindexer.cc index e6b93c450..60523410d 100644 --- a/cpp_src/client/cororeindexer.cc +++ b/cpp_src/client/cororeindexer.cc @@ -33,7 +33,7 @@ CoroReindexer& CoroReindexer::operator=(CoroReindexer&& rdx) noexcept { Error CoroReindexer::Connect(const std::string& dsn, net::ev::dynamic_loop& loop, const ConnectOpts& opts) { return impl_->Connect(dsn, loop, opts); } -Error CoroReindexer::Stop() { return impl_->Stop(); } +void CoroReindexer::Stop() { impl_->Stop(); } Error CoroReindexer::AddNamespace(const NamespaceDef& nsDef, const NsReplicationOpts& replOpts) { return impl_->AddNamespace(nsDef, ctx_, replOpts); } diff --git a/cpp_src/client/cororeindexer.h b/cpp_src/client/cororeindexer.h index 3cbafbfb0..acf75b445 100644 --- a/cpp_src/client/cororeindexer.h +++ b/cpp_src/client/cororeindexer.h @@ -45,12 +45,13 @@ class CoroReindexer { CoroReindexer &operator=(CoroReindexer &&) noexcept; /// Connect - connect to reindexer server - /// @param dsn - uri of server and database, like: `cproto://user@password:127.0.0.1:6534/dbname` + /// @param dsn - uri of server and database, like: `cproto://user@password:127.0.0.1:6534/dbname` or + /// `ucproto://user@password:/tmp/reindexer.sock:/dbname` /// @param loop - event loop for connections and coroutines handling /// @param opts - Connect options. May contaion any of
Error Connect(const std::string &dsn, net::ev::dynamic_loop &loop, const client::ConnectOpts &opts = client::ConnectOpts()); /// Stop - shutdown connector - Error Stop(); + void Stop(); /// Open or create namespace /// @param nsName - Name of namespace /// @param opts - Storage options. Can be one of
diff --git a/cpp_src/client/item.h b/cpp_src/client/item.h index 9c484bc9d..a2c11be33 100644 --- a/cpp_src/client/item.h +++ b/cpp_src/client/item.h @@ -82,9 +82,9 @@ class Item { /// Get internal shardId of item /// @return shardId of item int GetShardID() const noexcept { return shardId_; } - /// Get internal version of item - /// @return version of item - int NumFields(); + /// Get count of indexed fields + /// @return count of indexed fields + int NumFields() const noexcept; /// Set additional percepts for modify operation /// @param precepts - strings in format "fieldName=Func()" void SetPrecepts(std::vector precepts); diff --git a/cpp_src/client/itemimplbase.cc b/cpp_src/client/itemimplbase.cc index 09442ddc2..c33f92004 100644 --- a/cpp_src/client/itemimplbase.cc +++ b/cpp_src/client/itemimplbase.cc @@ -17,9 +17,8 @@ void ItemImplBase::FromCJSON(std::string_view slice) { GetPayload().Reset(); std::string_view data = slice; if (!unsafe_) { - holder_.reset(new char[slice.size()]); - std::copy(slice.begin(), slice.end(), holder_.get()); - data = std::string_view(holder_.get(), slice.size()); + holder_.clear(); + data = holder_.emplace_back(data); } Serializer rdser(data); @@ -36,7 +35,7 @@ void ItemImplBase::FromCJSON(std::string_view slice) { } Payload pl = GetPayload(); - CJsonDecoder decoder(tagsMatcher_); + CJsonDecoder decoder(tagsMatcher_, holder_); ser_.Reset(); try { decoder.Decode(pl, rdser, ser_); @@ -48,7 +47,7 @@ void ItemImplBase::FromCJSON(std::string_view slice) { } ser_.Reset(); rdser.SetPos(0); - CJsonDecoder decoder(tagsMatcher_); + CJsonDecoder decoder(tagsMatcher_, holder_); decoder.Decode(pl, rdser, ser_); } } @@ -66,9 +65,8 @@ void ItemImplBase::FromCJSON(std::string_view slice) { Error ItemImplBase::FromJSON(std::string_view slice, char **endp, bool /*pkOnly*/) { std::string_view data = slice; if (!unsafe_ && endp == nullptr) { - holder_.reset(new char[slice.size()]); - std::copy(slice.begin(), slice.end(), holder_.get()); - data = std::string_view(holder_.get(), slice.size()); + holder_.clear(); + data = holder_.emplace_back(data); } payloadValue_.Clone(); @@ -108,9 +106,8 @@ Error ItemImplBase::FromMsgPack(std::string_view buf, size_t &offset) { std::string_view data = buf; if (!unsafe_) { - holder_.reset(new char[buf.size()]); - std::copy(buf.begin(), buf.end(), holder_.get()); - data = std::string_view(holder_.get(), buf.size()); + holder_.clear(); + data = holder_.emplace_back(data); } ser_.Reset(); diff --git a/cpp_src/client/itemimplbase.h b/cpp_src/client/itemimplbase.h index d0245d7a8..6ccc7b647 100644 --- a/cpp_src/client/itemimplbase.h +++ b/cpp_src/client/itemimplbase.h @@ -80,7 +80,7 @@ class ItemImplBase { std::vector precepts_; bool unsafe_ = false; - std::unique_ptr holder_; + std::deque holder_; std::vector> largeJSONStrings_; }; diff --git a/cpp_src/client/queryresults.h b/cpp_src/client/queryresults.h index e04716f04..24b49f9dd 100644 --- a/cpp_src/client/queryresults.h +++ b/cpp_src/client/queryresults.h @@ -67,10 +67,16 @@ class QueryResults { int TotalCount() const noexcept { return results_.TotalCount(); } bool HaveRank() const noexcept { return results_.HaveRank(); } bool NeedOutputRank() const noexcept { return results_.NeedOutputRank(); } - const std::string& GetExplainResults() { return results_.GetExplainResults(); } - const std::vector& GetAggregationResults() { return results_.GetAggregationResults(); } + int64_t GetShardingConfigVersion() const noexcept { return results_.GetShardingConfigVersion(); } + const std::string& GetExplainResults() & { return results_.GetExplainResults(); } + const std::string& GetExplainResults() && = delete; + const std::vector& GetAggregationResults() & { return results_.GetAggregationResults(); } + const std::vector& GetAggregationResults() && = delete; + Error Status() const noexcept { return results_.Status(); } h_vector GetNamespaces() const { return results_.GetNamespaces(); } + const NsShardsIncarnationTags& GetIncarnationTags() const& noexcept { return results_.GetIncarnationTags(); } + const NsShardsIncarnationTags& GetIncarnationTags() && = delete; size_t GetNamespacesCount() const noexcept { return results_.GetNamespacesCount(); } bool IsCacheEnabled() const noexcept { return results_.IsCacheEnabled(); } diff --git a/cpp_src/client/raftclient.cc b/cpp_src/client/raftclient.cc index 7be7434f2..5ab3fccb6 100644 --- a/cpp_src/client/raftclient.cc +++ b/cpp_src/client/raftclient.cc @@ -25,7 +25,7 @@ RaftClient& RaftClient::operator=(RaftClient&& rdx) noexcept { Error RaftClient::Connect(const std::string& dsn, net::ev::dynamic_loop& loop, const client::ConnectOpts& opts) { return impl_->Connect(dsn, loop, opts); } -Error RaftClient::Stop() { return impl_->Stop(); } +void RaftClient::Stop() { impl_->Stop(); } Error RaftClient::SuggestLeader(const NodeData& suggestion, NodeData& response) { return impl_->SuggestLeader(suggestion, response, ctx_); } diff --git a/cpp_src/client/raftclient.h b/cpp_src/client/raftclient.h index 6f57bd651..85aca0941 100644 --- a/cpp_src/client/raftclient.h +++ b/cpp_src/client/raftclient.h @@ -38,7 +38,7 @@ class RaftClient { /// @param opts - Connect options. May contaion any of
Error Connect(const std::string &dsn, net::ev::dynamic_loop &loop, const client::ConnectOpts &opts = client::ConnectOpts()); /// Stop - shutdown connector - Error Stop(); + void Stop(); /// SuggestLeader - send cluster leader suggestion /// @param suggestion - node, suggested as a leader /// @param response - node, which has to be come leader according to remote server diff --git a/cpp_src/client/reindexer.cc b/cpp_src/client/reindexer.cc index e19af0f82..60f429645 100644 --- a/cpp_src/client/reindexer.cc +++ b/cpp_src/client/reindexer.cc @@ -12,7 +12,7 @@ Reindexer::Reindexer(const ReindexerConfig& config, uint32_t connCount, uint32_t Reindexer::~Reindexer() = default; Error Reindexer::Connect(const std::string& dsn, const client::ConnectOpts& opts) { return impl_->Connect(dsn, opts); } -Error Reindexer::Stop() { return impl_->Stop(); } +void Reindexer::Stop() { impl_->Stop(); } Error Reindexer::AddNamespace(const NamespaceDef& nsDef, const NsReplicationOpts& replOpts) { return impl_->AddNamespace(nsDef, ctx_, replOpts); } diff --git a/cpp_src/client/reindexer.h b/cpp_src/client/reindexer.h index 57ee2ca5e..110fa2e93 100644 --- a/cpp_src/client/reindexer.h +++ b/cpp_src/client/reindexer.h @@ -32,11 +32,12 @@ class Reindexer { Reindexer &operator=(Reindexer &&rdx) noexcept = default; /// Connect - connect to reindexer server - /// @param dsn - uri of server and database, like: `cproto://user@password:127.0.0.1:6534/dbname` + /// @param dsn - uri of server and database, like: `cproto://user@password:127.0.0.1:6534/dbname` or + /// `ucproto://user@password:/tmp/reindexer.sock:/dbname` /// @param opts - Connect options. May contaion any of
Error Connect(const std::string &dsn, const client::ConnectOpts &opts = client::ConnectOpts()); /// Stop - shutdown connector - Error Stop(); + void Stop(); /// Open or create namespace /// @param nsName - Name of namespace /// @param opts - Storage options. Can be one of
diff --git a/cpp_src/client/reindexerimpl.cc b/cpp_src/client/reindexerimpl.cc index 475737e83..cc0e6317f 100644 --- a/cpp_src/client/reindexerimpl.cc +++ b/cpp_src/client/reindexerimpl.cc @@ -192,10 +192,10 @@ CoroTransaction ReindexerImpl::NewTransaction(std::string_view nsName, const Int Error ReindexerImpl::CommitTransaction(Transaction &tr, QueryResults &results, const InternalRdxContext &ctx) { if (tr.IsFree()) { - return Error(errBadTransaction, "commit free transaction"); + return Error(errBadTransaction, "Attempt to commit empty transaction"); } if (tr.rx_.get() != this) { - return Error(errTxInvalidLeader, "Commit transaction to incorrect leader"); + return Error(errTxInvalidLeader, "Attempt to commit transaction to the incorrect leader"); } return sendCommand(tr.coroConnection(), DbCmdCommitTransaction, ctx, tr.tr_, results.results_); @@ -204,7 +204,7 @@ Error ReindexerImpl::CommitTransaction(Transaction &tr, QueryResults &results, c Error ReindexerImpl::RollBackTransaction(Transaction &tr, const InternalRdxContext &ctx) { if (tr.IsFree()) return tr.Status(); if (tr.rx_.get() != this) { - return Error(errLogic, "RollBack transaction to incorrect leader"); + return Error(errLogic, "Attempt to rollback transaction on the incorrect leader"); } return sendCommand(tr.coroConnection(), DbCmdRollBackTransaction, ctx, tr.tr_); } diff --git a/cpp_src/client/resultserializer.cc b/cpp_src/client/resultserializer.cc index 8ce7719c3..d90932427 100644 --- a/cpp_src/client/resultserializer.cc +++ b/cpp_src/client/resultserializer.cc @@ -11,6 +11,8 @@ void ResultSerializer::GetRawQueryParams(ResultSerializer::QueryParams& ret, con ret.totalcount = GetVarUint(); ret.qcount = GetVarUint(); ret.count = GetVarUint(); + ret.nsIncarnationTags.clear(); + ret.shardingConfigVersion = ShardingSourceId::NotSet; if (opts.IsWithClearAggs()) { if (opts.IsWithLazyMode()) { ret.aggResults = std::nullopt; @@ -52,8 +54,9 @@ void ResultSerializer::GetExtraParams(ResultSerializer::QueryParams& ret, Option bool firstLazyData = true; for (;;) { const int tag = GetVarUint(); - if (tag == QueryResultEnd) break; switch (tag) { + case QueryResultEnd: + return; case QueryResultAggregation: { std::string_view data = GetSlice(); if (!opts.IsWithLazyMode()) { @@ -92,6 +95,24 @@ void ResultSerializer::GetExtraParams(ResultSerializer::QueryParams& ret, Option ret.shardId = GetVarUint(); break; } + case QueryResultIncarnationTags: { + const auto size = GetVarUint(); + if (size) { + ret.nsIncarnationTags.reserve(size); + for (size_t i = 0; i < size; ++i) { + auto& shardTags = ret.nsIncarnationTags.emplace_back(); + shardTags.shardId = GetVarint(); + const auto tagsSize = GetVarUint(); + shardTags.tags.reserve(tagsSize); + for (size_t j = 0; j < tagsSize; ++j) { + shardTags.tags.emplace_back(GetVarint()); + } + } + } + break; + } + default: + throw Error(errLogic, "Unexpected Query tag: %d", tag); } } } diff --git a/cpp_src/client/resultserializer.h b/cpp_src/client/resultserializer.h index 462ee9d93..34f88f6ef 100644 --- a/cpp_src/client/resultserializer.h +++ b/cpp_src/client/resultserializer.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include "core/namespace/incarnationtags.h" #include "core/queryresults/aggregationresult.h" #include "tools/lsn.h" #include "tools/serializer.h" @@ -42,7 +43,8 @@ class ResultSerializer : public Serializer { int flags = 0; std::optional> aggResults; std::optional explainResults; - int64_t shardingConfigVersion = -1; + NsShardsIncarnationTags nsIncarnationTags; + int64_t shardingConfigVersion = ShardingSourceId::NotSet; int shardId = ShardingKeyType::ProxyOff; }; diff --git a/cpp_src/client/rpcclient.cc b/cpp_src/client/rpcclient.cc index c60d8464d..3452c3f14 100644 --- a/cpp_src/client/rpcclient.cc +++ b/cpp_src/client/rpcclient.cc @@ -9,6 +9,7 @@ #include "core/namespace/namespacestat.h" #include "core/namespace/snapshot/snapshot.h" #include "core/namespacedef.h" +#include "core/schema.h" #include "gason/gason.h" #include "tools/catch_and_return.h" #include "tools/cpucheck.h" @@ -30,6 +31,8 @@ RPCClient::RPCClient(const ReindexerConfig& config, INamespaces::PtrT sharedName RPCClient::~RPCClient() { Stop(); } Error RPCClient::Connect(const std::string& dsn, ev::dynamic_loop& loop, const client::ConnectOpts& opts) { + using namespace std::string_view_literals; + std::lock_guard lck(mtx_); if (conn_.IsRunning()) { return Error(errLogic, "Client is already started (%s)", dsn); @@ -39,9 +42,16 @@ Error RPCClient::Connect(const std::string& dsn, ev::dynamic_loop& loop, const c if (!connectData.uri.parse(dsn)) { return Error(errParams, "%s is not valid uri", dsn); } - if (connectData.uri.scheme() != "cproto") { +#ifdef _WIN32 + if (connectData.uri.scheme() != "cproto"sv) { return Error(errParams, "Scheme must be cproto"); } +#else + if (connectData.uri.scheme() != "cproto"sv && connectData.uri.scheme() != "ucproto"sv) { + return Error(errParams, "Scheme must be either cproto or ucproto"); + } +#endif + connectData.opts = cproto::CoroClientConnection::Options( config_.NetTimeout, config_.NetTimeout, opts.IsCreateDBIfMissing(), opts.HasExpectedClusterID(), opts.ExpectedClusterID(), config_.ReconnectAttempts, config_.EnableCompression, config_.RequestDedicatedThread, config_.AppName); @@ -50,7 +60,7 @@ Error RPCClient::Connect(const std::string& dsn, ev::dynamic_loop& loop, const c return errOK; } -Error RPCClient::Stop() { +void RPCClient::Stop() { if (conn_.IsRunning()) { std::lock_guard lck(mtx_); terminate_ = true; @@ -58,7 +68,6 @@ Error RPCClient::Stop() { loop_ = nullptr; terminate_ = false; } - return errOK; } Error RPCClient::AddNamespace(const NamespaceDef& nsDef, const InternalRdxContext& ctx, const NsReplicationOpts& replOpts) { @@ -364,7 +373,7 @@ Error RPCClient::Delete(const Query& query, CoroQueryResults& result, const Inte query.Serialize(ser); CoroQueryResults::NsArray nsArray; - query.WalkNested(true, true, [this, &nsArray](const Query& q) { nsArray.push_back(getNamespace(q._namespace)); }); + query.WalkNested(true, true, false, [this, &nsArray](const Query& q) { nsArray.push_back(getNamespace(q.NsName())); }); const int flags = result.i_.fetchFlags_ ? result.i_.fetchFlags_ : (kResultsWithItemID | kResultsWithPayloadTypes | kResultsCJson); result = CoroQueryResults(&conn_, std::move(nsArray), flags, config_.FetchAmount, config_.NetTimeout, result.i_.lazyMode_); @@ -385,7 +394,7 @@ Error RPCClient::Update(const Query& query, CoroQueryResults& result, const Inte query.Serialize(ser); CoroQueryResults::NsArray nsArray; - query.WalkNested(true, true, [this, &nsArray](const Query& q) { nsArray.push_back(getNamespace(q._namespace)); }); + query.WalkNested(true, true, false, [this, &nsArray](const Query& q) { nsArray.push_back(getNamespace(q.NsName())); }); const int flags = result.i_.fetchFlags_ ? result.i_.fetchFlags_ : (kResultsWithItemID | kResultsWithPayloadTypes | kResultsCJson); result = CoroQueryResults(&conn_, std::move(nsArray), flags, config_.FetchAmount, config_.NetTimeout, result.i_.lazyMode_); @@ -409,8 +418,7 @@ void vec2pack(const h_vector& vec, WrSerializer& ser) { Error RPCClient::Select(std::string_view querySQL, CoroQueryResults& result, const InternalRdxContext& ctx) { try { - Query query; - query.FromSQL(querySQL); + auto query = Query::FromSQL(querySQL); switch (query.type_) { case QuerySelect: return Select(query, result, ctx); @@ -419,7 +427,7 @@ Error RPCClient::Select(std::string_view querySQL, CoroQueryResults& result, con case QueryUpdate: return Update(query, result, ctx); case QueryTruncate: - return TruncateNamespace(query._namespace, ctx); + return TruncateNamespace(query.NsName(), ctx); default: return Error(errParams, "Incorrect qyery type"); } @@ -433,7 +441,7 @@ Error RPCClient::selectImpl(const Query& query, CoroQueryResults& result, millis CoroQueryResults::NsArray nsArray; WrSerializer qser; query.Serialize(qser); - query.WalkNested(true, true, [this, &nsArray](const Query& q) { nsArray.push_back(getNamespace(q._namespace)); }); + query.WalkNested(true, true, false, [this, &nsArray](const Query& q) { nsArray.push_back(getNamespace(q.NsName())); }); h_vector vers; for (auto& ns : nsArray) { auto tm = ns->GetTagsMatcher(); diff --git a/cpp_src/client/rpcclient.h b/cpp_src/client/rpcclient.h index 901e6d840..589b80f09 100644 --- a/cpp_src/client/rpcclient.h +++ b/cpp_src/client/rpcclient.h @@ -83,7 +83,7 @@ class RPCClient { ~RPCClient(); Error Connect(const std::string &dsn, ev::dynamic_loop &loop, const ConnectOpts &opts); - Error Stop(); + void Stop(); Error OpenNamespace(std::string_view nsName, const InternalRdxContext &ctx, const StorageOpts &opts = StorageOpts().Enabled().CreateIfMissing(), diff --git a/cpp_src/cluster/clusterizator.cc b/cpp_src/cluster/clusterizator.cc index 320a39ad1..0c17c88bf 100644 --- a/cpp_src/cluster/clusterizator.cc +++ b/cpp_src/cluster/clusterizator.cc @@ -1,7 +1,7 @@ #include "clusterizator.h" #include #include -#include "core/reindexerimpl.h" +#include "core/reindexer_impl/reindexerimpl.h" namespace reindexer { namespace cluster { diff --git a/cpp_src/cluster/config.cc b/cpp_src/cluster/config.cc index efb96db3e..24bba7824 100644 --- a/cpp_src/cluster/config.cc +++ b/cpp_src/cluster/config.cc @@ -472,7 +472,7 @@ void AsyncReplConfigData::GetYAML(WrSerializer &ser) const { "# Replication log level on replicator's startup. May be changed either via this config (with replication restart) or via config-action\n" "# (upsert '{ \"type\":\"action\", \"action\": { \"command\": \"set_log_level\", \"type\": \"async_replication\", \"level\": \"info\" } }' into #config-namespace).\n" "# Possible values: none, error, warning, info, trace.\n" - "log_level: " + logLevelToString(logLevel) + "\n" + "log_level: " + std::string(logLevelToString(logLevel)) + "\n" "# List of namespaces for replication. If emply, all namespaces\n" "# All replicated namespaces will become read only for followers\n" "# It should be written as YAML sequence, JSON-style arrays are not supported\n" @@ -869,6 +869,7 @@ Error ShardingConfig::FromYAML(const std::string &yaml) { proxyConnCount = root["proxy_conn_count"].as(proxyConnCount); proxyConnConcurrency = root["proxy_conn_concurrency"].as(proxyConnConcurrency); proxyConnThreads = root["proxy_conn_threads"].as(proxyConnThreads); + sourceId = root["source_id"].as(ShardingSourceId::NotSet); return Validate(); } catch (const YAML::Exception &ex) { return Error(errParseYAML, "yaml parsing error: '%s'", ex.what()); @@ -924,6 +925,7 @@ Error ShardingConfig::FromJSON(const gason::JsonNode &root) { proxyConnCount = root["proxy_conn_count"].As(proxyConnCount); proxyConnConcurrency = root["proxy_conn_concurrency"].As(proxyConnConcurrency); proxyConnThreads = root["proxy_conn_threads"].As(proxyConnThreads); + sourceId = root["source_id"].As(ShardingSourceId::NotSet); } catch (const Error &err) { return err; } catch (const gason::Exception &ex) { @@ -936,7 +938,7 @@ bool operator==(const ShardingConfig &lhs, const ShardingConfig &rhs) { return lhs.namespaces == rhs.namespaces && lhs.thisShardId == rhs.thisShardId && lhs.shards == rhs.shards && lhs.reconnectTimeout == rhs.reconnectTimeout && lhs.shardsAwaitingTimeout == rhs.shardsAwaitingTimeout && lhs.proxyConnCount == rhs.proxyConnCount && lhs.proxyConnConcurrency == rhs.proxyConnConcurrency && - rhs.proxyConnThreads == lhs.proxyConnThreads; + rhs.proxyConnThreads == lhs.proxyConnThreads && rhs.sourceId == lhs.sourceId; } bool operator==(const ShardingConfig::Key &lhs, const ShardingConfig::Key &rhs) { return lhs.shardId == rhs.shardId && lhs.algorithmType == rhs.algorithmType && lhs.RelaxCompare(rhs.values) == 0; @@ -973,6 +975,9 @@ YAML::Node ShardingConfig::GetYAMLObj() const { yaml["proxy_conn_count"] = proxyConnCount; yaml["proxy_conn_concurrency"] = proxyConnConcurrency; yaml["proxy_conn_threads"] = proxyConnThreads; + if (sourceId != ShardingSourceId::NotSet) { + yaml["source_id"] = sourceId; + } return yaml; } @@ -1013,6 +1018,9 @@ void ShardingConfig::GetJSON(JsonBuilder &jb) const { jb.Put("proxy_conn_count", proxyConnCount); jb.Put("proxy_conn_concurrency", proxyConnConcurrency); jb.Put("proxy_conn_threads", proxyConnThreads); + if (sourceId != ShardingSourceId::NotSet) { + jb.Put("source_id", sourceId); + } } Error ShardingConfig::Validate() const { diff --git a/cpp_src/cluster/config.h b/cpp_src/cluster/config.h index f495a9cdc..43ca32365 100644 --- a/cpp_src/cluster/config.h +++ b/cpp_src/cluster/config.h @@ -189,6 +189,9 @@ constexpr uint32_t kDefaultShardingProxyCoroPerConn = 8; constexpr uint32_t kDefaultShardingProxyConnThreads = 4; struct ShardingConfig { + static constexpr unsigned serverIdPos = 53; + static constexpr int64_t serverIdMask = (((1ll << 10) - 1) << serverIdPos); // 01111111111000...000 + struct Key { Error FromYAML(const YAML::Node& yaml, const std::map>& shards, KeyValueType& valuesType, std::vector>& checkVal); @@ -236,8 +239,10 @@ struct ShardingConfig { int proxyConnCount = kDefaultShardingProxyConnCount; int proxyConnConcurrency = kDefaultShardingProxyCoroPerConn; int proxyConnThreads = kDefaultShardingProxyConnThreads; + int64_t sourceId = ShardingSourceId::NotSet; }; bool operator==(const ShardingConfig&, const ShardingConfig&); +inline bool operator!=(const ShardingConfig& l, const ShardingConfig& r) { return !(l == r); } bool operator==(const ShardingConfig::Key&, const ShardingConfig::Key&); bool operator==(const ShardingConfig::Namespace&, const ShardingConfig::Namespace&); diff --git a/cpp_src/cluster/raftmanager.cc b/cpp_src/cluster/raftmanager.cc index 40df6a6b6..bb31f3c77 100644 --- a/cpp_src/cluster/raftmanager.cc +++ b/cpp_src/cluster/raftmanager.cc @@ -78,6 +78,7 @@ Error RaftManager::SendDesiredLeaderId(int nextServerId) { errString += "[" + err.what() + "]"; } } catch (...) { + logInfo("%d: Unable to send desired leader: got unknonw exception", serverId_); } }); } diff --git a/cpp_src/cluster/replication/asyncdatareplicator.cc b/cpp_src/cluster/replication/asyncdatareplicator.cc index 528c3d390..ed8bddd33 100644 --- a/cpp_src/cluster/replication/asyncdatareplicator.cc +++ b/cpp_src/cluster/replication/asyncdatareplicator.cc @@ -1,6 +1,6 @@ #include "asyncdatareplicator.h" #include "cluster/clusterizator.h" -#include "core/reindexerimpl.h" +#include "core/reindexer_impl/reindexerimpl.h" namespace reindexer { namespace cluster { diff --git a/cpp_src/cluster/replication/clusterdatareplicator.cc b/cpp_src/cluster/replication/clusterdatareplicator.cc index f303532cf..e4d6873fe 100644 --- a/cpp_src/cluster/replication/clusterdatareplicator.cc +++ b/cpp_src/cluster/replication/clusterdatareplicator.cc @@ -1,6 +1,6 @@ #include "clusterdatareplicator.h" #include "core/defnsconfigs.h" -#include "core/reindexerimpl.h" +#include "core/reindexer_impl/reindexerimpl.h" #include "tools/randomgenerator.h" namespace reindexer { diff --git a/cpp_src/cluster/replication/clusterreplthread.cc b/cpp_src/cluster/replication/clusterreplthread.cc index c408f8eb0..aa167fbeb 100644 --- a/cpp_src/cluster/replication/clusterreplthread.cc +++ b/cpp_src/cluster/replication/clusterreplthread.cc @@ -1,7 +1,7 @@ #include "clusterreplthread.h" #include "client/snapshot.h" #include "core/namespace/namespacestat.h" -#include "core/reindexerimpl.h" +#include "core/reindexer_impl/reindexerimpl.h" #include "net/cproto/cproto.h" namespace reindexer { diff --git a/cpp_src/cluster/replication/leadersyncer.cc b/cpp_src/cluster/replication/leadersyncer.cc index 5a17c9276..ec8b9fbe1 100644 --- a/cpp_src/cluster/replication/leadersyncer.cc +++ b/cpp_src/cluster/replication/leadersyncer.cc @@ -1,7 +1,8 @@ #include "leadersyncer.h" #include "client/snapshot.h" #include "cluster/logger.h" -#include "core/reindexerimpl.h" +#include "core/defnsconfigs.h" +#include "core/reindexer_impl/reindexerimpl.h" namespace reindexer { namespace cluster { @@ -14,8 +15,9 @@ Error LeaderSyncer::Sync(std::list&& entries, SharedSync std::unique_lock lck(mtx_); syncQueue_.Refill(std::move(entries)); assert(threads_.empty()); + std::once_flag onceUpdShardingCfg; for (size_t i = 0; i < cfg_.threadsCount; ++i) { - threads_.emplace_back(thCfg, syncQueue_, sharedSyncState, thisNode, statsCollector, log_); + threads_.emplace_back(thCfg, syncQueue_, sharedSyncState, thisNode, statsCollector, log_, onceUpdShardingCfg); } lck.unlock(); @@ -42,7 +44,96 @@ Error LeaderSyncer::Sync(std::list&& entries, SharedSync return err; } +template +static auto makeClusterSet(const std::vector& cluster) { + std::vector res(cluster.size()); + for (size_t i = 0; i < cluster.size(); ++i) { + if constexpr (std::is_same_v) { + res[i] = cluster[i].dsn; + } else { + res[i] = cluster[i]; + } + } + std::sort(res.begin(), res.end()); + return res; +} + +void LeaderSyncThread::actualizeShardingConfig() { + auto cluster = makeClusterSet(thisNode_.clusterConfig_->nodes); + auto isClusterEqualSomeShard = [&cluster](const cluster::ShardingConfig& config) { + return std::any_of(config.shards.begin(), config.shards.end(), + [&cluster](const auto& pair) { return cluster == makeClusterSet(pair.second); }); + }; + + cluster::ShardingConfig config; + if (auto configPtr = thisNode_.shardingConfig_.Get()) { + config = *configPtr; + } + + bool updated = false; + for (const auto& dsn : cfg_.dsns) { + loop_.spawn([&]() noexcept { + try { + client::CoroReindexer client; + auto err = client.Connect(dsn, loop_, client::ConnectOpts().WithExpectedClusterID(cfg_.clusterId)); + if (!err.ok()) { + logWarn("%s: Actualization sharding config error: %s", dsn, err.what()); + return; + } + + cluster::ShardingConfig nodeConfig; + client::CoroQueryResults qr; + err = client.Select(Query(kConfigNamespace).Where("type", CondEq, "sharding"), qr); + if (!err.ok()) { + logWarn("%s: Actualization sharding config error: %s", dsn, err.what()); + return; + } + + if (qr.Count() != 1) { + logWarn("%s: Unexpected count of sharding config query results", dsn); + return; + } + + auto item = qr.begin().GetItem(); + gason::JsonParser parser; + err = nodeConfig.FromJSON(parser.Parse(item.GetJSON())["sharding"]); + + if (!err.ok()) { + logWarn("%s: Actualization sharding config error: %s", dsn, err.what()); + return; + } + + if (!isClusterEqualSomeShard(nodeConfig)) { + logWarn("%s: Different sets of nodes of the obtained config and the current cluster", dsn); + return; + } + + if ((nodeConfig.sourceId & ~cluster::ShardingConfig::serverIdMask) > + (config.sourceId & ~cluster::ShardingConfig::serverIdMask)) { + config = std::move(nodeConfig); + updated = true; + } + } catch (const Error& err) { + logWarn("%s: Actualization sharding config error: %s", dsn, err.what()); + } catch (...) { + logWarn("%s: Unexpected exception during actualization sharding config", dsn); + } + }); + } + + loop_.run(); + + if (updated) { + using CallbackT = ReindexerImpl::CallbackT; + gason::JsonParser parser; + this->thisNode_.proxyCallbacks_.at({"leader_config_process", CallbackT::Type::System})(parser.Parse(span(config.GetJSON())), + CallbackT::SourceIdT{config.sourceId}, {}); + } +} + void LeaderSyncThread::sync() { + std::call_once(actShardingCfg_, &LeaderSyncThread::actualizeShardingConfig, this); + loop_.spawn([this] { LeaderSyncQueue::Entry entry; uint32_t nodeId = 0; diff --git a/cpp_src/cluster/replication/leadersyncer.h b/cpp_src/cluster/replication/leadersyncer.h index 154e480c6..b5abb2518 100644 --- a/cpp_src/cluster/replication/leadersyncer.h +++ b/cpp_src/cluster/replication/leadersyncer.h @@ -116,14 +116,15 @@ class LeaderSyncThread { }; LeaderSyncThread(const Config& cfg, LeaderSyncQueue& syncQueue, SharedSyncState<>& sharedSyncState, ReindexerImpl& thisNode, - ReplicationStatsCollector statsCollector, const Logger& l) + ReplicationStatsCollector statsCollector, const Logger& l, std::once_flag& actShardingCfg) : cfg_(cfg), syncQueue_(syncQueue), sharedSyncState_(sharedSyncState), thisNode_(thisNode), statsCollector_(statsCollector), client_(client::ReindexerConfig{10000, 0, cfg_.netTimeout, cfg_.enableCompression, true, "cluster_leader_syncer"}), - log_(l) { + log_(l), + actShardingCfg_(actShardingCfg) { terminateAsync_.set(loop_); terminateAsync_.set([this](net::ev::async&) { client_.Stop(); }); thread_ = std::thread([this]() noexcept { sync(); }); @@ -141,6 +142,7 @@ class LeaderSyncThread { private: void sync(); void syncNamespaceImpl(bool forced, const LeaderSyncQueue::Entry& syncEntry, std::string& tmpNsName); + void actualizeShardingConfig(); static constexpr std::string_view logModuleName() noexcept { return std::string_view("leadersyncer_t"); } const Config& cfg_; @@ -155,6 +157,7 @@ class LeaderSyncThread { net::ev::async terminateAsync_; net::ev::dynamic_loop loop_; const Logger& log_; + std::once_flag& actShardingCfg_; }; class LeaderSyncer { diff --git a/cpp_src/cluster/replication/replicationthread.cc b/cpp_src/cluster/replication/replicationthread.cc index 51671cea1..8eb27f8d9 100644 --- a/cpp_src/cluster/replication/replicationthread.cc +++ b/cpp_src/cluster/replication/replicationthread.cc @@ -4,7 +4,7 @@ #include "clusterreplthread.h" #include "core/defnsconfigs.h" #include "core/namespace/snapshot/snapshot.h" -#include "core/reindexerimpl.h" +#include "core/reindexer_impl/reindexerimpl.h" #include "tools/catch_and_return.h" #include "tools/flagguard.h" #include "updatesbatcher.h" @@ -294,14 +294,16 @@ template <> logInfo("%d:%d Start applying leader's sharding config locally", serverId_, node.uid); std::string config; + std::optional sourceId; if (auto configPtr = thisNode.shardingConfig_.Get()) { config = configPtr->GetJSON(); + sourceId = configPtr->sourceId; } return node.client.WithLSN(lsn_t(0, serverId_)) .ShardingControlRequest( - sharding::MakeRequestData(config, -1)); - }; + sharding::MakeRequestData(config, std::move(sourceId))); + } return Error(errTimeout, "%d:%d DB role switch waiting timeout", serverId_, node.uid); } CATCH_AND_RETURN @@ -1027,16 +1029,12 @@ UpdateApplyStatus ReplThread::applyUpdate(const UpdateRecord& r case UpdateRecord::Type::UpdateQuery: { auto& data = std::get>(rec.data); client::CoroQueryResults qr; - Query q; - q.FromSQL(data->sql); - return UpdateApplyStatus(client.WithLSN(lsn).Update(q, qr), rec.type); + return UpdateApplyStatus(client.WithLSN(lsn).Update(Query::FromSQL(data->sql), qr), rec.type); } case UpdateRecord::Type::DeleteQuery: { auto& data = std::get>(rec.data); client::CoroQueryResults qr; - Query q; - q.FromSQL(data->sql); - return UpdateApplyStatus(client.WithLSN(lsn).Delete(q, qr), rec.type); + return UpdateApplyStatus(client.WithLSN(lsn).Delete(Query::FromSQL(data->sql), qr), rec.type); } case UpdateRecord::Type::SetSchema: { auto& data = std::get>(rec.data); @@ -1120,9 +1118,7 @@ UpdateApplyStatus ReplThread::applyUpdate(const UpdateRecord& r return UpdateApplyStatus(Error(errLogic, "Tx is empty"), rec.type); } auto& data = std::get>(rec.data); - Query q; - q.FromSQL(data->sql); - return UpdateApplyStatus(nsData.tx.Modify(std::move(q), lsn), rec.type); + return UpdateApplyStatus(nsData.tx.Modify(Query::FromSQL(data->sql), lsn), rec.type); } case UpdateRecord::Type::SetTagsMatcherTx: { if (nsData.tx.IsFree()) { diff --git a/cpp_src/cluster/replication/roleswitcher.cc b/cpp_src/cluster/replication/roleswitcher.cc index dede91f83..bb04aa739 100644 --- a/cpp_src/cluster/replication/roleswitcher.cc +++ b/cpp_src/cluster/replication/roleswitcher.cc @@ -1,7 +1,7 @@ #include "roleswitcher.h" #include "client/snapshot.h" #include "cluster/logger.h" -#include "core/reindexerimpl.h" +#include "core/reindexer_impl/reindexerimpl.h" #include "coroutine/tokens_pool.h" #include "net/cproto/cproto.h" #include "tools/logger.h" diff --git a/cpp_src/cluster/replication/updatesqueue.h b/cpp_src/cluster/replication/updatesqueue.h index 0f4d5c9d7..624ba5a3f 100644 --- a/cpp_src/cluster/replication/updatesqueue.h +++ b/cpp_src/cluster/replication/updatesqueue.h @@ -2,6 +2,7 @@ #include #include +#include #include "cluster/logger.h" #include "cluster/stats/relicationstatscollector.h" #include "core/rdxcontext.h" diff --git a/cpp_src/cluster/sharding/locatorserviceadapter.cc b/cpp_src/cluster/sharding/locatorserviceadapter.cc index ca172ce2b..bdd0bf95d 100644 --- a/cpp_src/cluster/sharding/locatorserviceadapter.cc +++ b/cpp_src/cluster/sharding/locatorserviceadapter.cc @@ -5,7 +5,13 @@ namespace reindexer::sharding { std::shared_ptr LocatorServiceAdapter::GetShardConnection(std::string_view ns, int shardId, Error &status) { return locator_->GetShardConnection(ns, shardId, status); } + int LocatorServiceAdapter::ActualShardId() const noexcept { return locator_->ActualShardId(); } + +int64_t LocatorServiceAdapter::SourceId() const noexcept { return locator_->SourceId(); } + int LocatorServiceAdapter::GetShardId(std::string_view ns, const Item &item) const { return locator_->GetShardId(ns, item); } + ShardIDsContainer LocatorServiceAdapter::GetShardId(const Query &q) const { return locator_->GetShardId(q); } + } // namespace reindexer::sharding diff --git a/cpp_src/cluster/sharding/locatorserviceadapter.h b/cpp_src/cluster/sharding/locatorserviceadapter.h index 7cef8ae75..887626e5c 100644 --- a/cpp_src/cluster/sharding/locatorserviceadapter.h +++ b/cpp_src/cluster/sharding/locatorserviceadapter.h @@ -27,6 +27,7 @@ class LocatorServiceAdapter { } std::shared_ptr GetShardConnection(std::string_view ns, int shardId, Error &status); int ActualShardId() const noexcept; + int64_t SourceId() const noexcept; int GetShardId(std::string_view ns, const Item &item) const; ShardIDsContainer GetShardId(const Query &q) const; @@ -38,4 +39,4 @@ class LocatorServiceAdapter { }; } // namespace sharding -} // namespace reindexer \ No newline at end of file +} // namespace reindexer diff --git a/cpp_src/cluster/sharding/sharding.cc b/cpp_src/cluster/sharding/sharding.cc index 5f7c1a453..1f6002167 100644 --- a/cpp_src/cluster/sharding/sharding.cc +++ b/cpp_src/cluster/sharding/sharding.cc @@ -16,28 +16,27 @@ RoutingStrategy::RoutingStrategy(const cluster::ShardingConfig &config) : keys_( bool RoutingStrategy::getHostIdForQuery(const Query &q, int &hostId) const { bool containsKey = false; - std::string_view ns = q.Namespace(); - for (auto it = q.entries.cbegin(), next = it, end = q.entries.cend(); it != end; ++it) { + std::string_view ns = q.NsName(); + for (auto it = q.Entries().cbegin(), next = it, end = q.Entries().cend(); it != end; ++it) { ++next; it->InvokeAppropriate( - Skip{}, + Skip{}, [&](const QueryEntry &qe) { if (containsKey) { - if (keys_.IsShardIndex(ns, qe.index)) { + if (keys_.IsShardIndex(ns, qe.FieldName())) { throw Error(errLogic, "Duplication of shard key condition in the query"); } } else { bool isShardKey = false; - int id = keys_.GetShardId(ns, qe.index, qe.values, isShardKey); + int id = keys_.GetShardId(ns, qe.FieldName(), qe.Values(), isShardKey); if (isShardKey) { containsKey = true; - if (qe.condition != CondEq) { + if (qe.Condition() != CondEq) { throw Error(errLogic, "Shard key condition can only be 'Eq'"); } if (hostId == ShardingKeyType::ProxyOff) { hostId = id; - } - if (hostId != id) { + } else if (hostId != id) { throw Error(errLogic, "Shard key from other node"); } if (it->operation == OpOr || (next != end && next->operation == OpOr)) { @@ -49,28 +48,40 @@ bool RoutingStrategy::getHostIdForQuery(const Query &q, int &hostId) const { } } }, + [&](const SubQueryFieldEntry &sqe) { + if (keys_.IsShardIndex(ns, sqe.FieldName())) { + throw Error(errLogic, "Subqueries can not be used in the conditions with sharding key (%s)", sqe.FieldName()); + } + }, [&](const BetweenFieldsQueryEntry &qe) { - if (keys_.IsShardIndex(ns, qe.firstIndex) || keys_.IsShardIndex(ns, qe.secondIndex)) { + if (keys_.IsShardIndex(ns, qe.LeftFieldName()) || keys_.IsShardIndex(ns, qe.RightFieldName())) { throw Error(errLogic, "Shard key cannot be compared with another field"); } }, [&](const Bracket &) { for (auto i = it.cbegin().PlainIterator(), end = it.cend().PlainIterator(); i != end; ++i) { i->InvokeAppropriate( - Skip{}, + Skip{}, [&](const QueryEntry &qe) { - if (keys_.IsShardIndex(ns, qe.index)) { + if (keys_.IsShardIndex(ns, qe.FieldName())) { throw Error(errLogic, "Shard key condition cannot be included in bracket"); } }, + [&](const SubQueryFieldEntry &sqe) { + if (keys_.IsShardIndex(ns, sqe.FieldName())) { + throw Error(errLogic, "Subqueries can not be used in the conditions with sharding key (%s)", + sqe.FieldName()); + } + }, [&](const BetweenFieldsQueryEntry &qe) { - if (keys_.IsShardIndex(ns, qe.firstIndex) || keys_.IsShardIndex(ns, qe.secondIndex)) { + if (keys_.IsShardIndex(ns, qe.LeftFieldName()) || keys_.IsShardIndex(ns, qe.RightFieldName())) { throw Error(errLogic, "Shard key cannot be compared with another field"); } }); } }); } + // TODO check not update shard key // for (const UpdateEntry &entry : q.UpdateFields()) { // } @@ -79,34 +90,44 @@ bool RoutingStrategy::getHostIdForQuery(const Query &q, int &hostId) const { ShardIDsContainer RoutingStrategy::GetHostsIds(const Query &q) const { int hostId = ShardingKeyType::ProxyOff; - const std::string_view mainNs = q.Namespace(); + const std::string_view mainNs = q.NsName(); const bool mainNsIsSharded = keys_.IsSharded(mainNs); bool hasShardingKeys = mainNsIsSharded && getHostIdForQuery(q, hostId); const bool mainQueryToAllShards = mainNsIsSharded && !hasShardingKeys; - for (const auto &jq : q.joinQueries_) { - if (!keys_.IsSharded(jq.Namespace())) continue; + for (const auto &jq : q.GetJoinQueries()) { + if (!keys_.IsSharded(jq.NsName())) continue; if (getHostIdForQuery(jq, hostId)) { hasShardingKeys = true; } else { if (hasShardingKeys) { - throw Error(errLogic, "Join query must contain shard key."); + throw Error(errLogic, "Join query must contain shard key"); } } } - for (const auto &mq : q.mergeQueries_) { - if (!keys_.IsSharded(mq.Namespace())) continue; + for (const auto &mq : q.GetMergeQueries()) { + if (!keys_.IsSharded(mq.NsName())) continue; if (getHostIdForQuery(mq, hostId)) { hasShardingKeys = true; } else { if (hasShardingKeys) { - throw Error(errLogic, "Merge query must contain shard key."); + throw Error(errLogic, "Merge query must contain shard key"); + } + } + } + for (const auto &sq : q.GetSubQueries()) { + if (!keys_.IsSharded(sq.NsName())) continue; + if (getHostIdForQuery(sq, hostId)) { + hasShardingKeys = true; + } else { + if (hasShardingKeys) { + throw Error(errLogic, "Subquery must contain shard key"); } } } if (mainQueryToAllShards || !hasShardingKeys) { - if (!q.joinQueries_.empty() || !q.mergeQueries_.empty()) { - throw Error(errLogic, "Query to all shard can't contain JOIN or MERGE"); + if (!q.GetJoinQueries().empty() || !q.GetMergeQueries().empty() || !q.GetSubQueries().empty()) { + throw Error(errLogic, "Query to all shard can't contain JOIN, MERGE or SUBQUERY"); } for (const auto &agg : q.aggregations_) { if (agg.Type() == AggAvg || agg.Type() == AggFacet || agg.Type() == AggDistinct || agg.Type() == AggUnknown) { @@ -305,7 +326,7 @@ std::shared_ptr ConnectStrategy::tryConnectToLeader(const std } LocatorService::LocatorService(ClusterProxy &rx, cluster::ShardingConfig config) - : rx_(rx), config_(std::move(config)), routingStrategy_(config_), actualShardId(config.thisShardId) {} + : rx_(rx), config_(std::move(config)), routingStrategy_(config_), actualShardId(config_.thisShardId) {} Error LocatorService::convertShardingKeysValues(KeyValueType fieldType, std::vector &keys) { return fieldType.EvaluateOneOf( @@ -451,9 +472,9 @@ ConnectionsPtr LocatorService::GetShardsConnectionsWithId(const Query &q, Error ShardIDsContainer ids = routingStrategy_.GetHostsIds(q); assert(ids.size() > 0); if (ids.size() > 1) { - return GetShardsConnections(q.Namespace(), -1, status); + return GetShardsConnections(q.NsName(), ShardingKeyType::NotSetShard, status); } else { - return GetShardsConnections(q.Namespace(), ids[0], status); + return GetShardsConnections(q.NsName(), ids[0], status); } } @@ -468,7 +489,7 @@ ConnectionsPtr LocatorService::rebuildConnectionsVector(std::string_view ns, int ConnectionsPtr connections = std::make_shared>(); ShardIDsContainer ids; const bool emptyNsName = ns == ""; - if (shardId == -1) { + if (shardId == ShardingKeyType::NotSetShard) { if (emptyNsName) { ids = routingStrategy_.GetHostsIds(); } else { diff --git a/cpp_src/cluster/sharding/sharding.h b/cpp_src/cluster/sharding/sharding.h index 8cb7e1449..93be5621e 100644 --- a/cpp_src/cluster/sharding/sharding.h +++ b/cpp_src/cluster/sharding/sharding.h @@ -143,6 +143,7 @@ class LocatorService { } int ActualShardId() const noexcept { return actualShardId; } + int64_t SourceId() const noexcept { return config_.sourceId; } void Shutdown(); private: diff --git a/cpp_src/cluster/sharding/shardingcontrolrequest.cc b/cpp_src/cluster/sharding/shardingcontrolrequest.cc index a9310d354..05c91f7fa 100644 --- a/cpp_src/cluster/sharding/shardingcontrolrequest.cc +++ b/cpp_src/cluster/sharding/shardingcontrolrequest.cc @@ -22,9 +22,11 @@ void ShardingControlRequestData::GetJSON(WrSerializer& ser) const { switch (commandType) { case Type::SaveCandidate: - case Type::ApplyLeaderConfig: data = SaveConfigCommand{}; break; + case Type::ApplyLeaderConfig: + data = ApplyLeaderConfigCommand{}; + break; case Type::ApplyNew: data = ApplyConfigCommand{}; break; @@ -45,6 +47,19 @@ void ShardingControlRequestData::GetJSON(WrSerializer& ser) const { return errOK; } +void ApplyLeaderConfigCommand::GetJSON(JsonBuilder& json) const { + json.Put("config", config); + if (sourceId.has_value()) { + json.Put("source_id", *sourceId); + } +} + +void ApplyLeaderConfigCommand::FromJSON(const gason::JsonNode& payload) { + config = payload["config"].As(); + auto& sourceIdNode = payload["source_id"]; + sourceId = sourceIdNode.empty() ? std::optional() : sourceIdNode.As(); +} + void SaveConfigCommand::GetJSON(JsonBuilder& json) const { json.Put("config", config); json.Put("source_id", sourceId); diff --git a/cpp_src/cluster/sharding/shardingcontrolrequest.h b/cpp_src/cluster/sharding/shardingcontrolrequest.h index c41d0bf10..55de8f54e 100644 --- a/cpp_src/cluster/sharding/shardingcontrolrequest.h +++ b/cpp_src/cluster/sharding/shardingcontrolrequest.h @@ -14,6 +14,17 @@ struct JsonNode; } namespace reindexer::sharding { +struct ApplyLeaderConfigCommand { + ApplyLeaderConfigCommand() = default; + ApplyLeaderConfigCommand(std::string_view config, std::optional sourceId) noexcept : config(config), sourceId(sourceId) {} + + std::string_view config; + std::optional sourceId; + + void GetJSON(JsonBuilder& json) const; + void FromJSON(const gason::JsonNode& payload); +}; + struct SaveConfigCommand { SaveConfigCommand() = default; SaveConfigCommand(std::string_view config, int64_t sourceId) noexcept : config(config), sourceId(sourceId) {} @@ -56,11 +67,13 @@ struct ShardingControlRequestData { }; private: - using CommandDataType = std::variant; - using Enum2Type = meta::Map< - meta::Values2Types, - std::tuple>; + using CommandDataType = std::variant; + using Enum2Type = meta::Map; template friend ShardingControlRequestData MakeRequestData(Args&&... args) noexcept; diff --git a/cpp_src/cmd/reindexer_server/CMakeLists.txt b/cpp_src/cmd/reindexer_server/CMakeLists.txt index 22633b678..37ba4b8d1 100644 --- a/cpp_src/cmd/reindexer_server/CMakeLists.txt +++ b/cpp_src/cmd/reindexer_server/CMakeLists.txt @@ -7,6 +7,15 @@ set(TARGET reindexer_server) file (GLOB_RECURSE SRCS "main.cc") add_executable(${TARGET} ${SRCS}) list(APPEND REINDEXER_LIBRARIES reindexer_server_library reindexer ${REINDEXER_LIBRARIES}) + +if (WITH_CPPTRACE) + list(APPEND REINDEXER_LIBRARIES cpptrace ${REINDEXER_LIBRARIES}) +endif() + +if(MSVC) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -DEBUG") +endif() + include_directories(${PROJECT_SOURCE_DIR}) add_dependencies(${TARGET} reindexer_server_library) @@ -57,6 +66,7 @@ if (WIN32) set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP true) include(InstallRequiredSystemLibraries) install(FILES ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} DESTINATION ${CMAKE_INSTALL_BINDIR}) + install(FILES $ DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL) else() get_filename_component(MINGW_DLL_DIR ${CMAKE_CXX_COMPILER} PATH) install(FILES diff --git a/cpp_src/cmd/reindexer_server/contrib/Dockerfile b/cpp_src/cmd/reindexer_server/contrib/Dockerfile index 8eb80a77c..94681c300 100644 --- a/cpp_src/cmd/reindexer_server/contrib/Dockerfile +++ b/cpp_src/cmd/reindexer_server/contrib/Dockerfile @@ -3,7 +3,7 @@ FROM alpine:3.14 AS build RUN cd /tmp && apk update && \ apk add git curl autoconf automake libtool linux-headers g++ make libunwind-dev grpc-dev grpc protobuf-dev c-ares-dev && \ git clone https://github.com/gperftools/gperftools.git && \ - cd gperftools && \ + cd gperftools && git checkout gperftools-2.13 && \ echo "noinst_PROGRAMS =" >> Makefile.am && \ sed -i s/_sigev_un\._tid/sigev_notify_thread_id/ src/profile-handler.cc && \ ./autogen.sh && ./configure --disable-dependency-tracking && make -j8 && make install diff --git a/cpp_src/cmd/reindexer_server/main.cc b/cpp_src/cmd/reindexer_server/main.cc index 11cf822d8..2c62cc5fd 100644 --- a/cpp_src/cmd/reindexer_server/main.cc +++ b/cpp_src/cmd/reindexer_server/main.cc @@ -20,6 +20,10 @@ int main(int argc, char* argv[]) { std::cerr << err.what() << std::endl; return EXIT_FAILURE; } +#if defined(WIN32) && defined(REINDEX_WITH_CPPTRACE) + reindexer::debug::set_minidump_path(svc.GetCoreLogPath()); +#endif + svc.EnableHandleSignals(); int ret = svc.Start(); spdlog::drop_all(); diff --git a/cpp_src/cmd/reindexer_server/sharding/go_test_sharding.sh b/cpp_src/cmd/reindexer_server/sharding/go_test_sharding.sh index 73852e70d..d0d3b0b94 100755 --- a/cpp_src/cmd/reindexer_server/sharding/go_test_sharding.sh +++ b/cpp_src/cmd/reindexer_server/sharding/go_test_sharding.sh @@ -79,7 +79,9 @@ do shard=$((shard+1)) done -gotestsum --junitfile sharding_tests.xml ./test/sharding/... -run "TestShardingIDs|TestShardingBuiltin" -dsn cproto://127.0.0.1:6000/sharding_db -tags sharding_test -count=1 +#gotestsum --junitfile sharding_tests.xml ./test/sharding/... -run "TestShardingIDs|TestShardingBuiltin" -dsn cproto://127.0.0.1:6000/sharding_db -tags sharding_test -count=1 + +go test ./test/sharding/... -run "TestShardingIDs|TestShardingBuiltin" -dsn cproto://127.0.0.1:6000/sharding_db -tags sharding_test -count=1 # Kill servers node=0 diff --git a/cpp_src/cmd/reindexer_server/test/get_last_rx_version.py b/cpp_src/cmd/reindexer_server/test/get_last_rx_version.py new file mode 100644 index 000000000..8007b8201 --- /dev/null +++ b/cpp_src/cmd/reindexer_server/test/get_last_rx_version.py @@ -0,0 +1,28 @@ +import argparse +import re + +import requests +from packaging.version import parse + + +URL = "http://repo.restream.ru/itv-api-ng/7/x86_64/" + +parser = argparse.ArgumentParser(description='Version') +parser.add_argument('-v', '--version', default="3") +args = parser.parse_args() + +version = args.version +if version == "3": + name = ">reindexer-server-" +elif version == "4": + name = ">reindexer-4-server-" +else: + raise ValueError(f"Version {version} is invalid") + +r = requests.get(URL) +res = r.text +res_list = re.findall(f'{name}.*.rpm', res) +versions_list = [(i[1:], parse(i[len(name):-11])) for i in res_list] +versions_list.sort(key=lambda x: x[1]) + +print(versions_list[-1][0]) diff --git a/cpp_src/cmd/reindexer_tool/CMakeLists.txt b/cpp_src/cmd/reindexer_tool/CMakeLists.txt index 91ac08bff..a4ae4c4de 100644 --- a/cpp_src/cmd/reindexer_tool/CMakeLists.txt +++ b/cpp_src/cmd/reindexer_tool/CMakeLists.txt @@ -4,6 +4,15 @@ project(reindexer_tool) set(TARGET reindexer_tool) +if (WITH_CPPTRACE) + list(APPEND REINDEXER_LIBRARIES cpptrace ${REINDEXER_LIBRARIES}) +endif() + +if(MSVC) + set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -DEBUG") +endif() + + if (NOT WITH_STDLIB_DEBUG) find_library(ReplXX_LIBRARY NAMES ${ReplXX_NAMES} replxx) find_path(ReplXX_INCLUDE_DIR NAMES replxx.hxx HINTS /opt/local/include /usr/local/include /usr/include) @@ -46,5 +55,9 @@ install(TARGETS ${TARGET} ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" ) +if(MSVC) + install(FILES $ DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL) +endif() + get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS) list(REMOVE_ITEM CPACK_COMPONENTS_ALL "leveldb_lib" "replxx_lib" "snappy_lib") diff --git a/cpp_src/cmd/reindexer_tool/commandsexecutor.cc b/cpp_src/cmd/reindexer_tool/commandsexecutor.cc index e6fa03382..912dfeb85 100644 --- a/cpp_src/cmd/reindexer_tool/commandsexecutor.cc +++ b/cpp_src/cmd/reindexer_tool/commandsexecutor.cc @@ -7,6 +7,7 @@ #include "coroutine/waitgroup.h" #include "executorscommand.h" #include "tableviewscroller.h" +#include "tools/catch_and_return.h" #include "tools/fsops.h" #include "tools/jsontools.h" #include "tools/stringstools.h" @@ -50,19 +51,18 @@ Error CommandsExecutor::Run(const std::string& } template -void CommandsExecutor::GetSuggestions(const std::string& input, std::vector& suggestions) { +Error CommandsExecutor::GetSuggestions(const std::string& input, std::vector& suggestions) { OutParamCommand> cmd( - [this, &input](std::vector& suggestions) { - getSuggestions(input, suggestions); - return errOK; - }, - suggestions); - execCommand(cmd); + [this, &input](std::vector& suggestions) { return getSuggestions(input, suggestions); }, suggestions); + return execCommand(cmd); } template Error CommandsExecutor::Stop() { - GenericCommand cmd([this] { return stop(true); }); + GenericCommand cmd([this] { + stop(true); + return Error{}; + }); auto err = execCommand(cmd); if (err.ok() && executorThr_.joinable()) { executorThr_.join(); @@ -256,7 +256,43 @@ Error CommandsExecutor::runImpl(const std::string& dsn, Args&&... a using reindexer::net::ev::sig; assertrx(!executorThr_.joinable()); - auto fn = [this](const std::string& dsn, Args&&... args) { + auto fnLoop = [this](const std::string& dsn, Args&&... args) { + Error err; + std::string config; + if (reindexer::fs::ReadFile(reindexer::fs::JoinPath(reindexer::fs::GetHomeDir(), kConfigFile), config) > 0) { + try { + gason::JsonParser jsonParser; + gason::JsonNode value = jsonParser.Parse(reindexer::giftStr(config)); + for (auto node : value) { + WrSerializer ser; + reindexer::jsonValueToString(node.value, ser, 0, 0, false); + variables_[kVariableOutput] = std::string(ser.Slice()); + } + } catch (const gason::Exception& e) { + err = Error(errParseJson, "Unable to parse output mode: %s", e.what()); + } + } + if (err.ok() && (variables_.empty() || variables_.find(kVariableOutput) == variables_.end())) { + variables_[kVariableOutput] = kOutputModeJson; + } + if (err.ok() && !uri_.parse(dsn)) { + err = Error(errNotValid, "Cannot connect to DB: Not a valid uri"); + } + if (err.ok()) err = db().Connect(dsn, std::forward(args)...); + if (err.ok()) { + loop_.spawn( + [this] { + // This coroutine should prevent loop from stopping for core::Reindexer + stopCh_.pop(); + }, + k8KStack); + } + std::lock_guard lck(mtx_); + status_.running = err.ok(); + status_.err = std::move(err); + }; + + auto fnThread = [this, &fnLoop](const std::string& dsn, Args&&... args) { sig sint; sint.set(loop_); sint.set([this](sig&) { cancelCtx_.Cancel(); }); @@ -279,49 +315,12 @@ Error CommandsExecutor::runImpl(const std::string& dsn, Args&&... a }); }); cmdAsync_.start(); - - auto fn = [this](const std::string& dsn, Args&&... args) { - Error err; - std::string config; - if (reindexer::fs::ReadFile(reindexer::fs::JoinPath(reindexer::fs::GetHomeDir(), kConfigFile), config) > 0) { - try { - gason::JsonParser jsonParser; - gason::JsonNode value = jsonParser.Parse(reindexer::giftStr(config)); - for (auto node : value) { - WrSerializer ser; - reindexer::jsonValueToString(node.value, ser, 0, 0, false); - variables_[kVariableOutput] = std::string(ser.Slice()); - } - } catch (const gason::Exception& e) { - err = Error(errParseJson, "Unable to parse output mode: %s", e.what()); - } - } - if (variables_.empty() || variables_.find(kVariableOutput) == variables_.end()) { - variables_[kVariableOutput] = kOutputModeJson; - } - if (err.ok() && !uri_.parse(dsn)) { - err = Error(errNotValid, "Cannot connect to DB: Not a valid uri"); - } - if (err.ok()) err = db().Connect(dsn, std::forward(args)...); - if (err.ok()) { - loop_.spawn( - [this] { - // This coroutine should prevent loop from stopping for core::Reindexer - stopCh_.pop(); - }, - k8KStack); - } - std::lock_guard lck(mtx_); - status_.running = err.ok(); - status_.err = std::move(err); - }; - loop_.spawn(std::bind(fn, std::cref(dsn), std::forward(args)...)); - + loop_.spawn(std::bind(fnLoop, std::cref(dsn), std::forward(args)...)); loop_.run(); }; setStatus(Status()); - executorThr_ = std::thread(std::bind(fn, std::cref(dsn), std::forward(args)...)); + executorThr_ = std::thread(std::bind(fnThread, std::cref(dsn), std::forward(args)...)); auto status = GetStatus(); while (!status.running && status.err.ok()) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); @@ -341,11 +340,21 @@ Error CommandsExecutor::runImpl(const std::string& dsn, Args&&... a template std::string CommandsExecutor::getCurrentDsn(bool withPath) const { + using namespace std::string_view_literals; std::string dsn(uri_.scheme() + "://"); if (!uri_.password().empty() && !uri_.username().empty()) { - dsn += uri_.username() + ":" + uri_.password() + "@"; + dsn += uri_.username() + ':' + uri_.password() + '@'; + } + if (uri_.scheme() == "ucproto"sv) { + auto dbName = uri_.db(); + if (dbName.size()) { + dsn += uri_.path().substr(0, uri_.path().size() - dbName.size() - 1) + ':' + (withPath ? uri_.path() : "/"); + } else { + dsn += uri_.path() + ':' + (withPath ? uri_.path() : "/"); + } + } else { + dsn += uri_.hostname() + ':' + uri_.port() + (withPath ? uri_.path() : "/"); } - dsn += uri_.hostname() + ":" + uri_.port() + (withPath ? uri_.path() : "/"); return dsn; } @@ -545,15 +554,14 @@ Error CommandsExecutor::processImpl(const std::string& command) noe } template <> -Error CommandsExecutor::stop(bool terminate) { +void CommandsExecutor::stop(bool terminate) { if (terminate) { stopCh_.close(); } - return Error(); } template <> -Error CommandsExecutor::stop(bool terminate) { +void CommandsExecutor::stop(bool terminate) { if (terminate) { stopCh_.close(); } @@ -561,11 +569,17 @@ Error CommandsExecutor::stop(bool terminate) { } template -void CommandsExecutor::getSuggestions(const std::string& input, std::vector& suggestions) { - if (!input.empty() && input[0] != '\\') db().GetSqlSuggestions(input, input.length() - 1, suggestions); +Error CommandsExecutor::getSuggestions(const std::string& input, std::vector& suggestions) { + if (!input.empty() && input[0] != '\\') { + auto err = db().GetSqlSuggestions(input, input.length() - 1, suggestions); + if (!err.ok()) { + return err; + } + } if (suggestions.empty()) { addCommandsSuggestions(input, suggestions); } + return {}; } template @@ -585,124 +599,115 @@ std::vector ToJSONVector(const QueryResultsT& r) { } template -Error CommandsExecutor::commandSelect(const std::string& command) { - Query q; +Error CommandsExecutor::commandSelect(const std::string& command) noexcept { try { - q.FromSQL(command); - } catch (const Error& err) { - return err; - } - - int flags = kResultsWithPayloadTypes | kResultsCJson | kResultsWithItemID; - flags = q.IsWALQuery() ? (flags | kResultsWithRaw) : flags; - if (variables_.find(kVariableWithShardId) != variables_.end() && variables_[kVariableWithShardId] == "on") { - flags |= kResultsNeedOutputShardId | kResultsWithShardId; - } - - typename DBInterface::QueryResultsT results(flags); - - auto err = db().Select(q, results); - - if (err.ok()) { - if (results.Count()) { - auto& outputType = variables_[kVariableOutput]; - if (outputType == kOutputModeTable) { - auto jsonData = ToJSONVector(results); - auto isCanceled = [this]() -> bool { return cancelCtx_.IsCancelled(); }; - - reindexer::TableViewBuilder tableResultsBuilder; - if (output_.IsCout() && !reindexer::isStdoutRedirected()) { - TableViewScroller resultsScroller(tableResultsBuilder, reindexer::getTerminalSize().height - 1); - resultsScroller.Scroll(output_, std::move(jsonData), isCanceled); + typename DBInterface::QueryResultsT results(kResultsWithPayloadTypes | kResultsCJson | kResultsWithItemID | kResultsWithRaw); + const auto q = Query::FromSQL(command); + + auto err = db().Select(q, results); + + if (err.ok()) { + if (results.Count()) { + auto& outputType = variables_[kVariableOutput]; + if (outputType == kOutputModeTable) { + auto jsonData = ToJSONVector(results); + auto isCanceled = [this]() -> bool { return cancelCtx_.IsCancelled(); }; + + reindexer::TableViewBuilder tableResultsBuilder; + if (output_.IsCout() && !reindexer::isStdoutRedirected()) { + TableViewScroller resultsScroller(tableResultsBuilder, reindexer::getTerminalSize().height - 1); + resultsScroller.Scroll(output_, std::move(jsonData), isCanceled); + } else { + tableResultsBuilder.Build(output_(), std::move(jsonData), isCanceled); + } } else { - tableResultsBuilder.Build(output_(), std::move(jsonData), isCanceled); - } - } else { - if (!cancelCtx_.IsCancelled()) { - output_() << "[" << std::endl; - err = queryResultsToJson(output_(), results, q.IsWALQuery(), !output_.IsCout()); - output_() << "]" << std::endl; + if (!cancelCtx_.IsCancelled()) { + output_() << "[" << std::endl; + err = queryResultsToJson(output_(), results, q.IsWALQuery(), !output_.IsCout()); + output_() << "]" << std::endl; + } } } - } - const std::string& explain = results.GetExplainResults(); - if (!explain.empty() && !cancelCtx_.IsCancelled()) { - output_() << "Explain: " << std::endl; - if (variables_[kVariableOutput] == kOutputModePretty) { - WrSerializer ser; - prettyPrintJSON(reindexer::giftStr(explain), ser); - output_() << ser.Slice() << std::endl; - } else { - output_() << explain << std::endl; + const std::string& explain = results.GetExplainResults(); + if (!explain.empty() && !cancelCtx_.IsCancelled()) { + output_() << "Explain: " << std::endl; + if (variables_[kVariableOutput] == kOutputModePretty) { + WrSerializer ser; + prettyPrintJSON(reindexer::giftStr(explain), ser); + output_() << ser.Slice() << std::endl; + } else { + output_() << explain << std::endl; + } + } + if (!cancelCtx_.IsCancelled()) { + output_() << "Returned " << results.Count() << " rows"; + if (results.TotalCount()) output_() << ", total count " << results.TotalCount(); + output_() << std::endl; } - } - if (!cancelCtx_.IsCancelled()) { - output_() << "Returned " << results.Count() << " rows"; - if (results.TotalCount()) output_() << ", total count " << results.TotalCount(); - output_() << std::endl; - } - auto& aggResults = results.GetAggregationResults(); - if (aggResults.size() && !cancelCtx_.IsCancelled()) { - output_() << "Aggregations: " << std::endl; - for (auto& agg : aggResults) { - switch (agg.type) { - case AggFacet: { - assertrx(!agg.fields.empty()); - reindexer::h_vector maxW; - maxW.reserve(agg.fields.size()); - for (const auto& field : agg.fields) { - maxW.emplace_back(field.length()); - } - for (auto& row : agg.facets) { - assertrx(row.values.size() == agg.fields.size()); - for (size_t i = 0; i < row.values.size(); ++i) { - maxW.at(i) = std::max(maxW.at(i), int(row.values[i].length())); + auto& aggResults = results.GetAggregationResults(); + if (aggResults.size() && !cancelCtx_.IsCancelled()) { + output_() << "Aggregations: " << std::endl; + for (auto& agg : aggResults) { + switch (agg.type) { + case AggFacet: { + assertrx(!agg.fields.empty()); + reindexer::h_vector maxW; + maxW.reserve(agg.fields.size()); + for (const auto& field : agg.fields) { + maxW.emplace_back(field.length()); + } + for (auto& row : agg.facets) { + assertrx(row.values.size() == agg.fields.size()); + for (size_t i = 0; i < row.values.size(); ++i) { + maxW.at(i) = std::max(maxW.at(i), int(row.values[i].length())); + } } - } - int rowWidth = 8 + (maxW.size() - 1) * 2; - for (auto& mW : maxW) { - mW += 3; - rowWidth += mW; - } - for (size_t i = 0; i < agg.fields.size(); ++i) { - if (i != 0) output_() << "| "; - output_() << std::left << std::setw(maxW.at(i)) << agg.fields[i]; - } - output_() << "| count" << std::endl; - output_() << std::left << std::setw(rowWidth) << std::setfill('-') << "" << std::endl << std::setfill(' '); - for (auto& row : agg.facets) { - for (size_t i = 0; i < row.values.size(); ++i) { + int rowWidth = 8 + (maxW.size() - 1) * 2; + for (auto& mW : maxW) { + mW += 3; + rowWidth += mW; + } + for (size_t i = 0; i < agg.fields.size(); ++i) { if (i != 0) output_() << "| "; - output_() << std::left << std::setw(maxW.at(i)) << row.values[i]; + output_() << std::left << std::setw(maxW.at(i)) << agg.fields[i]; + } + output_() << "| count" << std::endl; + output_() << std::left << std::setw(rowWidth) << std::setfill('-') << "" << std::endl << std::setfill(' '); + for (auto& row : agg.facets) { + for (size_t i = 0; i < row.values.size(); ++i) { + if (i != 0) output_() << "| "; + output_() << std::left << std::setw(maxW.at(i)) << row.values[i]; + } + output_() << "| " << row.count << std::endl; + } + } break; + case AggDistinct: + assertrx(agg.fields.size() == 1); + output_() << "Distinct (" << agg.fields.front() << ")" << std::endl; + for (auto& v : agg.distincts) { + output_() << v.template As(agg.payloadType, agg.distinctsFields) << std::endl; } - output_() << "| " << row.count << std::endl; - } - } break; - case AggDistinct: - assertrx(agg.fields.size() == 1); - output_() << "Distinct (" << agg.fields.front() << ")" << std::endl; - for (auto& v : agg.distincts) { - output_() << v.template As(agg.payloadType, agg.distinctsFields) << std::endl; - } - output_() << "Returned " << agg.distincts.size() << " values" << std::endl; - break; - case AggSum: - case AggAvg: - case AggMin: - case AggMax: - case AggCount: - case AggCountCached: - case AggUnknown: - assertrx(agg.fields.size() == 1); - output_() << reindexer::AggTypeToStr(agg.type) << "(" << agg.fields.front() << ") = " << agg.GetValueOrZero() - << std::endl; + output_() << "Returned " << agg.distincts.size() << " values" << std::endl; + break; + case AggSum: + case AggAvg: + case AggMin: + case AggMax: + case AggCount: + case AggCountCached: + case AggUnknown: + assertrx(agg.fields.size() == 1); + output_() << reindexer::AggTypeToStr(agg.type) << "(" << agg.fields.front() << ") = " << agg.GetValueOrZero() + << std::endl; + } } } } + return err; } - return err; + CATCH_AND_RETURN; } template @@ -759,21 +764,19 @@ Error CommandsExecutor::commandUpsert(const std::string& command) { } template -Error CommandsExecutor::commandUpdateSQL(const std::string& command) { - typename DBInterface::QueryResultsT results; - Query q; +Error CommandsExecutor::commandUpdateSQL(const std::string& command) noexcept { try { - q.FromSQL(command); - } catch (const Error& err) { - return err; - } + typename DBInterface::QueryResultsT results; + Query q = Query::FromSQL(command); - auto err = db().Update(q, results); + auto err = db().Update(q, results); - if (err.ok()) { - output_() << "Updated " << results.Count() << " documents" << std::endl; + if (err.ok()) { + output_() << "Updated " << results.Count() << " documents" << std::endl; + } + return err; } - return err; + CATCH_AND_RETURN; } template @@ -793,20 +796,18 @@ Error CommandsExecutor::commandDelete(const std::string& command) { } template -Error CommandsExecutor::commandDeleteSQL(const std::string& command) { - typename DBInterface::QueryResultsT results; - Query q; +Error CommandsExecutor::commandDeleteSQL(const std::string& command) noexcept { try { - q.FromSQL(command); - } catch (const Error& err) { - return err; - } - auto err = db().Delete(q, results); + typename DBInterface::QueryResultsT results; + Query q = Query::FromSQL(command); + auto err = db().Delete(q, results); - if (err.ok()) { - output_() << "Deleted " << results.Count() << " documents" << std::endl; + if (err.ok()) { + output_() << "Deleted " << results.Count() << " documents" << std::endl; + } + return err; } - return err; + CATCH_AND_RETURN; } template @@ -895,7 +896,8 @@ Error CommandsExecutor::commandDump(const std::string& command) { return Error(errCanceled, "Canceled"); } wrser << "\\UPSERT " << reindexer::escapeString(nsDef.name) << ' '; - it.GetJSON(wrser, false); + err = it.GetJSON(wrser, false); + if (!err.ok()) return err; wrser << '\n'; if (wrser.Len() > 0x100000) { output_() << wrser.Slice(); @@ -981,9 +983,11 @@ Error CommandsExecutor::commandMeta(const std::string& command) { auto nsName = reindexer::unescapeString(parser.NextToken()); std::vector allMeta; auto err = db().EnumMeta(nsName, allMeta); + if (!err.ok()) return err; for (auto& metaKey : allMeta) { std::string metaData; - db().GetMeta(nsName, metaKey, metaData); + err = db().GetMeta(nsName, metaKey, metaData); + if (!err.ok()) return err; output_() << metaKey << " = " << metaData << std::endl; } return err; @@ -1051,15 +1055,18 @@ Error CommandsExecutor::commandBench(const std::string& command) { LineParser parser(command); parser.NextToken(); - int benchTime = reindexer::stoi(parser.NextToken()); - if (benchTime == 0) benchTime = kBenchDefaultTime; + const std::string_view benchTimeToken = parser.NextToken(); + const int benchTime = benchTimeToken.empty() ? kBenchDefaultTime : reindexer::stoi(benchTimeToken); - db().DropNamespace(kBenchNamespace); + auto err = db().DropNamespace(kBenchNamespace); + if (!err.ok() && err.code() != errNotFound) { + return err; + } NamespaceDef nsDef(kBenchNamespace); nsDef.AddIndex("id", "hash", "int", IndexOpts().PK()); - auto err = db().AddNamespace(nsDef); + err = db().AddNamespace(nsDef); if (!err.ok()) return err; output_() << "Seeding " << kBenchItemsCount << " documents to bench namespace..." << std::endl; @@ -1086,31 +1093,30 @@ Error CommandsExecutor::commandBench(const std::string& command) { template <> Error CommandsExecutor::commandProcessDatabases(const std::string& command) { + using namespace std::string_view_literals; LineParser parser(command); parser.NextToken(); std::string_view subCommand = parser.NextToken(); - assertrx(uri_.scheme() == "cproto"); - if (subCommand == "list") { + assertrx(uri_.scheme() == "cproto"sv || uri_.scheme() == "ucproto"sv); + if (subCommand == "list"sv) { std::vector dbList; Error err = getAvailableDatabases(dbList); if (!err.ok()) return err; for (const std::string& dbName : dbList) output_() << dbName << std::endl; return Error(); - } else if (subCommand == "use") { + } else if (subCommand == "use"sv) { std::string currentDsn = getCurrentDsn() + std::string(parser.NextToken()); - Error err = stop(false); - if (!err.ok()) return err; - err = db().Connect(currentDsn, loop_); + stop(false); + auto err = db().Connect(currentDsn, loop_); if (err.ok()) err = db().Status(); if (err.ok()) output_() << "Succesfully connected to " << currentDsn << std::endl; return err; - } else if (subCommand == "create") { + } else if (subCommand == "create"sv) { auto dbName = parser.NextToken(); std::string currentDsn = getCurrentDsn() + std::string(dbName); - Error err = stop(false); - if (!err.ok()) return err; + stop(false); output_() << "Creating database '" << dbName << "'" << std::endl; - err = db().Connect(currentDsn, loop_, reindexer::client::ConnectOpts().CreateDBIfMissing()); + auto err = db().Connect(currentDsn, loop_, reindexer::client::ConnectOpts().CreateDBIfMissing()); if (!err.ok()) { std::cerr << "Error on database '" << dbName << "' creation" << std::endl; return err; @@ -1228,11 +1234,14 @@ std::function CommandsExecutor::getMergedSerialMeta(DBInterface& if (val > maxVal) { maxVal = val; } + // NOLINTBEGIN(bugprone-empty-catch) } catch (std::exception&) { } + // NOLINTEND(bugprone-empty-catch) } result = std::to_string(maxVal); return Error(); } +template +std::string CommandsExecutor::URI::getDBFromUCproto() const { + std::vector pathParts; + reindexer::split(std::string_view(uri_.path()), ":", true, pathParts); + return pathParts.size() >= 2 ? std::string(pathParts.back()) : std::string(); +} + template class CommandsExecutor; template class CommandsExecutor; template Error CommandsExecutor::Run(const std::string& dsn, const ConnectOpts& opts); diff --git a/cpp_src/cmd/reindexer_tool/commandsexecutor.h b/cpp_src/cmd/reindexer_tool/commandsexecutor.h index b70c4c8db..6e88ced3e 100644 --- a/cpp_src/cmd/reindexer_tool/commandsexecutor.h +++ b/cpp_src/cmd/reindexer_tool/commandsexecutor.h @@ -50,7 +50,7 @@ class CommandsExecutor { ~CommandsExecutor() { stop(true); } template Error Run(const std::string& dsn, const Args&... args); - void GetSuggestions(const std::string& input, std::vector& suggestions); + Error GetSuggestions(const std::string& input, std::vector& suggestions); Error Stop(); Error Process(const std::string& command); Error FromFile(std::istream& in); @@ -83,13 +83,13 @@ class CommandsExecutor { std::vector& suggestions); Error processImpl(const std::string& command) noexcept; - Error stop(bool terminate); - void getSuggestions(const std::string& input, std::vector& suggestions); - Error commandSelect(const std::string& command); + void stop(bool terminate); + Error getSuggestions(const std::string& input, std::vector& suggestions); + Error commandSelect(const std::string& command) noexcept; Error commandUpsert(const std::string& command); - Error commandUpdateSQL(const std::string& command); + Error commandUpdateSQL(const std::string& command) noexcept; Error commandDelete(const std::string& command); - Error commandDeleteSQL(const std::string& command); + Error commandDeleteSQL(const std::string& command) noexcept; Error commandDump(const std::string& command); Error commandNamespaces(const std::string& command); Error commandMeta(const std::string& command); @@ -216,13 +216,34 @@ class CommandsExecutor { }; // clang-format on + class URI { + public: + bool parse(const std::string& dsn) { return uri_.parse(dsn); } + const std::string& scheme() const { return uri_.scheme(); } + const std::string& path() const { return uri_.path(); } + const std::string& username() const { return uri_.username(); } + const std::string& password() const { return uri_.password(); } + const std::string& hostname() const { return uri_.hostname(); } + const std::string& port() const { return uri_.port(); } + std::string db() const { + if (uri_.scheme() == "ucproto") { + return getDBFromUCproto(); + } + return uri_.db(); + } + std::string getDBFromUCproto() const; + + private: + httpparser::UrlParser uri_; + }; + reindexer::net::ev::dynamic_loop loop_; CancelContext cancelCtx_; DBInterface db_; Output output_; int numThreads_; std::unordered_map variables_; - httpparser::UrlParser uri_; + URI uri_; reindexer::net::ev::async cmdAsync_; std::mutex mtx_; std::condition_variable condVar_; diff --git a/cpp_src/cmd/reindexer_tool/commandsprocessor.cc b/cpp_src/cmd/reindexer_tool/commandsprocessor.cc index c8e9e2981..f7a086af1 100644 --- a/cpp_src/cmd/reindexer_tool/commandsprocessor.cc +++ b/cpp_src/cmd/reindexer_tool/commandsprocessor.cc @@ -3,8 +3,8 @@ #include #include "client/cororeindexer.h" #include "core/reindexer.h" -#include "tableviewscroller.h" #include "tools/fsops.h" +#include "tools/terminalutils.h" namespace reindexer_tool { @@ -30,9 +30,13 @@ template void CommandsProcessor::setCompletionCallback(T& rx, void (T::*set_completion_callback)(new_v_callback_t const&)) { (rx.*set_completion_callback)([this](std::string const& input, int) -> replxx::Replxx::completions_t { std::vector completions; - executor_.GetSuggestions(input, completions); + const auto err = executor_.GetSuggestions(input, completions); replxx::Replxx::completions_t result; - for (const std::string& suggestion : completions) result.emplace_back(suggestion); + if (err.ok()) { + for (const std::string& suggestion : completions) { + result.emplace_back(suggestion); + } + } return result; }); } @@ -43,7 +47,8 @@ void CommandsProcessor::setCompletionCallback(T& rx, void (T::*set_ (rx.*set_completion_callback)( [this](std::string const& input, int, void*) -> replxx::Replxx::completions_t { std::vector completions; - executor_.GetSuggestions(input, completions); + const auto err = executor_.GetSuggestions(input, completions); + if (!err.ok()) return {}; return completions; }, nullptr); diff --git a/cpp_src/cmd/reindexer_tool/readme.md b/cpp_src/cmd/reindexer_tool/readme.md index 80443bd90..fe104486b 100644 --- a/cpp_src/cmd/reindexer_tool/readme.md +++ b/cpp_src/cmd/reindexer_tool/readme.md @@ -21,7 +21,7 @@ Reindexer command line tool is an client utility to work with database. reindexer_tool {OPTIONS} Options - -d[DSN], --dsn=[DSN] DSN to 'reindexer', like 'cproto://127.0.0.1:6534/dbname' or 'builtin:///var/lib/reindexer/dbname' + -d[DSN], --dsn=[DSN] DSN to 'reindexer', like 'cproto://127.0.0.1:6534/dbname', 'builtin:///var/lib/reindexer/dbname' or `ucproto://user@password:/tmp/reindexer.sock:/dbname` -f[FILENAME], --filename=[FILENAME] Execute commands from file, then exit -c[COMMAND], --command=[COMMAND] Run only single command (SQL or internal) and exit -o[FILENAME], --output=[FILENAME] Send query results to file diff --git a/cpp_src/cmd/reindexer_tool/reindexer_tool.cc b/cpp_src/cmd/reindexer_tool/reindexer_tool.cc index f347105da..faa84d2b0 100644 --- a/cpp_src/cmd/reindexer_tool/reindexer_tool.cc +++ b/cpp_src/cmd/reindexer_tool/reindexer_tool.cc @@ -56,8 +56,16 @@ int main(int argc, char* argv[]) { args::HelpFlag help(parser, "help", "show this message", {'h', "help"}); args::Group progOptions("options"); - args::ValueFlag dbDsn(progOptions, "DSN", "DSN to 'reindexer'. Can be 'cproto://:/' or 'builtin://'", - {'d', "dsn"}, "", Options::Single | Options::Global); +#ifdef _WIN32 + args::ValueFlag dbDsn(progOptions, "DSN", + "DSN to 'reindexer'. Can be 'cproto://:/' or 'builtin://'", {'d', "dsn"}, "", + Options::Single | Options::Global); +#else // _WIN32 + args::ValueFlag dbDsn( + progOptions, "DSN", + "DSN to 'reindexer'. Can be 'cproto://:/', 'builtin://' or 'ucproto://:/'", + {'d', "dsn"}, "", Options::Single | Options::Global); +#endif // _WIN32 args::ValueFlag fileName(progOptions, "FILENAME", "execute commands from file, then exit", {'f', "filename"}, "", Options::Single | Options::Global); args::ValueFlag command(progOptions, "COMMAND", "run only single command (SQL or internal) and exit'", {'c', "command"}, "", @@ -106,6 +114,8 @@ int main(int argc, char* argv[]) { signal(SIGPIPE, SIG_IGN); #endif + using namespace std::string_view_literals; + using reindexer::checkIfStartsWithCS; std::string db; if (dsn.empty()) { db = args::get(dbName); @@ -113,7 +123,15 @@ int main(int argc, char* argv[]) { std::cerr << "Error: --dsn either database name should be set as a first argument" << std::endl; return 2; } - if (db.substr(0, 9) == "cproto://" || db.substr(0, 10) == "builtin://") { + + if (checkIfStartsWithCS("cproto://"sv, db) || checkIfStartsWithCS("ucproto://"sv, db) || checkIfStartsWithCS("builtin://"sv, db)) { +#ifdef _WIN32 + if (checkIfStartsWithCS("ucproto://"sv, db) == 0) { + std::cerr << "Invalid DSN: ucproto:// is not supported on the Windows platform. Use cproto:// or builtin:// instead" + << std::endl; + return 2; + } +#endif // _WIN32 dsn = db; } else { dsn = "cproto://reindexer:reindexer@127.0.0.1:6534/" + db; @@ -133,7 +151,14 @@ int main(int argc, char* argv[]) { std::cout << "Reindexer command line tool version " << REINDEX_VERSION << std::endl; } - if (dsn.compare(0, 9, "cproto://") == 0) { + if (checkIfStartsWithCS("cproto://"sv, dsn) || checkIfStartsWithCS("ucproto://"sv, dsn)) { +#ifdef _WIN32 + if (checkIfStartsWithCS("ucproto://"sv, dsn)) { + std::cerr << "Invalid DSN: ucproto:// is not supported on the Windows platform. Use cproto:// or builtin:// instead" + << std::endl; + return 2; + } +#endif // _WIN32 reindexer::client::ReindexerConfig config; config.EnableCompression = true; config.AppName = args::get(appName); @@ -141,13 +166,17 @@ int main(int argc, char* argv[]) { args::get(connThreads), config); err = commandsProcessor.Connect(dsn, reindexer::client::ConnectOpts().CreateDBIfMissing(createDBF && args::get(createDBF))); if (err.ok()) ok = commandsProcessor.Run(args::get(command), args::get(dumpMode)); - } else if (dsn.compare(0, 10, "builtin://") == 0) { + } else if (checkIfStartsWithCS("builtin://"sv, dsn)) { reindexer::Reindexer db; CommandsProcessor commandsProcessor(args::get(outFileName), args::get(fileName), args::get(connThreads)); err = commandsProcessor.Connect(dsn, ConnectOpts().DisableReplication()); if (err.ok()) ok = commandsProcessor.Run(args::get(command), args::get(dumpMode)); } else { +#ifdef _WIN32 std::cerr << "Invalid DSN format: " << dsn << " Must begin from cproto:// or builtin://" << std::endl; +#else // _WIN32 + std::cerr << "Invalid DSN format: " << dsn << " Must begin from cproto://, ucproto:// or builtin://" << std::endl; +#endif // _WIN32 } if (!err.ok()) { std::cerr << "ERROR: " << err.what() << std::endl; diff --git a/cpp_src/cmd/reindexer_tool/repair_tool.cc b/cpp_src/cmd/reindexer_tool/repair_tool.cc index aba2ec050..2daae4555 100644 --- a/cpp_src/cmd/reindexer_tool/repair_tool.cc +++ b/cpp_src/cmd/reindexer_tool/repair_tool.cc @@ -5,12 +5,16 @@ #include "iotools.h" #include "tools/fsops.h" +#ifdef REINDEX_WITH_V3_FOLLOWERS +#include "replv3/updatesobserver.h" +#endif // REINDEX_WITH_V3_FOLLOWERS + namespace reindexer_tool { const char kStoragePlaceholderFilename[] = ".reindexer.storage"; constexpr unsigned kStorageLoadingThreads = 6; -Error RepairTool::RepairStorage(const std::string& dsn) noexcept { +Error RepairTool::RepairStorage(const std::string &dsn) noexcept { if (dsn.compare(0, 10, "builtin://") != 0) { return Error(errParams, "Invalid DSN format for repair: %s. Must begin from builtin://", dsn); } @@ -25,14 +29,14 @@ Error RepairTool::RepairStorage(const std::string& dsn) noexcept { std::unique_ptr storage; try { storage.reset(reindexer::datastorage::StorageFactory::create(storageType)); - } catch (std::exception& ex) { + } catch (std::exception &ex) { return Error(errParams, "Skiping DB at '%s' - ", path, ex.what()); } std::vector foundNs; if (reindexer::fs::ReadDir(path, foundNs) < 0) { return Error(errParams, "Can't read dir to repair: %s", path); } - for (auto& ns : foundNs) { + for (auto &ns : foundNs) { if (ns.isDir && reindexer::validateObjectName(ns.name, true)) { auto err = repairNamespace(storage.get(), path, ns.name, storageType); if (!err.ok()) { @@ -48,7 +52,7 @@ Error RepairTool::RepairStorage(const std::string& dsn) noexcept { return hasErrors ? Error(errParams, "Some of namespaces had repair errors") : errOK; } -Error RepairTool::repairNamespace(IDataStorage* storage, const std::string& storagePath, const std::string& name, +Error RepairTool::repairNamespace(IDataStorage *storage, const std::string &storagePath, const std::string &name, StorageType storageType) noexcept { auto nsPath = reindexer::fs::JoinPath(storagePath, name); std::cout << "Repairing " << nsPath << "..." << std::endl; @@ -61,13 +65,36 @@ Error RepairTool::repairNamespace(IDataStorage* storage, const std::string& stor if (!reindexer::validateObjectName(name, true)) { return Error(errParams, "Namespace name contains invalid character. Only alphas, digits,'_','-', are allowed"); } - reindexer::NamespaceImpl ns(name, {}, nullptr); + class DummyClusterizator final : public reindexer::cluster::INsDataReplicator { + Error Replicate(reindexer::cluster::UpdateRecord &&, std::function f, const reindexer::RdxContext &) override { + f(); + return {}; + } + Error Replicate(reindexer::cluster::UpdatesContainer &&, std::function f, const reindexer::RdxContext &) override { + f(); + return {}; + } + Error ReplicateAsync(reindexer::cluster::UpdateRecord &&, const reindexer::RdxContext &) override { return {}; } + Error ReplicateAsync(reindexer::cluster::UpdatesContainer &&, const reindexer::RdxContext &) override { return {}; } + void AwaitInitialSync(std::string_view, const reindexer::RdxContext &) const override {} + void AwaitInitialSync(const reindexer::RdxContext &) const override {} + bool IsInitialSyncDone(std::string_view) const override { return true; } + bool IsInitialSyncDone() const override { return true; } + }; + DummyClusterizator dummyClusterizator; + +#ifdef REINDEX_WITH_V3_FOLLOWERS + reindexer::UpdatesObservers observers; + reindexer::NamespaceImpl ns(name, {}, dummyClusterizator, observers); +#else // REINDEX_WITH_V3_FOLLOWERS + reindexer::NamespaceImpl ns(name, {}, dummyClusterizator); +#endif // REINDEX_WITH_V3_FOLLOWERS StorageOpts storageOpts; reindexer::RdxContext dummyCtx; std::cout << "Loading " << name << std::endl; ns.EnableStorage(storagePath, storageOpts.Enabled(true), storageType, dummyCtx); ns.LoadFromStorage(kStorageLoadingThreads, dummyCtx); - } catch (const Error& err) { + } catch (const Error &err) { std::cout << "Namespace was not repaired: " << err.what() << ". Should it be deleted? y/N" << std::endl; for (;;) { std::string input; diff --git a/cpp_src/core/cbinding/reindexer_c.cc b/cpp_src/core/cbinding/reindexer_c.cc index 837005e0c..c66ed41c7 100644 --- a/cpp_src/core/cbinding/reindexer_c.cc +++ b/cpp_src/core/cbinding/reindexer_c.cc @@ -8,14 +8,11 @@ #include "cgocancelcontextpool.h" #include "core/cjson/baseencoder.h" -#include "core/selectfunc/selectfuncparser.h" -#include "debug/allocdebug.h" #include "estl/syncpool.h" #include "reindexer_version.h" #include "resultserializer.h" #include "tools/logger.h" #include "tools/semversion.h" -#include "tools/stringstools.h" using namespace reindexer; const int kQueryResultsPoolSize = 1024; @@ -28,6 +25,7 @@ static Error err_not_init(errNotValid, "Reindexer db has not initialized"); static Error err_too_many_queries(errLogic, "Too many parallel queries"); static std::atomic bindingCaps; +static_assert(std::atomic::is_always_lock_free, "Expected lockfree structure here"); static reindexer_error error2c(const Error& err_) { reindexer_error err; @@ -266,6 +264,9 @@ reindexer_ret reindexer_modify_item_packed(uintptr_t rx, reindexer_buffer args, case ModeDelete: err = rdxKeeper.db().Delete(ns, item, *res); break; + default: + err = Error(errParams, "Unexpected ItemModifyMode: %d", mode); + break; } } else { switch (mode) { @@ -281,6 +282,9 @@ reindexer_ret reindexer_modify_item_packed(uintptr_t rx, reindexer_buffer args, case ModeDelete: err = rdxKeeper.db().Delete(ns, item); break; + default: + err = Error(errParams, "Unexpected ItemModifyMode: %d", mode); + break; } if (err.ok()) { LocalQueryResults lqr; @@ -461,15 +465,6 @@ reindexer_error reindexer_set_schema(uintptr_t rx, reindexer_string nsName, rein return error2c(res); } -reindexer_error reindexer_enable_storage(uintptr_t rx, reindexer_string path, reindexer_ctx_info ctx_info) { - Error res = err_not_init; - if (rx) { - CGORdxCtxKeeper rdxKeeper(rx, ctx_info, ctx_pool); - res = rdxKeeper.db().EnableStorage(str2c(path)); - } - return error2c(res); -} - // TODO: Rename this method, when all the connectors will support new version of connect reindexer_error reindexer_connect_v4(uintptr_t rx, reindexer_string dsn, ConnectOpts opts, reindexer_string client_vers, BindingCapabilities caps) { @@ -539,17 +534,14 @@ reindexer_ret reindexer_select_query(uintptr_t rx, struct reindexer_buffer in, i Serializer ser(in.data, in.len); CGORdxCtxKeeper rdxKeeper(rx, ctx_info, ctx_pool); - Query q; - q.Deserialize(ser); + Query q = Query::Deserialize(ser); while (!ser.Eof()) { - JoinedQuery q1; - q1.joinType = JoinType(ser.GetVarUint()); - q1.Deserialize(ser); - q1.debugLevel = q.debugLevel; + const auto joinType = JoinType(ser.GetVarUint()); + JoinedQuery q1{joinType, Query::Deserialize(ser)}; if (q1.joinType == JoinType::Merge) { - q.mergeQueries_.emplace_back(std::move(q1)); + q.Merge(std::move(q1)); } else { - q.joinQueries_.emplace_back(std::move(q1)); + q.AddJoinQuery(std::move(q1)); } } @@ -558,7 +550,7 @@ reindexer_ret reindexer_select_query(uintptr_t rx, struct reindexer_buffer in, i return ret2c(err_too_many_queries, out); } err = rdxKeeper.db().Select(q, *result); - if (q.debugLevel >= LogError && err.code() != errOK) logPrintf(LogError, "Query error %s", err.what()); + if (q.GetDebugLevel() >= LogError && err.code() != errOK) logPrintf(LogError, "Query error %s", err.what()); if (err.ok()) { results2c(std::move(result), &out, as_json, pt_versions, pt_versions_count); } else { @@ -583,16 +575,15 @@ reindexer_ret reindexer_delete_query(uintptr_t rx, reindexer_buffer in, reindexe Serializer ser(in.data, in.len); CGORdxCtxKeeper rdxKeeper(rx, ctx_info, ctx_pool); - Query q; + Query q = Query::Deserialize(ser); q.type_ = QueryDelete; - q.Deserialize(ser); auto result{new_results(false)}; if (!result) { return ret2c(err_too_many_queries, out); } res = rdxKeeper.db().Delete(q, *result); - if (q.debugLevel >= LogError && res.code() != errOK) logPrintf(LogError, "Query error %s", res.what()); + if (q.GetDebugLevel() >= LogError && res.code() != errOK) logPrintf(LogError, "Query error %s", res.what()); if (res.ok()) { results2c(std::move(result), &out); } @@ -612,15 +603,14 @@ reindexer_ret reindexer_update_query(uintptr_t rx, reindexer_buffer in, reindexe Serializer ser(in.data, in.len); CGORdxCtxKeeper rdxKeeper(rx, ctx_info, ctx_pool); - Query q; - q.Deserialize(ser); + Query q = Query::Deserialize(ser); q.type_ = QueryUpdate; auto result{new_results(false)}; if (!result) { return ret2c(err_too_many_queries, out); } res = rdxKeeper.db().Update(q, *result); - if (q.debugLevel >= LogError && res.code() != errOK) logPrintf(LogError, "Query error %s", res.what()); + if (q.GetDebugLevel() >= LogError && res.code() != errOK) logPrintf(LogError, "Query error %s", res.what()); if (res.ok()) { int32_t ptVers = -1; results2c(std::move(result), &out, 0, &ptVers, 1); @@ -642,9 +632,8 @@ reindexer_error reindexer_delete_query_tx(uintptr_t rx, uintptr_t tr, reindexer_ return error2c(errOK); } Serializer ser(in.data, in.len); - Query q; try { - q.Deserialize(ser); + Query q = Query::Deserialize(ser); q.type_ = QueryDelete; Error err = trw->tr_.Modify(std::move(q)); @@ -664,9 +653,8 @@ reindexer_error reindexer_update_query_tx(uintptr_t rx, uintptr_t tr, reindexer_ return error2c(errOK); } Serializer ser(in.data, in.len); - Query q; try { - q.Deserialize(ser); + Query q = Query::Deserialize(ser); q.type_ = QueryUpdate; Error err = trw->tr_.Modify(std::move(q)); diff --git a/cpp_src/core/cbinding/reindexer_c.h b/cpp_src/core/cbinding/reindexer_c.h index 13676178f..1f9e8754d 100644 --- a/cpp_src/core/cbinding/reindexer_c.h +++ b/cpp_src/core/cbinding/reindexer_c.h @@ -17,7 +17,6 @@ reindexer_error reindexer_connect_v4(uintptr_t rx, reindexer_string dsn, Connect reindexer_error reindexer_connect(uintptr_t rx, reindexer_string dsn, ConnectOpts opts, reindexer_string client_vers); reindexer_error reindexer_ping(uintptr_t rx); -reindexer_error reindexer_enable_storage(uintptr_t rx, reindexer_string path, reindexer_ctx_info ctx_info); reindexer_error reindexer_init_system_namespaces(uintptr_t rx); reindexer_error reindexer_open_namespace(uintptr_t rx, reindexer_string nsName, StorageOpts opts, reindexer_ctx_info ctx_info); diff --git a/cpp_src/core/cbinding/resultserializer.cc b/cpp_src/core/cbinding/resultserializer.cc index fe440e13b..4012754ee 100644 --- a/cpp_src/core/cbinding/resultserializer.cc +++ b/cpp_src/core/cbinding/resultserializer.cc @@ -21,7 +21,7 @@ constexpr int kKnownResultsFlagsMask = int(GetKnownFlagsBitMask(kResultsFlagMaxV void WrResultSerializer::resetUnknownFlags() noexcept { opts_.flags &= kKnownResultsFlagsMask; } -void WrResultSerializer::putQueryParams(QueryResults* results) { +void WrResultSerializer::putQueryParams(const BindingCapabilities& caps, QueryResults* results) { // Flags of present objects PutVarUint(opts_.flags); // Total @@ -33,18 +33,19 @@ void WrResultSerializer::putQueryParams(QueryResults* results) { if (opts_.flags & kResultsWithPayloadTypes) { assertrx(opts_.ptVersions.data()); - if (int(opts_.ptVersions.size()) != results->GetMergedNSCount()) { + const auto mergedNsCount = results->GetMergedNSCount(); + if (int(opts_.ptVersions.size()) != mergedNsCount) { logPrintf(LogWarning, "ptVersionsCount != results->GetMergedNSCount: %d != %d. Client's meta data can become incosistent.", - opts_.ptVersions.size(), results->GetMergedNSCount()); + opts_.ptVersions.size(), mergedNsCount); } auto cntP = getPtUpdatesCount(results); putPayloadTypes(*this, results, opts_, cntP.first, cntP.second); } - putExtraParams(results); + putExtraParams(caps, results); } -void WrResultSerializer::putExtraParams(QueryResults* results) { +void WrResultSerializer::putExtraParams(const BindingCapabilities& caps, QueryResults* results) { if (opts_.withAggregations) { for (const AggregationResult& aggregationRes : results->GetAggregationResults()) { PutVarUint(QueryResultAggregation); @@ -68,12 +69,24 @@ void WrResultSerializer::putExtraParams(QueryResults* results) { PutVarUint(results->GetCommonShardID()); opts_.flags &= ~kResultsWithShardId; // not set shardId for item } + } + if (caps.HasIncarnationTags()) { int64_t shardingConfVer = results->GetShardingConfigVersion(); if (shardingConfVer != -1) { PutVarUint(QueryResultShardingVersion); PutVarUint(shardingConfVer); } + PutVarUint(QueryResultIncarnationTags); + auto tags = results->GetIncarnationTags(); + PutVarUint(tags.size()); + for (auto& shardTags : tags) { + PutVarint(shardTags.shardId); + PutVarUint(shardTags.tags.size()); + for (auto& t : shardTags.tags) { + PutVarint(int64_t(t)); + } + } } PutVarUint(QueryResultEnd); @@ -181,11 +194,12 @@ void WrResultSerializer::putPayloadTypes(WrSerializer& ser, const QueryResults* std::pair WrResultSerializer::getPtUpdatesCount(const QueryResults* results) { if (opts_.flags & kResultsWithPayloadTypes) { assertrx(opts_.ptVersions.data()); - if (int(opts_.ptVersions.size()) != results->GetMergedNSCount()) { + const auto mergedNsCount = results->GetMergedNSCount(); + if (int(opts_.ptVersions.size()) != mergedNsCount) { logPrintf(LogWarning, "ptVersionsCount != results->GetMergedNSCount: %d != %d. Client's meta data can become incosistent.", - opts_.ptVersions.size(), results->GetMergedNSCount()); + opts_.ptVersions.size(), mergedNsCount); } - int cnt = 0, totalCnt = std::min(results->GetMergedNSCount(), int(opts_.ptVersions.size())); + int cnt = 0, totalCnt = std::min(mergedNsCount, int(opts_.ptVersions.size())); for (int i = 0; i < totalCnt; i++) { const TagsMatcher& tm = results->GetTagsMatcher(i); @@ -234,7 +248,7 @@ bool WrResultSerializer::PutResults(QueryResults* result, const BindingCapabilit } } - putQueryParams(result); + putQueryParams(caps, result); size_t saveLen = len_; const bool storeAsPointers = (opts_.flags & kResultsFormatMask) == kResultsPtrs; auto ptrStorage = storeAsPointers ? storage : nullptr; @@ -255,8 +269,7 @@ bool WrResultSerializer::PutResults(QueryResults* result, const BindingCapabilit PutVarUint(it.ItemsCount()); if (it.ItemsCount() == 0) continue; LocalQueryResults qr = it.ToQueryResults(); - qr.addNSContext(result->GetPayloadType(joinedField), result->GetTagsMatcher(joinedField), - result->GetFieldsFilter(joinedField), result->GetSchema(joinedField)); + qr.addNSContext(*result, joinedField, lsn_t()); for (auto& jit : qr) putItemParams(jit, rowIt.GetShardId(), storage, nullptr); } } diff --git a/cpp_src/core/cbinding/resultserializer.h b/cpp_src/core/cbinding/resultserializer.h index 9155edaa8..b77b756c3 100644 --- a/cpp_src/core/cbinding/resultserializer.h +++ b/cpp_src/core/cbinding/resultserializer.h @@ -42,10 +42,10 @@ class WrResultSerializer : public WrSerializer { private: void resetUnknownFlags() noexcept; - void putQueryParams(QueryResults* query); + void putQueryParams(const BindingCapabilities& caps, QueryResults* query); template void putItemParams(ItT& it, int shardId, QueryResults::ProxiedRefsStorage* storage, const QueryResults* result); - void putExtraParams(QueryResults* query); + void putExtraParams(const BindingCapabilities& caps, QueryResults* query); static void putPayloadTypes(WrSerializer& ser, const QueryResults* results, const ResultFetchOpts& opts, int cnt, int totalCnt); std::pair getPtUpdatesCount(const QueryResults* results); ResultFetchOpts opts_; diff --git a/cpp_src/core/cjson/cjsondecoder.cc b/cpp_src/core/cjson/cjsondecoder.cc index e6a7c2d73..b6e61adb4 100644 --- a/cpp_src/core/cjson/cjsondecoder.cc +++ b/cpp_src/core/cjson/cjsondecoder.cc @@ -7,71 +7,57 @@ namespace reindexer { -bool CJsonDecoder::decodeCJson(Payload &pl, Serializer &rdser, WrSerializer &wrser, bool match) { +template +bool CJsonDecoder::decodeCJson(Payload &pl, Serializer &rdser, WrSerializer &wrser, FilterT filter, RecoderT recoder, TagOptT) { const ctag tag = rdser.GetCTag(); + TagType tagType = tag.Type(); if (tag == kCTagEnd) { wrser.PutCTag(kCTagEnd); return false; } - const int tagName = tag.Name(); - if (tagName) { - // Check + int tagName = 0; + if constexpr (std::is_same_v) { + tagName = tag.Name(); + assertrx_dbg(tagName); + // Check if tag exists (void)tagsMatcher_.tag2name(tagName); tagsPath_.emplace_back(tagName); } + if rx_unlikely (tag.Field() >= 0) { - throw Error(errLogic, "Reference tag was found in transport CJSON for field %d[%s] in ns [%s]", tag.Field(), - tagsMatcher_.tag2name(tagName), pl.Type().Name()); + throwTagReferenceError(tag, pl); } const int field = tagsMatcher_.tags2field(tagsPath_.data(), tagsPath_.size()); - - if (filter_) { - if (field >= 0) { - match = filter_->contains(field); - } else { - match = match && filter_->match(tagsPath_); - } - } - Recoder *recoder{nullptr}; - if (recoder_) { - if (field >= 0) { - if (recoder_->Match(field)) { - recoder = recoder_; - } - } else { - if (recoder_->Match(tagsPath_)) { - recoder = recoder_; - } - } - } - TagType tagType = tag.Type(); - if (recoder) { - tagType = recoder->Type(tagType); - } if (field >= 0) { + const bool match = filter.contains(field); if (match) { + tagType = recoder.RegisterTagType(tagType, field); if (tagType == TAG_NULL) { - objectScalarIndexes_.set(field); wrser.PutCTag(ctag{TAG_NULL, tagName}); - } else if (recoder) { - recoder->Recode(rdser, pl, tagName, wrser); + } else if (recoder.Recode(rdser, pl, tagName, wrser)) { + // No more actions needed after recoding } else { const auto &fieldRef{pl.Type().Field(field)}; const KeyValueType fieldType{fieldRef.Type()}; if (tagType == TAG_ARRAY) { if rx_unlikely (!fieldRef.IsArray()) { - throw Error(errLogic, "Error parsing cjson field '%s' - got array, expected scalar %s", fieldRef.Name(), - fieldType.Name()); + throwUnexpectedArrayError(fieldRef); } const carraytag atag = rdser.GetCArrayTag(); const auto count = atag.Count(); const int ofs = pl.ResizeArray(field, count, true); const TagType atagType = atag.Type(); - for (size_t i = 0; i < count; ++i) { - const TagType type = atagType != TAG_OBJECT ? atagType : rdser.GetCTag().Type(); - pl.Set(field, ofs + i, cjsonValueToVariant(type, rdser, fieldType)); + if (atagType != TAG_OBJECT) { + for (size_t i = 0; i < count; ++i) { + pl.Set(field, ofs + i, cjsonValueToVariant(atagType, rdser, fieldType)); + } + } else { + for (size_t i = 0; i < count; ++i) { + pl.Set(field, ofs + i, cjsonValueToVariant(rdser.GetCTag().Type(), rdser, fieldType)); + } } + wrser.PutCTag(ctag{TAG_ARRAY, tagName, field}); wrser.PutVarUint(count); } else { @@ -93,44 +79,76 @@ bool CJsonDecoder::decodeCJson(Payload &pl, Serializer &rdser, WrSerializer &wrs skipCjsonTag(tag, rdser); } } else { - wrser.PutCTag(ctag{tagType, tagName, field}); - if (tagType == TAG_OBJECT) { - while (decodeCJson(pl, rdser, wrser, match)) - ; - } else if (!match) { - skipCjsonTag(tag, rdser); - } else if (recoder) { - recoder->Recode(rdser, wrser); - } else if (tagType == TAG_ARRAY) { - const carraytag atag = rdser.GetCArrayTag(); - wrser.PutCArrayTag(atag); - const auto count = atag.Count(); - const TagType atagType = atag.Type(); - CounterGuardIR32 g(arrayLevel_); - for (size_t i = 0; i < count; ++i) { - switch (atagType) { - case TAG_OBJECT: - decodeCJson(pl, rdser, wrser, match); - break; - case TAG_VARINT: - case TAG_NULL: - case TAG_BOOL: - case TAG_STRING: - case TAG_END: - case TAG_DOUBLE: - case TAG_ARRAY: - case TAG_UUID: + const bool match = filter.match(tagsPath_); + if (match) { + tagType = recoder.RegisterTagType(tagType, tagsPath_); + wrser.PutCTag(ctag{tagType, tagName, field}); + if (tagType == TAG_OBJECT) { + while (decodeCJson(pl, rdser, wrser, filter.MakeCleanCopy(), recoder.MakeCleanCopy(), NamedTagOpt{})) + ; + } else if (recoder.Recode(rdser, wrser)) { + // No more actions needed after recoding + } else if (tagType == TAG_ARRAY) { + const carraytag atag = rdser.GetCArrayTag(); + wrser.PutCArrayTag(atag); + const auto count = atag.Count(); + const TagType atagType = atag.Type(); + CounterGuardIR32 g(arrayLevel_); + if (atagType == TAG_OBJECT) { + for (size_t i = 0; i < count; ++i) { + decodeCJson(pl, rdser, wrser, filter.MakeCleanCopy(), recoder.MakeCleanCopy(), NamelessTagOpt{}); + } + } else { + for (size_t i = 0; i < count; ++i) { copyCJsonValue(atagType, rdser, wrser); - break; + } } + } else { + copyCJsonValue(tagType, rdser, wrser); } + } else if (tagType != TAG_OBJECT) { + // !match + skipCjsonTag(tag, rdser); } else { - copyCJsonValue(tagType, rdser, wrser); + // !match + wrser.PutCTag(ctag{tagType, tagName, field}); + while (decodeCJson(pl, rdser, wrser, filter.MakeSkipFilter(), recoder.MakeCleanCopy(), NamedTagOpt{})) + ; } } - if (tagName) tagsPath_.pop_back(); + if constexpr (std::is_same_v) { + tagsPath_.pop_back(); + } + return true; } +[[nodiscard]] Variant CJsonDecoder::cjsonValueToVariant(TagType tagType, Serializer &rdser, KeyValueType fieldType) { + if (fieldType.Is() && tagType != TagType::TAG_STRING) { + storage_.emplace_back(rdser.GetRawVariant(KeyValueType{tagType}).As()); + return Variant(p_string(&storage_.back()), false); + } else { + return reindexer::cjsonValueToVariant(tagType, rdser, fieldType); + } +} + +RX_NO_INLINE void CJsonDecoder::throwTagReferenceError(ctag tag, const Payload &pl) { + throw Error(errLogic, "Reference tag was found in transport CJSON for field %d[%s] in ns [%s]", tag.Field(), + tagsMatcher_.tag2name(tag.Name()), pl.Type().Name()); +} + +RX_NO_INLINE void CJsonDecoder::throwUnexpectedArrayError(const PayloadFieldType &fieldRef) { + throw Error(errLogic, "Error parsing cjson field '%s' - got array, expected scalar %s", fieldRef.Name(), fieldRef.Type().Name()); +} + +template bool CJsonDecoder::decodeCJson( + Payload &, Serializer &, WrSerializer &, CJsonDecoder::DummyFilter, CJsonDecoder::DummyRecoder, CJsonDecoder::NamelessTagOpt); +template bool CJsonDecoder::decodeCJson( + Payload &, Serializer &, WrSerializer &, CJsonDecoder::DummyFilter, CJsonDecoder::DefaultRecoder, CJsonDecoder::NamelessTagOpt); +template bool CJsonDecoder::decodeCJson( + Payload &, Serializer &, WrSerializer &, CJsonDecoder::RestrictingFilter, CJsonDecoder::DummyRecoder, CJsonDecoder::NamelessTagOpt); +template bool CJsonDecoder::decodeCJson( + Payload &, Serializer &, WrSerializer &, CJsonDecoder::RestrictingFilter, CJsonDecoder::DefaultRecoder, CJsonDecoder::NamelessTagOpt); + } // namespace reindexer diff --git a/cpp_src/core/cjson/cjsondecoder.h b/cpp_src/core/cjson/cjsondecoder.h index a128fe487..9835c4039 100644 --- a/cpp_src/core/cjson/cjsondecoder.h +++ b/cpp_src/core/cjson/cjsondecoder.h @@ -1,5 +1,6 @@ #pragma once +#include #include "core/payload/payloadiface.h" namespace reindexer { @@ -20,25 +21,157 @@ class Recoder { class CJsonDecoder { public: - CJsonDecoder(TagsMatcher &tagsMatcher) noexcept : tagsMatcher_(tagsMatcher), filter_(nullptr) {} - CJsonDecoder(TagsMatcher &tagsMatcher, const FieldsSet *filter, Recoder *recoder) noexcept - : tagsMatcher_(tagsMatcher), filter_(filter), recoder_(recoder) {} + explicit CJsonDecoder(TagsMatcher &tagsMatcher, std::deque &storage) noexcept + : tagsMatcher_(tagsMatcher), storage_(storage) {} + class SkipFilter { + public: + SkipFilter MakeCleanCopy() const noexcept { return SkipFilter(); } + SkipFilter MakeSkipFilter() const noexcept { return SkipFilter(); } - void Decode(Payload &pl, Serializer &rdSer, WrSerializer &wrSer) { + RX_ALWAYS_INLINE bool contains([[maybe_unused]] int field) const noexcept { return false; } + RX_ALWAYS_INLINE bool match(const TagsPath &) const noexcept { return false; } + }; + + class DummyFilter { + public: + DummyFilter MakeCleanCopy() const noexcept { return DummyFilter(); } + SkipFilter MakeSkipFilter() const noexcept { return SkipFilter(); } + RX_ALWAYS_INLINE bool HasArraysFields(const PayloadTypeImpl &) const noexcept { return false; } + + RX_ALWAYS_INLINE bool contains([[maybe_unused]] int field) const noexcept { return true; } + RX_ALWAYS_INLINE bool match(const TagsPath &) const noexcept { return true; } + }; + + class IndexedSkipFilter { + public: + IndexedSkipFilter(const FieldsSet &f) noexcept : f_(&f) {} + IndexedSkipFilter MakeCleanCopy() const noexcept { return IndexedSkipFilter(*f_); } + IndexedSkipFilter MakeSkipFilter() const noexcept { return IndexedSkipFilter(*f_); } + + RX_ALWAYS_INLINE bool contains(int field) const noexcept { return f_->contains(field); } + RX_ALWAYS_INLINE bool match(const TagsPath &) const noexcept { return false; } + + private: + const FieldsSet *f_; + }; + + class RestrictingFilter { + public: + RestrictingFilter(const FieldsSet &f) noexcept : f_(&f), match_(true) {} + + RestrictingFilter MakeCleanCopy() const noexcept { return RestrictingFilter(*f_); } + IndexedSkipFilter MakeSkipFilter() const noexcept { return IndexedSkipFilter(*f_); } + RX_ALWAYS_INLINE bool HasArraysFields(const PayloadTypeImpl &pt) const noexcept { + for (auto f : *f_) { + if (f >= 0 && pt.Field(f).IsArray()) { + return true; + } + } + return false; + } + + RX_ALWAYS_INLINE bool contains(int field) noexcept { + match_ = f_->contains(field); + return match_; + } + RX_ALWAYS_INLINE bool match(const TagsPath &tagsPath) noexcept { + match_ = match_ && f_->getTagsPathsLength() && f_->match(tagsPath); + return match_; + } + + private: + const FieldsSet *f_; + bool match_; + }; + + class DummyRecoder { + public: + RX_ALWAYS_INLINE DummyRecoder MakeCleanCopy() const noexcept { return DummyRecoder(); } + RX_ALWAYS_INLINE bool Recode(Serializer &, WrSerializer &) const noexcept { return false; } + RX_ALWAYS_INLINE bool Recode(Serializer &, Payload &, [[maybe_unused]] int tagName, WrSerializer &) const noexcept { return false; } + RX_ALWAYS_INLINE TagType RegisterTagType(TagType oldTagType, [[maybe_unused]] int field) const noexcept { return oldTagType; } + RX_ALWAYS_INLINE TagType RegisterTagType(TagType oldTagType, const TagsPath &) const noexcept { return oldTagType; } + }; + class DefaultRecoder { + public: + DefaultRecoder(Recoder &r) noexcept : r_(&r), needToRecode_(false) {} + + RX_ALWAYS_INLINE DefaultRecoder MakeCleanCopy() const noexcept { return DefaultRecoder(*r_); } + + RX_ALWAYS_INLINE bool Recode(Serializer &ser, WrSerializer &wser) const { + if (needToRecode_) { + r_->Recode(ser, wser); + } + return needToRecode_; + } + RX_ALWAYS_INLINE bool Recode(Serializer &s, Payload &p, int tagName, WrSerializer &wser) const { + if (needToRecode_) { + r_->Recode(s, p, tagName, wser); + } + return needToRecode_; + } + RX_ALWAYS_INLINE TagType RegisterTagType(TagType oldTagType, int field) { + needToRecode_ = r_->Match(field); + return needToRecode_ ? r_->Type(oldTagType) : oldTagType; + } + RX_ALWAYS_INLINE TagType RegisterTagType(TagType oldTagType, const TagsPath &tagsPath) { + needToRecode_ = r_->Match(tagsPath); + return needToRecode_ ? r_->Type(oldTagType) : oldTagType; + } + + private: + Recoder *r_; + bool needToRecode_; + }; + struct NamedTagOpt {}; + struct NamelessTagOpt {}; + + template + void Decode(Payload &pl, Serializer &rdSer, WrSerializer &wrSer, FilterT filter = FilterT(), RecoderT recoder = RecoderT()) { + static_assert(std::is_same_v || std::is_same_v, + "Other filter types are not allowed for the public API"); + static_assert(std::is_same_v || std::is_same_v, + "Other recoder types are not allowed for the public API"); objectScalarIndexes_.reset(); - decodeCJson(pl, rdSer, wrSer, true); + if rx_likely (!filter.HasArraysFields(pl.Type())) { + decodeCJson(pl, rdSer, wrSer, filter, recoder, NamelessTagOpt{}); + return; + } +#ifdef RX_WITH_STDLIB_DEBUG + std::abort(); +#else + // Search of the indexed fields inside the object arrays is not imlpemented + // Possible implementation has noticable negative effect on 'FromCJSONPKOnly' benchmark. + // Currently we are using filter for PKs only, and PKs can not be arrays, so this code actually will never be called at the + // current moment + decodeCJson(pl, rdSer, wrSer, DummyFilter(), recoder, NamelessTagOpt{}); +#endif // RX_WITH_STDLIB_DEBUG } private: - bool decodeCJson(Payload &pl, Serializer &rdser, WrSerializer &wrser, bool match); + template + bool decodeCJson(Payload &pl, Serializer &rdser, WrSerializer &wrser, FilterT filter, RecoderT recoder, TagOptT); bool isInArray() const noexcept { return arrayLevel_ > 0; } + [[noreturn]] void throwTagReferenceError(ctag, const Payload &); + [[noreturn]] void throwUnexpectedArrayError(const PayloadFieldType &); + + [[nodiscard]] Variant cjsonValueToVariant(TagType tag, Serializer &rdser, KeyValueType dstType); TagsMatcher &tagsMatcher_; - const FieldsSet *filter_; TagsPath tagsPath_; - Recoder *recoder_{nullptr}; int32_t arrayLevel_ = 0; ScalarIndexesSetT objectScalarIndexes_; + // storage for owning strings obtained from numbers + std::deque &storage_; }; +extern template bool CJsonDecoder::decodeCJson( + Payload &, Serializer &, WrSerializer &, CJsonDecoder::DummyFilter, CJsonDecoder::DummyRecoder, CJsonDecoder::NamelessTagOpt); +extern template bool CJsonDecoder::decodeCJson( + Payload &, Serializer &, WrSerializer &, CJsonDecoder::DummyFilter, CJsonDecoder::DefaultRecoder, CJsonDecoder::NamelessTagOpt); +extern template bool CJsonDecoder::decodeCJson( + Payload &, Serializer &, WrSerializer &, CJsonDecoder::RestrictingFilter, CJsonDecoder::DummyRecoder, CJsonDecoder::NamelessTagOpt); +extern template bool CJsonDecoder::decodeCJson( + Payload &, Serializer &, WrSerializer &, CJsonDecoder::RestrictingFilter, CJsonDecoder::DefaultRecoder, CJsonDecoder::NamelessTagOpt); + } // namespace reindexer diff --git a/cpp_src/core/cjson/cjsontools.cc b/cpp_src/core/cjson/cjsontools.cc index 0294772ff..f7d56db0b 100644 --- a/cpp_src/core/cjson/cjsontools.cc +++ b/cpp_src/core/cjson/cjsontools.cc @@ -93,15 +93,21 @@ void copyCJsonValue(TagType tagType, Serializer &rdser, WrSerializer &wrser) { } void skipCjsonTag(ctag tag, Serializer &rdser, std::array *fieldsArrayOffsets) { - const auto field = tag.Field(); - const bool embeddedField = (field < 0); switch (tag.Type()) { case TAG_ARRAY: { + const auto field = tag.Field(); + const bool embeddedField = (field < 0); if (embeddedField) { const carraytag atag = rdser.GetCArrayTag(); - for (size_t i = 0, count = atag.Count(); i < count; ++i) { - const ctag t = atag.Type() != TAG_OBJECT ? ctag{atag.Type()} : rdser.GetCTag(); - skipCjsonTag(t, rdser); + const auto count = atag.Count(); + if (atag.Type() == TAG_OBJECT) { + for (size_t i = 0; i < count; ++i) { + skipCjsonTag(rdser.GetCTag(), rdser); + } + } else { + for (size_t i = 0; i < count; ++i) { + skipCjsonTag(ctag{atag.Type()}, rdser); + } } } else { const auto len = rdser.GetVarUint(); @@ -110,7 +116,6 @@ void skipCjsonTag(ctag tag, Serializer &rdser, std::array } } } break; - case TAG_OBJECT: for (ctag otag{rdser.GetCTag()}; otag != kCTagEnd; otag = rdser.GetCTag()) { skipCjsonTag(otag, rdser, fieldsArrayOffsets); @@ -122,12 +127,15 @@ void skipCjsonTag(ctag tag, Serializer &rdser, std::array case TAG_END: case TAG_BOOL: case TAG_NULL: - case TAG_UUID: + case TAG_UUID: { + const auto field = tag.Field(); + const bool embeddedField = (field < 0); if (embeddedField) { rdser.SkipRawVariant(KeyValueType{tag.Type()}); } else if (fieldsArrayOffsets) { (*fieldsArrayOffsets)[field] += 1; } + } } } @@ -160,4 +168,13 @@ void buildPayloadTuple(const PayloadIface &pl, const TagsMatcher *tagsMatcher template void buildPayloadTuple(const PayloadIface &, const TagsMatcher *, WrSerializer &); template void buildPayloadTuple(const PayloadIface &, const TagsMatcher *, WrSerializer &); +void throwUnexpectedNestedArrayError(std::string_view parserName, const PayloadFieldType &f) { + throw Error(errLogic, "Error parsing %s field '%s' - got value nested into the array, but expected scalar %s", parserName, f.Name(), + f.Type().Name()); +} + +void throwScalarMultipleEncodesError(const Payload &pl, const PayloadFieldType &f, int field) { + throw Error(errLogic, "Non-array field '%s' [%d] from '%s' can only be encoded once.", f.Name(), field, pl.Type().Name()); +} + } // namespace reindexer diff --git a/cpp_src/core/cjson/cjsontools.h b/cpp_src/core/cjson/cjsontools.h index 69070c1e8..553280d4e 100644 --- a/cpp_src/core/cjson/cjsontools.h +++ b/cpp_src/core/cjson/cjsontools.h @@ -17,15 +17,16 @@ void putCJsonValue(TagType tagType, int tagName, const VariantArray &values, WrS void skipCjsonTag(ctag tag, Serializer &rdser, std::array *fieldsArrayOffsets = nullptr); [[nodiscard]] Variant cjsonValueToVariant(TagType tag, Serializer &rdser, KeyValueType dstType); +[[noreturn]] void throwUnexpectedNestedArrayError(std::string_view parserName, const PayloadFieldType &f); +[[noreturn]] void throwScalarMultipleEncodesError(const Payload &pl, const PayloadFieldType &f, int field); RX_ALWAYS_INLINE void validateNonArrayFieldRestrictions(const ScalarIndexesSetT &scalarIndexes, const Payload &pl, const PayloadFieldType &f, int field, bool isInArray, std::string_view parserName) { if (!f.IsArray()) { if rx_unlikely (isInArray) { - throw Error(errLogic, "Error parsing %s field '%s' - got value nested into the array, but expected scalar %s", parserName, - f.Name(), f.Type().Name()); + throwUnexpectedNestedArrayError(parserName, f); } if rx_unlikely (scalarIndexes.test(field)) { - throw Error(errLogic, "Non-array field '%s' [%d] from '%s' can only be encoded once.", f.Name(), field, pl.Type().Name()); + throwScalarMultipleEncodesError(pl, f, field); } } } diff --git a/cpp_src/core/cjson/ctag.h b/cpp_src/core/cjson/ctag.h index f752ce006..b591ad616 100644 --- a/cpp_src/core/cjson/ctag.h +++ b/cpp_src/core/cjson/ctag.h @@ -14,7 +14,6 @@ namespace reindexer { class Serializer; class WrSerializer; -} // namespace reindexer class ctag { friend class reindexer::Serializer; @@ -52,8 +51,8 @@ class ctag { [[nodiscard]] constexpr bool operator!=(ctag other) const noexcept { return !operator==(other); } private: - explicit constexpr ctag(uint32_t tag) noexcept : ctag{typeImpl(tag), nameImpl(tag), fieldImpl(tag)} { assertrx(tag == tag_); } - explicit constexpr ctag(uint64_t tag) noexcept : ctag{typeImpl(tag), nameImpl(tag), fieldImpl(tag)} { assertrx(tag == tag_); } + explicit constexpr ctag(uint32_t tag) noexcept : ctag{typeImpl(tag), nameImpl(tag), fieldImpl(tag)} { assertrx_dbg(tag == tag_); } + explicit constexpr ctag(uint64_t tag) noexcept : ctag{typeImpl(tag), nameImpl(tag), fieldImpl(tag)} { assertrx_dbg(tag == tag_); } [[nodiscard]] constexpr static TagType typeImpl(uint32_t tag) noexcept { return static_cast((tag & kTypeMask) | ((tag >> kType1Offset) & kInvertedTypeMask)); } @@ -90,7 +89,7 @@ class carraytag { [[nodiscard]] constexpr bool operator!=(carraytag other) const noexcept { return !operator==(other); } private: - explicit constexpr carraytag(uint32_t atag) noexcept : carraytag{countImpl(atag), typeImpl(atag)} { assertrx(atag == atag_); } + explicit constexpr carraytag(uint32_t atag) noexcept : carraytag{countImpl(atag), typeImpl(atag)} { assertrx_dbg(atag == atag_); } [[nodiscard]] constexpr uint32_t asNumber() const noexcept { return atag_; } [[nodiscard]] static constexpr TagType typeImpl(uint32_t atag) noexcept { return static_cast((atag >> kCountBits) & kTypeMask); @@ -99,3 +98,5 @@ class carraytag { uint32_t atag_; }; + +} // namespace reindexer diff --git a/cpp_src/core/cjson/fieldextractor.h b/cpp_src/core/cjson/fieldextractor.h index 6b11f179a..43ac72d64 100644 --- a/cpp_src/core/cjson/fieldextractor.h +++ b/cpp_src/core/cjson/fieldextractor.h @@ -16,7 +16,7 @@ class FieldsExtractor { }; FieldsExtractor() = default; - FieldsExtractor(VariantArray *va, KeyValueType expectedType, int expectedPathDepth, FieldsSet *filter = nullptr, + FieldsExtractor(VariantArray *va, KeyValueType expectedType, int expectedPathDepth, const FieldsSet *filter, FieldParams *params = nullptr) noexcept : values_(va), expectedType_(expectedType), expectedPathDepth_(expectedPathDepth), filter_(filter), params_(params) {} FieldsExtractor(FieldsExtractor &&other) = default; @@ -176,7 +176,7 @@ class FieldsExtractor { VariantArray *values_ = nullptr; KeyValueType expectedType_{KeyValueType::Undefined{}}; int expectedPathDepth_ = 0; - FieldsSet *filter_; + const FieldsSet *filter_; FieldParams *params_; }; diff --git a/cpp_src/core/cjson/jsonbuilder.cc b/cpp_src/core/cjson/jsonbuilder.cc index bb8905d7a..2c57fcb51 100644 --- a/cpp_src/core/cjson/jsonbuilder.cc +++ b/cpp_src/core/cjson/jsonbuilder.cc @@ -3,7 +3,8 @@ namespace reindexer { -JsonBuilder::JsonBuilder(WrSerializer &ser, ObjType type, const TagsMatcher *tm) : ser_(&ser), tm_(tm), type_(type) { +JsonBuilder::JsonBuilder(WrSerializer &ser, ObjType type, const TagsMatcher *tm, bool emitTrailingForFloat) + : ser_(&ser), tm_(tm), type_(type), emitTrailingForFloat_(emitTrailingForFloat) { switch (type_) { case ObjType::TypeArray: (*ser_) << '['; @@ -38,12 +39,12 @@ JsonBuilder &JsonBuilder::End() { JsonBuilder JsonBuilder::Object(std::string_view name, int /*size*/) { putName(name); - return JsonBuilder(*ser_, ObjType::TypeObject, tm_); + return JsonBuilder(*ser_, ObjType::TypeObject, tm_, emitTrailingForFloat_); } JsonBuilder JsonBuilder::Array(std::string_view name, int /*size*/) { putName(name); - return JsonBuilder(*ser_, ObjType::TypeArray, tm_); + return JsonBuilder(*ser_, ObjType::TypeArray, tm_, emitTrailingForFloat_); } void JsonBuilder::putName(std::string_view name) { diff --git a/cpp_src/core/cjson/jsonbuilder.h b/cpp_src/core/cjson/jsonbuilder.h index ccdc04b07..b46555ebe 100644 --- a/cpp_src/core/cjson/jsonbuilder.h +++ b/cpp_src/core/cjson/jsonbuilder.h @@ -10,16 +10,18 @@ namespace reindexer { class JsonBuilder { public: JsonBuilder() noexcept : ser_(nullptr), tm_(nullptr) {} - JsonBuilder(WrSerializer &ser, ObjType type = ObjType::TypeObject, const TagsMatcher *tm = nullptr); + JsonBuilder(WrSerializer &ser, ObjType type = ObjType::TypeObject, const TagsMatcher *tm = nullptr, bool emitTrailingForFloat = true); ~JsonBuilder() { End(); } JsonBuilder(const JsonBuilder &) = delete; - JsonBuilder(JsonBuilder &&other) : ser_(other.ser_), tm_(other.tm_), type_(other.type_), count_(other.count_) { + JsonBuilder(JsonBuilder &&other) noexcept + : ser_(other.ser_), tm_(other.tm_), type_(other.type_), count_(other.count_), emitTrailingForFloat_(other.emitTrailingForFloat_) { other.type_ = ObjType::TypePlain; } JsonBuilder &operator=(const JsonBuilder &) = delete; JsonBuilder &operator=(JsonBuilder &&) = delete; void SetTagsMatcher(const TagsMatcher *tm) noexcept { tm_ = tm; } + void EmitTrailingForFloat(bool val) noexcept { emitTrailingForFloat_ = val; } /// Start new object JsonBuilder Object(std::string_view name = {}, int size = KUnknownFieldSize); @@ -56,12 +58,22 @@ class JsonBuilder { JsonBuilder &Put(std::string_view name, Uuid arg, int offset = 0); JsonBuilder &Put(std::nullptr_t, std::string_view arg, int offset = 0) { return Put(std::string_view{}, arg, offset); } JsonBuilder &Put(std::string_view name, const char *arg, int offset = 0) { return Put(name, std::string_view(arg), offset); } - template ::value || std::is_floating_point::value>::type * = nullptr> + template ::value>::type * = nullptr> JsonBuilder &Put(std::string_view name, const T &arg, int /*offset*/ = 0) { putName(name); (*ser_) << arg; return *this; } + template ::value>::type * = nullptr> + JsonBuilder &Put(std::string_view name, const T &arg, int /*offset*/ = 0) { + putName(name); + if (emitTrailingForFloat_) { + (*ser_) << arg; + } else { + ser_->PutDoubleStrNoTrailing(arg); + } + return *this; + } template JsonBuilder &Put(int tagName, const T &arg, int offset = 0) { return Put(getNameByTag(tagName), arg, offset); @@ -86,6 +98,7 @@ class JsonBuilder { const TagsMatcher *tm_; ObjType type_ = ObjType::TypePlain; int count_ = 0; + bool emitTrailingForFloat_ = true; }; } // namespace reindexer diff --git a/cpp_src/core/cjson/tagsmatcher.h b/cpp_src/core/cjson/tagsmatcher.h index 22278dfeb..e36612cd4 100644 --- a/cpp_src/core/cjson/tagsmatcher.h +++ b/cpp_src/core/cjson/tagsmatcher.h @@ -25,12 +25,12 @@ class TagsMatcher { const int res = impl_->name2tag(name); return res ? res : impl_.clone()->name2tag(name, canAdd, updated_); } - int tags2field(const int16_t* path, size_t pathLen) const { return impl_->tags2field(path, pathLen); } + int tags2field(const int16_t* path, size_t pathLen) const noexcept { return impl_->tags2field(path, pathLen); } const std::string& tag2name(int tag) const { return impl_->tag2name(tag); } TagsPath path2tag(std::string_view jsonPath) const { return impl_->path2tag(jsonPath); } TagsPath path2tag(std::string_view jsonPath, bool canAdd) { - auto res = path2tag(jsonPath); if (jsonPath.empty()) return TagsPath(); + auto res = path2tag(jsonPath); return res.empty() && canAdd ? impl_.clone()->path2tag(jsonPath, canAdd, updated_) : res; } IndexedTagsPath path2indexedtag(std::string_view jsonPath, const IndexExpressionEvaluator& ev) const { diff --git a/cpp_src/core/cjson/tagsmatcherimpl.h b/cpp_src/core/cjson/tagsmatcherimpl.h index c468fbf94..b3b0f9956 100644 --- a/cpp_src/core/cjson/tagsmatcherimpl.h +++ b/cpp_src/core/cjson/tagsmatcherimpl.h @@ -4,7 +4,6 @@ #include #include -#include "core/keyvalue/key_string.h" #include "core/payload/payloadtype.h" #include "core/payload/payloadtypeimpl.h" #include "ctag.h" @@ -85,25 +84,29 @@ class TagsMatcherImpl { if (content == "*"sv) { node.MarkAllItems(true); } else { - int index = stoi(content); - if (index == 0 && content != "0"sv && ev) { - VariantArray values = ev(content); - if (values.size() != 1) { - throw Error(errParams, "Index expression has wrong syntax: '%s'", content); + auto index = try_stoi(content); + if (!index) { + if (ev) { + VariantArray values = ev(content); + if (values.size() != 1) { + throw Error(errParams, "Index expression_ has wrong syntax: '%s'", content); + } + values.front().Type().EvaluateOneOf( + [](OneOf) noexcept {}, + [&](OneOf) { + throw Error(errParams, "Wrong type of index: '%s'", content); + }); + node.SetExpression(content); + index = values.front().As(); + } else { + throw Error(errParams, "Can't convert '%s' to number", content); } - values.front().Type().EvaluateOneOf( - [](OneOf) noexcept {}, - [&](OneOf) { - throw Error(errParams, "Wrong type of index: '%s'", content); - }); - node.SetExpression(content); - index = values.front().As(); } if (index < 0) { throw Error(errLogic, "Array index value cannot be negative"); } - node.SetIndex(index); + node.SetIndex(*index); } field = field.substr(0, openBracketPos); } @@ -150,7 +153,7 @@ class TagsMatcherImpl { return tags2names_[tag - 1]; } - int tags2field(const int16_t *path, size_t pathLen) const { + int tags2field(const int16_t *path, size_t pathLen) const noexcept { if (!pathLen) return -1; return pathCache_.lookup(path, pathLen); } @@ -201,7 +204,6 @@ class TagsMatcherImpl { names2tags_.emplace(name, tag); tags2names_[tag] = name; } - version_++; // assert(ser.Eof()); } void deserialize(Serializer &ser, int version, int stateToken) { diff --git a/cpp_src/core/cjson/tagspathcache.h b/cpp_src/core/cjson/tagspathcache.h index fecd8d3de..26e0240f9 100644 --- a/cpp_src/core/cjson/tagspathcache.h +++ b/cpp_src/core/cjson/tagspathcache.h @@ -31,7 +31,7 @@ class TagsPathCache { len--; } } - int lookup(const int16_t *tagsPath, size_t len) const { + int lookup(const int16_t *tagsPath, size_t len) const noexcept { assertrx(len); auto cache = this; for (;;) { @@ -51,7 +51,7 @@ class TagsPathCache { } } - void walk(int16_t *path, int depth, const std::function& visitor) const { + void walk(int16_t *path, int depth, const std::function &visitor) const { int16_t &i = path[depth]; for (i = 0; i < int(entries_.size()); i++) { if (entries_[i].field_ > 0) visitor(depth + 1, entries_[i].field_); diff --git a/cpp_src/core/clusterproxy.cc b/cpp_src/core/clusterproxy.cc index ed7035dfd..04d6fccb0 100644 --- a/cpp_src/core/clusterproxy.cc +++ b/cpp_src/core/clusterproxy.cc @@ -15,37 +15,46 @@ using namespace std::string_view_literals; void ClusterProxy::clientToCoreQueryResults(client::QueryResults &clientResults, LocalQueryResults &result) { QueryResults qr; if (clientResults.HaveJoined()) { - throw Error(errLogic, "JOIN queries are not supported bu Cluster Proxy"); + throw Error(errLogic, "JOIN queries are not supported by Cluster Proxy"); } if (clientResults.GetMergedNSCount() > 1) { - throw Error(errLogic, "MERGE queries are not supported bu Cluster Proxy"); + throw Error(errLogic, "MERGE queries are not supported by Cluster Proxy"); } if (result.getMergedNSCount() != 0 || result.totalCount != 0) { - throw Error(errLogic, "Query results merging is not supported bu Cluster Proxy"); + throw Error(errLogic, "Query results merging is not supported by Cluster Proxy"); } - for (int i = 0; i < clientResults.GetMergedNSCount(); ++i) { - result.addNSContext(clientResults.GetPayloadType(i), clientResults.GetTagsMatcher(i), FieldsSet(), nullptr); - } - if (clientResults.GetExplainResults().size() > 0) { - result.explainResults = clientResults.GetExplainResults(); + const auto itemsCnt = clientResults.Count(); + if (clientResults.GetMergedNSCount() > 0) { + auto &incTags = clientResults.GetIncarnationTags()[0].tags; + if (itemsCnt || incTags.size()) { + result.addNSContext(clientResults.GetPayloadType(0), clientResults.GetTagsMatcher(0), FieldsSet(), nullptr, + incTags.size() ? incTags[0] : lsn_t()); + } } - if (clientResults.GetAggregationResults().size() > 0) { - result.aggregationResults = clientResults.GetAggregationResults(); + result.explainResults = clientResults.GetExplainResults(); + result.aggregationResults = clientResults.GetAggregationResults(); + result.totalCount = clientResults.TotalCount(); + if (!itemsCnt) { + return; } - for (auto it = clientResults.begin(); it != clientResults.end(); ++it) { + RdxContext dummyCtx; + auto &localPt = result.getPayloadType(0); + auto &localTm = result.getTagsMatcher(0); + auto cNamespaces = clientResults.GetNamespaces(); + for (auto it = clientResults.begin(), itEnd = clientResults.end(); it != itEnd; ++it) { auto item = it.GetItem(); if (!item.Status().ok()) { throw item.Status(); } if (!item) { - Item itemServer = impl_.NewItem(clientResults.GetNamespaces()[0], RdxContext()); + Item itemServer = impl_.NewItem(cNamespaces[0], dummyCtx); result.AddItem(itemServer); continue; } - ItemImpl itemimpl(result.getPayloadType(0), result.getTagsMatcher(0)); + ItemImpl itemimpl(localPt, localTm); Error err = itemimpl.FromJSON(item.GetJSON()); if (!err.ok()) { throw err; @@ -55,13 +64,12 @@ void ClusterProxy::clientToCoreQueryResults(client::QueryResults &clientResults, WrSerializer wrser; itemimpl.tagsMatcher().serialize(wrser); Serializer ser(wrser.Slice()); - result.getTagsMatcher(0).deserialize(ser, itemimpl.tagsMatcher().version(), itemimpl.tagsMatcher().stateToken()); + localTm.deserialize(ser, itemimpl.tagsMatcher().version(), itemimpl.tagsMatcher().stateToken()); } itemimpl.Value().SetLSN(item.GetLSN()); result.Add(ItemRef(it.itemParams_.id, itemimpl.Value(), it.itemParams_.proc, it.itemParams_.nsid, true)); result.SaveRawData(std::move(itemimpl)); } - result.totalCount = clientResults.TotalCount(); } template <> @@ -96,7 +104,7 @@ std::shared_ptr ClusterProxy::getLeader(const cluster::RaftIn leader_.reset(); leaderId_ = -1; std::string leaderDsn; - Error err = impl_.GetLeaderDsn(leaderDsn, getServerID(), info); + Error err = impl_.GetLeaderDsn(leaderDsn, GetServerID(), info); if (!err.ok()) { throw err; } @@ -143,31 +151,31 @@ Error ClusterProxy::Connect(const std::string &dsn, ConnectOpts opts) { bool ClusterProxy::shouldProxyQuery(const Query &q) { assertrx(q.Type() == QuerySelect); - if (kReplicationStatsNamespace != q._namespace) { + if (kReplicationStatsNamespace != q.NsName()) { return false; } if (q.HasLimit()) { return false; } - if (q.joinQueries_.size() || q.mergeQueries_.size()) { - throw Error(errParams, "Joins and merges are not allowed for #replicationstats queries"); + if (q.GetJoinQueries().size() || q.GetMergeQueries().size() || q.GetSubQueries().size()) { + throw Error(errParams, "Joins, merges and subqueries are not allowed for #replicationstats queries"); } bool hasTypeCond = false; bool isAsyncReplQuery = false; bool isClusterReplQuery = false; constexpr auto kConditionError = "Query to #replicationstats has to contain one of the following conditions: type='async' or type='cluster'"sv; - for (auto it = q.entries.cbegin(), end = q.entries.cend(); it != end; ++it) { - if (it->HoldsOrReferTo() && it->Value().index == "type"sv) { + for (auto it = q.Entries().cbegin(), end = q.Entries().cend(); it != end; ++it) { + if (it->Is() && it->Value().FieldName() == "type"sv) { auto nextIt = it; ++nextIt; auto &entry = it->Value(); - if (hasTypeCond || entry.condition != CondEq || entry.values.size() != 1 || - !entry.values[0].Type().Is() || it->operation != OpAnd || + if (hasTypeCond || entry.Condition() != CondEq || entry.Values().size() != 1 || + !entry.Values()[0].Type().Is() || it->operation != OpAnd || (nextIt != end && nextIt->operation == OpOr)) { throw Error(errParams, kConditionError); } - auto str = entry.values[0].As(); + auto str = entry.Values()[0].As(); if (str == cluster::kAsyncReplStatsType) { isAsyncReplQuery = true; } else if (str == cluster::kClusterReplStatsType) { @@ -187,7 +195,7 @@ bool ClusterProxy::shouldProxyQuery(const Query &q) { return isClusterReplQuery; } -[[nodiscard]] Error ClusterProxy::ResetShardingConfig(std::optional config) noexcept { +Error ClusterProxy::ResetShardingConfig(std::optional config) noexcept { try { impl_.shardingConfig_.Set(std::move(config)); return impl_.tryLoadShardingConf(); diff --git a/cpp_src/core/clusterproxy.h b/cpp_src/core/clusterproxy.h index 99e4a9380..927893181 100644 --- a/cpp_src/core/clusterproxy.h +++ b/cpp_src/core/clusterproxy.h @@ -5,7 +5,7 @@ #include "client/reindexer.h" #include "cluster/config.h" #include "cluster/consts.h" -#include "core/reindexerimpl.h" +#include "core/reindexer_impl/reindexerimpl.h" #include "tools/clusterproxyloghelper.h" namespace reindexer { @@ -124,7 +124,7 @@ class ClusterProxy { return resultFollowerAction<&client::Reindexer::Update>(ctx, clientToLeader, q, qr); }; clusterProxyLog(LogTrace, "[%d proxy] ClusterProxy::Update query", getServerIDRel()); - return proxyCall(ctx, q._namespace, action, q, qr); + return proxyCall(ctx, q.NsName(), action, q, qr); } Error Upsert(std::string_view nsName, Item &item, const RdxContext &ctx) { auto action = [this](const RdxContext &ctx, LeaderRefT clientToLeader, std::string_view nsName, Item &item) { @@ -156,7 +156,7 @@ class ClusterProxy { return resultFollowerAction<&client::Reindexer::Delete>(ctx, clientToLeader, q, qr); }; clusterProxyLog(LogTrace, "[%d proxy] ClusterProxy::Delete QUERY", getServerIDRel()); - return proxyCall(ctx, q._namespace, action, q, qr); + return proxyCall(ctx, q.NsName(), action, q, qr); } Error Select(const Query &q, LocalQueryResults &qr, const RdxContext &ctx) { using namespace std::placeholders; @@ -171,7 +171,7 @@ class ClusterProxy { return resultFollowerAction<&client::Reindexer::Select>(ctx, clientToLeader, q, qr); }; clusterProxyLog(LogTrace, "[%d proxy] ClusterProxy::Select query proxied", getServerIDRel()); - return proxyCall(rdxDeadlineCtx, q._namespace, action, q, qr); + return proxyCall(rdxDeadlineCtx, q.NsName(), action, q, qr); } Error Commit(std::string_view nsName) { return impl_.Commit(nsName); } Item NewItem(std::string_view nsName, const RdxContext &ctx) { return impl_.NewItem(nsName, ctx); } @@ -180,7 +180,7 @@ class ClusterProxy { using LocalFT = LocalTransaction (ReindexerImpl::*)(std::string_view, const RdxContext &); auto action = [this](const RdxContext &ctx, LeaderRefT clientToLeader, std::string_view nsName) { try { - client::Reindexer l = clientToLeader->WithLSN(ctx.GetOriginLSN()).WithEmmiterServerId(getServerID()); + client::Reindexer l = clientToLeader->WithLSN(ctx.GetOriginLSN()).WithEmmiterServerId(GetServerID()); return Transaction(impl_.NewTransaction(nsName, ctx), std::move(l)); } catch (const Error &err) { return Transaction(err); @@ -190,9 +190,9 @@ class ClusterProxy { return proxyCall(ctx, nsName, action, nsName); } Error CommitTransaction(Transaction &tr, QueryResults &qr, bool txExpectsSharding, const RdxContext &ctx) { - return tr.commit(getServerID(), txExpectsSharding, impl_, qr, ctx); + return tr.commit(GetServerID(), txExpectsSharding, impl_, qr, ctx); } - Error RollBackTransaction(Transaction &tr, const RdxContext &ctx) { return tr.rollback(getServerID(), ctx); } + Error RollBackTransaction(Transaction &tr, const RdxContext &ctx) { return tr.rollback(GetServerID(), ctx); } Error GetMeta(std::string_view nsName, const std::string &key, std::string &data, const RdxContext &ctx) { return impl_.GetMeta(nsName, key, data, ctx); @@ -219,9 +219,6 @@ class ClusterProxy { return impl_.SetClusterizationStatus(nsName, status, ctx); } bool NeedTraceActivity() const noexcept { return impl_.NeedTraceActivity(); } - Error EnableStorage(const std::string &storagePath, bool skipPlaceholderCheck, const RdxContext &ctx) { - return impl_.EnableStorage(storagePath, skipPlaceholderCheck, ctx); - } Error InitSystemNamespaces() { return impl_.InitSystemNamespaces(); } Error ApplySnapshotChunk(std::string_view nsName, const SnapshotChunk &ch, const RdxContext &ctx) { return impl_.ApplySnapshotChunk(nsName, ch, ctx); @@ -279,6 +276,13 @@ class ClusterProxy { [[nodiscard]] Error ResetShardingConfigCandidate(int64_t sourceId, const RdxContext &ctx) noexcept; [[nodiscard]] Error RollbackShardingConfigCandidate(int64_t sourceId, const RdxContext &ctx) noexcept; + Error SubscribeUpdates(IUpdatesObserver *observer, const UpdatesFilters &filters, SubscriptionOpts opts) { + return impl_.SubscribeUpdates(observer, filters, opts); + } + Error UnsubscribeUpdates(IUpdatesObserver *observer) { return impl_.UnsubscribeUpdates(observer); } + // REINDEX_WITH_V3_FOLLOWERS + int GetServerID() const noexcept { return sId_.load(std::memory_order_acquire); } + private: static constexpr auto kReplicationStatsTimeout = std::chrono::seconds(10); static constexpr uint32_t kMaxClusterProxyConnCount = 64; @@ -367,7 +371,6 @@ class ClusterProxy { std::mutex processPingEventMutex_; int lastPingLeaderId_ = -1; - int getServerID() const noexcept { return sId_.load(std::memory_order_acquire); } int getServerIDRel() const noexcept { return sId_.load(std::memory_order_relaxed); } std::shared_ptr getLeader(const cluster::RaftInfo &info); void resetLeader(); @@ -564,8 +567,8 @@ class ClusterProxy { if (!err.ok()) { return err; } - if (!query.joinQueries_.empty() || !query.mergeQueries_.empty()) { - return Error(errLogic, "Unable to proxy query with JOIN or MERGE"); + if (!query.GetJoinQueries().empty() || !query.GetMergeQueries().empty() || !query.GetSubQueries().empty()) { + return Error(errLogic, "Unable to proxy query with JOIN, MERGE or SUBQUERY"); } clientToCoreQueryResults(clientResults, qr); return err; diff --git a/cpp_src/core/comparator.cc b/cpp_src/core/comparator.cc index 08d001339..c105f53f2 100644 --- a/cpp_src/core/comparator.cc +++ b/cpp_src/core/comparator.cc @@ -82,16 +82,11 @@ void Comparator::Bind(const PayloadType &type, int field) { } } -void Comparator::BindEqualPosition(int field, const VariantArray &val, CondType cond) { cmpEqualPosition.BindField(field, val, cond); } - -void Comparator::BindEqualPosition(const TagsPath &tagsPath, const VariantArray &val, CondType cond) { - cmpEqualPosition.BindField(tagsPath, val, cond); -} - bool Comparator::isNumericComparison(const VariantArray &values) const { if (valuesType_.Is() || values.empty()) return false; const KeyValueType keyType{values.front().Type()}; - return !valuesType_.IsSame(keyType) && (valuesType_.Is() || keyType.Is()); + return !valuesType_.IsSame(keyType) && ((valuesType_.Is() && !keyType.Is()) || + (keyType.Is() && !valuesType_.Is())); } bool Comparator::Compare(const PayloadValue &data, int rowId) { @@ -191,7 +186,7 @@ void Comparator::ExcludeDistinct(const PayloadValue &data, int rowId) { } else { // Exclude field from payload by offset (fast path) - assertrx(!type_.Is()); + assertrx_throw(!type_.Is()); // Check if we have column (rawData_), then go to fastest path with column if (rawData_) return excludeDistinct(rawData_ + rowId * sizeof_); diff --git a/cpp_src/core/comparator.h b/cpp_src/core/comparator.h index 88ba5b6ba..20d1e9b57 100644 --- a/cpp_src/core/comparator.h +++ b/cpp_src/core/comparator.h @@ -17,8 +17,10 @@ class Comparator : public ComparatorVars { bool Compare(const PayloadValue &lhs, int rowId); void ExcludeDistinct(const PayloadValue &, int rowId); void Bind(const PayloadType &type, int field); - void BindEqualPosition(int field, const VariantArray &val, CondType cond); - void BindEqualPosition(const TagsPath &tagsPath, const VariantArray &val, CondType cond); + template + void BindEqualPosition(F &&field, const VariantArray &val, CondType cond) { + cmpEqualPosition.BindField(std::forward(field), val, cond); + } void ClearDistinct() { cmpInt.ClearDistinct(); cmpBool.ClearDistinct(); @@ -118,7 +120,7 @@ class Comparator : public ComparatorVars { ComparatorImpl cmpComposite; ComparatorImpl cmpGeom; ComparatorImpl cmpUuid; - CompositeArrayComparator cmpEqualPosition; + EqualPositionComparator cmpEqualPosition; KeyValueType valuesType_{KeyValueType::Undefined{}}; }; diff --git a/cpp_src/core/comparatorimpl.h b/cpp_src/core/comparatorimpl.h index 30868e678..d552baba4 100644 --- a/cpp_src/core/comparatorimpl.h +++ b/cpp_src/core/comparatorimpl.h @@ -349,10 +349,12 @@ class ComparatorImpl { void SetValues(CondType cond, const VariantArray &values, const ComparatorVars &vars) { if (cond == CondSet) { valuesSet_.reset(new intrusive_atomic_rc_wrapper( - values.size(), hash_composite(vars.payloadType_, vars.fields_), equal_composite(vars.payloadType_, vars.fields_))); + values.size(), hash_composite(PayloadType{vars.payloadType_}, FieldsSet{vars.fields_}), + equal_composite(PayloadType{vars.payloadType_}, FieldsSet{vars.fields_}))); } else if (cond == CondAllSet) { valuesSet_.reset(new intrusive_atomic_rc_wrapper( - values.size(), hash_composite(vars.payloadType_, vars.fields_), equal_composite(vars.payloadType_, vars.fields_))); + values.size(), hash_composite(PayloadType{vars.payloadType_}, FieldsSet{vars.fields_}), + equal_composite(PayloadType{vars.payloadType_}, FieldsSet{vars.fields_}))); allSetValuesSet_.reset(new intrusive_atomic_rc_wrapper>{}); } diff --git a/cpp_src/core/compositearraycomparator.cc b/cpp_src/core/compositearraycomparator.cc index 6e58fdf98..4ffb48e83 100644 --- a/cpp_src/core/compositearraycomparator.cc +++ b/cpp_src/core/compositearraycomparator.cc @@ -2,37 +2,29 @@ #include namespace reindexer { -CompositeArrayComparator::CompositeArrayComparator() {} +void EqualPositionComparator::BindField(int field, const VariantArray &values, CondType cond) { bindField(field, values, cond); } -void CompositeArrayComparator::BindField(int field, const VariantArray &values, CondType condType) { +void EqualPositionComparator::BindField(const FieldsPath &fieldPath, const VariantArray &values, CondType cond) { + bindField(fieldPath, values, cond); +} + +template +void EqualPositionComparator::bindField(F field, const VariantArray &values, CondType cond) { fields_.push_back(field); Context &ctx = ctx_.emplace_back(); - ctx.cond = condType; - ctx.cmpBool.SetValues(condType, values); - ctx.cmpInt.SetValues(condType, values); - ctx.cmpInt64.SetValues(condType, values); - ctx.cmpString.SetValues(condType, values, CollateOpts()); - ctx.cmpDouble.SetValues(condType, values); - ctx.cmpUuid.SetValues(condType, values); + ctx.cond = cond; + ctx.cmpBool.SetValues(cond, values); + ctx.cmpInt.SetValues(cond, values); + ctx.cmpInt64.SetValues(cond, values); + ctx.cmpString.SetValues(cond, values, CollateOpts()); + ctx.cmpDouble.SetValues(cond, values); + ctx.cmpUuid.SetValues(cond, values); assertrx(ctx_.size() == fields_.size()); } -void CompositeArrayComparator::BindField(const TagsPath &tagsPath, const VariantArray &values, CondType condType) { - fields_.push_back(tagsPath); - Context &ctx = ctx_.emplace_back(); - - ctx.cond = condType; - ctx.cmpBool.SetValues(condType, values); - ctx.cmpInt.SetValues(condType, values); - ctx.cmpInt64.SetValues(condType, values); - ctx.cmpString.SetValues(condType, values, CollateOpts()); - ctx.cmpDouble.SetValues(condType, values); - ctx.cmpUuid.SetValues(condType, values); -} - -bool CompositeArrayComparator::Compare(const PayloadValue &pv, const ComparatorVars &vars) { +bool EqualPositionComparator::Compare(const PayloadValue &pv, const ComparatorVars &vars) { ConstPayload pl(vars.payloadType_, pv); size_t len = INT_MAX; @@ -64,7 +56,7 @@ bool CompositeArrayComparator::Compare(const PayloadValue &pv, const ComparatorV return false; } -bool CompositeArrayComparator::compareField(size_t field, const Variant &v, const ComparatorVars &vars) { +bool EqualPositionComparator::compareField(size_t field, const Variant &v, const ComparatorVars &vars) { return v.Type().EvaluateOneOf( [&](KeyValueType::Bool) { return ctx_[field].cmpBool.Compare(ctx_[field].cond, static_cast(v)); }, [&](KeyValueType::Int) { return ctx_[field].cmpInt.Compare(ctx_[field].cond, static_cast(v)); }, diff --git a/cpp_src/core/compositearraycomparator.h b/cpp_src/core/compositearraycomparator.h index d646ed6b4..a58735dc6 100644 --- a/cpp_src/core/compositearraycomparator.h +++ b/cpp_src/core/compositearraycomparator.h @@ -5,17 +5,19 @@ namespace reindexer { -class CompositeArrayComparator { +class EqualPositionComparator { public: - CompositeArrayComparator(); + EqualPositionComparator() noexcept = default; - void BindField(int field, const VariantArray &values, CondType condType); - void BindField(const TagsPath &tagsPath, const VariantArray &values, CondType condType); - bool Compare(const PayloadValue &pv, const ComparatorVars &vars); + void BindField(int field, const VariantArray &, CondType); + void BindField(const FieldsPath &, const VariantArray &, CondType); + bool Compare(const PayloadValue &, const ComparatorVars &); bool IsBinded() { return !ctx_.empty(); } private: - bool compareField(size_t field, const Variant &v, const ComparatorVars &vars); + bool compareField(size_t field, const Variant &, const ComparatorVars &); + template + void bindField(F field, const VariantArray &, CondType); struct Context { CondType cond; diff --git a/cpp_src/core/dbconfig.cc b/cpp_src/core/dbconfig.cc index 03485a8cf..291d43976 100644 --- a/cpp_src/core/dbconfig.cc +++ b/cpp_src/core/dbconfig.cc @@ -1,6 +1,7 @@ #include "dbconfig.h" #include +#include #include #include #include @@ -28,7 +29,7 @@ static CacheMode str2cacheMode(std::string_view mode) { } Error DBConfigProvider::FromJSON(const gason::JsonNode &root, bool autoCorrect) { - std::set typesChanged; + std::bitset typesChanged; std::string errLogString; try { @@ -40,13 +41,13 @@ Error DBConfigProvider::FromJSON(const gason::JsonNode &root, bool autoCorrect) profilingDataLoadResult_ = profilingDataSafe.FromJSON(profilingNode); if (profilingDataLoadResult_.ok()) { - typesChanged.emplace(ProfilingConf); + typesChanged.set(ProfilingConf); } else { errLogString += profilingDataLoadResult_.what(); } } - std::unordered_map namespacesData; + fast_hash_map namespacesData; auto &namespacesNode = root["namespaces"]; if (!namespacesNode.empty()) { std::string namespacesErrLogString; @@ -72,7 +73,7 @@ Error DBConfigProvider::FromJSON(const gason::JsonNode &root, bool autoCorrect) if (!nssHaveErrors) { namespacesDataLoadResult_ = errOK; - typesChanged.emplace(NamespaceDataConf); + typesChanged.set(NamespaceDataConf); } else { namespacesDataLoadResult_ = Error(errParseJson, namespacesErrLogString); ensureEndsWith(errLogString, "\n") += "NamespacesConfig: JSON parsing error: " + namespacesErrLogString; @@ -85,7 +86,7 @@ Error DBConfigProvider::FromJSON(const gason::JsonNode &root, bool autoCorrect) asyncReplicationDataLoadResult_ = asyncReplConfigDataSafe.FromJSON(asyncReplicationNode); if (asyncReplicationDataLoadResult_.ok()) { - typesChanged.emplace(AsyncReplicationConf); + typesChanged.set(AsyncReplicationConf); } else { ensureEndsWith(errLogString, "\n") += asyncReplicationDataLoadResult_.what(); } @@ -98,28 +99,28 @@ Error DBConfigProvider::FromJSON(const gason::JsonNode &root, bool autoCorrect) replicationDataLoadResult_ = replicationDataSafe.FromJSON(replicationNode); if (replicationDataLoadResult_.ok()) { - typesChanged.emplace(ReplicationConf); + typesChanged.set(ReplicationConf); } else { ensureEndsWith(errLogString, "\n") += replicationDataLoadResult_.what(); } } else { replicationDataSafe.FromJSONWithCorrection(replicationNode, replicationDataLoadResult_); - typesChanged.emplace(ReplicationConf); + typesChanged.set(ReplicationConf); } } // Applying entire configuration only if no read errors if (errLogString.empty()) { - if (typesChanged.find(ProfilingConf) != typesChanged.end()) { + if (typesChanged.test(ProfilingConf)) { profilingData_ = profilingDataSafe; } - if (typesChanged.find(NamespaceDataConf) != typesChanged.end()) { + if (typesChanged.test(NamespaceDataConf)) { namespacesData_ = std::move(namespacesData); } - if (typesChanged.find(AsyncReplicationConf) != typesChanged.end()) { + if (typesChanged.test(AsyncReplicationConf)) { asyncReplicationData_ = std::move(asyncReplConfigDataSafe); } - if (typesChanged.find(ReplicationConf) != typesChanged.end()) { + if (typesChanged.test(ReplicationConf)) { replicationData_ = std::move(replicationDataSafe); } } @@ -137,11 +138,13 @@ Error DBConfigProvider::FromJSON(const gason::JsonNode &root, bool autoCorrect) // notifying handlers under shared_lock so none of them go out of scope smart_lock lk(mtx_, false); - for (auto changedType : typesChanged) { - auto it = handlers_.find(changedType); - if (it != handlers_.end()) { + for (unsigned changedType = 0; changedType < typesChanged.size(); ++changedType) { + if (!typesChanged.test(changedType)) { + continue; + } + if (handlers_[changedType]) { try { - (it->second)(); + handlers_[changedType](); } catch (const Error &err) { logPrintf(LogError, "DBConfigProvider: Error processing event handler: '%s'", err.what()); } @@ -186,9 +189,8 @@ void DBConfigProvider::setHandler(ConfigType cfgType, std::function hand int DBConfigProvider::setHandler(std::function handler) { smart_lock lk(mtx_, true); - HandlersCounter_++; - replicationConfigDataHandlers_[HandlersCounter_] = std::move(handler); - return HandlersCounter_; + replicationConfigDataHandlers_[++handlersCounter_] = std::move(handler); + return handlersCounter_; } void DBConfigProvider::unsetHandler(int id) { @@ -206,11 +208,11 @@ cluster::AsyncReplConfigData DBConfigProvider::GetAsyncReplicationConfig() { return asyncReplicationData_; } -bool DBConfigProvider::GetNamespaceConfig(const std::string &nsName, NamespaceConfigData &data) { +bool DBConfigProvider::GetNamespaceConfig(std::string_view nsName, NamespaceConfigData &data) { shared_lock lk(mtx_); auto it = namespacesData_.find(nsName); if (it == namespacesData_.end()) { - it = namespacesData_.find("*"); + it = namespacesData_.find(std::string_view("*")); } if (it == namespacesData_.end()) { data = {}; @@ -367,7 +369,7 @@ Error NamespaceConfigData::FromJSON(const gason::JsonNode &v) { tryReadOptionalJsonValue(&errorString, v, "lazyload"sv, lazyLoad); tryReadOptionalJsonValue(&errorString, v, "unload_idle_threshold"sv, noQueryIdleThreshold); - std::string stringVal = logLevelToString(logLevel); + std::string stringVal(logLevelToString(logLevel)); if (tryReadOptionalJsonValue(&errorString, v, "log_level"sv, stringVal).ok()) { logLevel = logLevelFromString(stringVal); } @@ -403,6 +405,20 @@ Error NamespaceConfigData::FromJSON(const gason::JsonNode &v) { tryReadOptionalJsonValue(&errorString, v, "index_updates_counting_mode"sv, idxUpdatesCountingMode); tryReadOptionalJsonValue(&errorString, v, "sync_storage_flush_limit"sv, syncStorageFlushLimit, 0); + auto cacheNode = v["cache"]; + if (!cacheNode.empty()) { + tryReadOptionalJsonValue(&errorString, cacheNode, "index_idset_cache_size"sv, cacheConfig.idxIdsetCacheSize, 0); + tryReadOptionalJsonValue(&errorString, cacheNode, "index_idset_hits_to_cache"sv, cacheConfig.idxIdsetHitsToCache, 0); + tryReadOptionalJsonValue(&errorString, cacheNode, "ft_index_cache_size"sv, cacheConfig.ftIdxCacheSize, 0); + tryReadOptionalJsonValue(&errorString, cacheNode, "ft_index_hits_to_cache"sv, cacheConfig.ftIdxHitsToCache, 0); + tryReadOptionalJsonValue(&errorString, cacheNode, "joins_preselect_cache_size"sv, cacheConfig.joinCacheSize, 0); + tryReadOptionalJsonValue(&errorString, cacheNode, "joins_preselect_hit_to_cache"sv, cacheConfig.joinHitsToCache, 0); + tryReadOptionalJsonValue(&errorString, cacheNode, "query_count_cache_size"sv, cacheConfig.queryCountCacheSize, 0); + tryReadOptionalJsonValue(&errorString, cacheNode, "query_count_hit_to_cache"sv, cacheConfig.queryCountHitsToCache, 0); + tryReadOptionalJsonValue(&errorString, cacheNode, "index_idset_cache_size"sv, cacheConfig.idxIdsetCacheSize, 0); + tryReadOptionalJsonValue(&errorString, cacheNode, "index_idset_cache_size"sv, cacheConfig.idxIdsetCacheSize, 0); + } + if (!errorString.empty()) { return Error(errParseJson, "NamespaceConfigData: JSON parsing error: '%s'", errorString); } diff --git a/cpp_src/core/dbconfig.h b/cpp_src/core/dbconfig.h index 4de3c7d57..f6f4d04b0 100644 --- a/cpp_src/core/dbconfig.h +++ b/cpp_src/core/dbconfig.h @@ -3,10 +3,9 @@ #include #include #include -#include #include #include "cluster/config.h" -#include "estl/fast_hash_set.h" +#include "estl/fast_hash_map.h" #include "estl/mutex.h" #include "estl/shared_mutex.h" #include "tools/errors.h" @@ -21,7 +20,14 @@ class JsonBuilder; class RdxContext; class WrSerializer; -enum ConfigType { ProfilingConf, NamespaceDataConf, AsyncReplicationConf, ReplicationConf }; +enum ConfigType { + ProfilingConf = 0, + NamespaceDataConf, + AsyncReplicationConf, + ReplicationConf, + // + kConfigTypesTotalCount +}; class LongQueriesLoggingParams { public: @@ -70,6 +76,31 @@ class ProfilingConfigData { std::atomic longTxLoggingParams; }; +constexpr size_t kDefaultCacheSizeLimit = 1024 * 1024 * 128; +constexpr uint32_t kDefaultHitCountToCache = 2; + +struct NamespaceCacheConfigData { + bool IsIndexesCacheEqual(const NamespaceCacheConfigData &o) noexcept { + return idxIdsetCacheSize == o.idxIdsetCacheSize && idxIdsetHitsToCache == o.idxIdsetHitsToCache && + ftIdxCacheSize == o.ftIdxCacheSize && ftIdxHitsToCache == o.ftIdxHitsToCache; + } + bool IsJoinCacheEqual(const NamespaceCacheConfigData &o) noexcept { + return joinCacheSize == o.joinCacheSize && joinHitsToCache == o.joinHitsToCache; + } + bool IsQueryCountCacheEqual(const NamespaceCacheConfigData &o) noexcept { + return queryCountCacheSize == o.queryCountCacheSize && queryCountHitsToCache == o.queryCountHitsToCache; + } + + uint64_t idxIdsetCacheSize = kDefaultCacheSizeLimit; + uint32_t idxIdsetHitsToCache = kDefaultHitCountToCache; + uint64_t ftIdxCacheSize = kDefaultCacheSizeLimit; + uint32_t ftIdxHitsToCache = kDefaultHitCountToCache; + uint64_t joinCacheSize = 2 * kDefaultCacheSizeLimit; + uint32_t joinHitsToCache = kDefaultHitCountToCache; + uint64_t queryCountCacheSize = kDefaultCacheSizeLimit; + uint32_t queryCountHitsToCache = kDefaultHitCountToCache; +}; + struct NamespaceConfigData { bool lazyLoad = false; int noQueryIdleThreshold = 0; @@ -86,7 +117,8 @@ struct NamespaceConfigData { int64_t maxPreselectSize = 1000; double maxPreselectPart = 0.1; bool idxUpdatesCountingMode = false; - int syncStorageFlushLimit = 25000; + int syncStorageFlushLimit = 20000; + NamespaceCacheConfigData cacheConfig; Error FromJSON(const gason::JsonNode &v); }; @@ -147,7 +179,7 @@ class DBConfigProvider { cluster::AsyncReplConfigData GetAsyncReplicationConfig(); ReplicationConfigData GetReplicationConfig(); - bool GetNamespaceConfig(const std::string &nsName, NamespaceConfigData &data); + bool GetNamespaceConfig(std::string_view nsName, NamespaceConfigData &data); LongQueriesLoggingParams GetSelectLoggingParams() const noexcept { return profilingData_.longSelectLoggingParams.load(std::memory_order_relaxed); } @@ -169,10 +201,10 @@ class DBConfigProvider { Error namespacesDataLoadResult_; Error asyncReplicationDataLoadResult_; Error replicationDataLoadResult_; - std::unordered_map namespacesData_; - std::unordered_map> handlers_; - std::unordered_map> replicationConfigDataHandlers_; - int HandlersCounter_ = 0; + fast_hash_map namespacesData_; + std::array, kConfigTypesTotalCount> handlers_; + fast_hash_map> replicationConfigDataHandlers_; + int handlersCounter_ = 0; mutable shared_timed_mutex mtx_; }; diff --git a/cpp_src/core/defnsconfigs.h b/cpp_src/core/defnsconfigs.h index 7f66401be..d73c190b6 100644 --- a/cpp_src/core/defnsconfigs.h +++ b/cpp_src/core/defnsconfigs.h @@ -59,7 +59,17 @@ const std::vector kDefDBConfig = { "max_preselect_size":1000, "max_preselect_part":0.1, "index_updates_counting_mode":false, - "sync_storage_flush_limit":25000 + "sync_storage_flush_limit":20000, + "cache":{ + "index_idset_cache_size":134217728, + "index_idset_hits_to_cache":2, + "ft_index_cache_size":134217728, + "ft_index_hits_to_cache":2, + "joins_preselect_cache_size":268435456, + "joins_preselect_hit_to_cache":2, + "query_count_cache_size":134217728, + "query_count_hit_to_cache":2 + } } ] })json", @@ -132,7 +142,6 @@ const std::vector kSystemNsDefs = { .AddIndex("last_sec_avg_lock_time_us", "-", "int64", IndexOpts().Dense()) .AddIndex("latency_stddev", "-", "double", IndexOpts().Dense()), NamespaceDef(kNamespacesNamespace, StorageOpts()).AddIndex(kNsNameField, "hash", "string", IndexOpts().PK()), - NamespaceDef(kPerfStatsNamespace, StorageOpts()).AddIndex(kNsNameField, "hash", "string", IndexOpts().PK()), NamespaceDef(kMemStatsNamespace, StorageOpts()) .AddIndex(kNsNameField, "hash", "string", IndexOpts().PK()) .AddIndex("items_count", "-", "int64", IndexOpts().Dense()) diff --git a/cpp_src/core/expressiontree.h b/cpp_src/core/expressiontree.h index 1a16eab63..a29659f0f 100644 --- a/cpp_src/core/expressiontree.h +++ b/cpp_src/core/expressiontree.h @@ -18,7 +18,7 @@ class Bracket { void Append() noexcept { ++size_; } /// Decrease space occupied by children void Erase(size_t length) noexcept { - assertrx(size_ > length); + assertrx_throw(size_ > length); size_ -= length; } void CopyPayloadFrom(const Bracket&) const noexcept {} @@ -37,18 +37,6 @@ struct Skip {}; /// For detailed documentation see expressiontree.md template class ExpressionTree { - template - class Ref { - public: - explicit Ref(T& v) noexcept : ptr_{&v} {} - operator T&() noexcept { return *ptr_; } - operator const T&() const noexcept { return *ptr_; } - bool operator==(const Ref& other) const noexcept(noexcept(std::declval() == std::declval())) { return *ptr_ == *other.ptr_; } - - private: - T* ptr_; - }; - template struct OverloadResolutionHelper : private OverloadResolutionHelper { constexpr static T resolve(std::function) noexcept; @@ -77,11 +65,6 @@ class ExpressionTree { R operator()(Arg&& arg) const noexcept(noexcept(std::declval()(std::forward(arg)))) { return functor_(std::forward(arg)); } - R operator()(const Ref& arg) const noexcept(noexcept(std::declval()(arg))) { return functor_(arg); } - R operator()(Ref& arg) const noexcept(noexcept(std::declval()(arg))) { return functor_(arg); } - R operator()(Ref&& arg) const noexcept(noexcept(std::declval()(std::forward>(arg)))) { - return functor_(std::forward>(arg)); - } private: F functor_; @@ -102,18 +85,6 @@ class ExpressionTree { R operator()(Arg&& arg) const noexcept(noexcept(std::declval()(std::forward(arg)))) { return functor_(std::forward(arg)); } - template - R operator()(const Ref& arg) const noexcept(noexcept(std::declval()(arg))) { - return functor_(arg); - } - template - R operator()(Ref& arg) const noexcept(noexcept(std::declval()(arg))) { - return functor_(arg); - } - template - R operator()(Ref&& arg) const noexcept(noexcept(std::declval()(std::forward>(arg)))) { - return functor_(std::forward>(arg)); - } private: F functor_; @@ -142,9 +113,6 @@ class ExpressionTree { void operator()(const T&) const noexcept {} void operator()(T&) const noexcept {} void operator()(T&&) const noexcept {} - void operator()(const Ref&) const noexcept {} - void operator()(Ref&) const noexcept {} - void operator()(Ref&&) const noexcept {} }; template class SkipHelper { @@ -152,9 +120,6 @@ class ExpressionTree { void operator()(const T&) const noexcept {} void operator()(T&) const noexcept {} void operator()(T&&) const noexcept {} - void operator()(const Ref&) const noexcept {} - void operator()(Ref&) const noexcept {} - void operator()(Ref&&) const noexcept {} }; template class VisitorHelper, Fs...> : private SkipHelper, private VisitorHelper { @@ -190,7 +155,7 @@ class ExpressionTree { class Node { friend ExpressionTree; - using Storage = std::variant...>; + using Storage = std::variant; struct SizeVisitor { template @@ -203,20 +168,18 @@ class ExpressionTree { template struct GetVisitor { T& operator()(T& v) const noexcept { return v; } - T& operator()(Ref& r) const noexcept { return r; } template T& operator()(U&) const noexcept { - assertrx(0); + assertrx_throw(0); abort(); } }; template struct GetVisitor { const T& operator()(const T& v) const noexcept { return v; } - const T& operator()(const Ref& r) const noexcept { return r; } template const T& operator()(const U&) const noexcept { - assertrx(0); + assertrx_throw(0); abort(); } }; @@ -225,67 +188,31 @@ class ExpressionTree { bool operator()(const T& lhs, const T& rhs) const noexcept(noexcept(lhs == rhs)) { return lhs == rhs; } - template - bool operator()(const Ref& lhs, const T& rhs) const noexcept(noexcept(rhs == rhs)) { - return static_cast(lhs) == rhs; - } - template - bool operator()(const T& lhs, const Ref& rhs) const noexcept(noexcept(lhs == lhs)) { - return lhs == static_cast(rhs); - } template bool operator()(const T&, const U&) const noexcept { return false; } }; - struct LazyCopyVisitor { - Storage operator()(SubTree& st) const noexcept { return st; } - template - Storage operator()(Ref& r) const { - return r; - } - template - Storage operator()(T& v) const { - return Ref{v}; - } - }; - struct DeepCopyVisitor { + struct CopyVisitor { Storage operator()(const SubTree& st) const noexcept { return st; } template - Storage operator()(const Ref& r) const { - return static_cast(r); - } - template Storage operator()(const T& v) const { return v; } }; - struct DeepRValueCopyVisitor { + struct MoveVisitor { Storage operator()(SubTree&& st) const noexcept { return std::move(st); } template - Storage operator()(Ref&& r) const { - return static_cast(r); - } - template Storage operator()(T&& v) const { return std::forward(v); } }; - struct IsRefVisitor { - template - bool operator()(const T&) const noexcept { - return false; - } - template - bool operator()(const Ref&) const noexcept { - return true; - } - }; public: - Node() : storage_{SubTree{1}} {} + Node() : storage_{std::in_place_type, 1} {} template - Node(OperationType op, size_t s, Args&&... args) : storage_{SubTree{s, std::forward(args)...}}, operation{op} {} + Node(OperationType op, size_t s, Args&&... args) + : storage_{std::in_place_type, s, std::forward(args)...}, operation{op} {} template Node(OperationType op, T&& v) : storage_{std::forward(v)}, operation{op} {} Node(const Node& other) : storage_{other.storage_}, operation{other.operation} {} @@ -323,13 +250,9 @@ class ExpressionTree { } bool IsSubTree() const noexcept { return storage_.index() == 0; } template - bool Holds() const noexcept { + bool Is() const noexcept { return std::holds_alternative(storage_); } - template - bool HoldsOrReferTo() const noexcept { - return std::holds_alternative(storage_) || std::holds_alternative>(storage_); - } void Append() { std::get(storage_).Append(); } void Erase(size_t length) { std::get(storage_).Erase(length); } /// Execute appropriate functor depending on content type @@ -342,26 +265,22 @@ class ExpressionTree { return std::visit(Visitor{std::forward(funcs)...}, storage_); } - Node MakeLazyCopy() & { - static const LazyCopyVisitor visitor; - return {operation, std::visit(visitor, storage_)}; - } - Node MakeDeepCopy() const& { - static const DeepCopyVisitor visitor; + Node Copy() const& { + static const CopyVisitor visitor; return {operation, std::visit(visitor, storage_)}; } - Node MakeDeepCopy() && { - static const DeepRValueCopyVisitor visitor; + Node Move() && { + static const MoveVisitor visitor; return {operation, std::visit(visitor, std::move(storage_))}; } - bool IsRef() const { - static const IsRefVisitor visitor; - return std::visit(visitor, storage_); - } template void SetValue(T&& v) { storage_ = std::forward(v); } + template + void Emplace(Args&&... args) { + storage_.template emplace(std::forward(args)...); + } private: Storage storage_; @@ -379,13 +298,13 @@ class ExpressionTree { ExpressionTree& operator=(ExpressionTree&&) = default; ExpressionTree(const ExpressionTree& other) : activeBrackets_{other.activeBrackets_} { container_.reserve(other.container_.size()); - for (const Node& n : other.container_) container_.emplace_back(n.MakeDeepCopy()); + for (const Node& n : other.container_) container_.emplace_back(n.Copy()); } ExpressionTree& operator=(const ExpressionTree& other) { if (this == &other) return *this; container_.clear(); container_.reserve(other.container_.size()); - for (const Node& n : other.container_) container_.emplace_back(n.MakeDeepCopy()); + for (const Node& n : other.container_) container_.emplace_back(n.Copy()); activeBrackets_ = other.activeBrackets_; return *this; } @@ -401,9 +320,9 @@ class ExpressionTree { /// Insert value at the position template void Insert(size_t pos, OperationType op, T&& v) { - assertrx(pos < container_.size()); + assertrx_throw(pos < container_.size()); for (unsigned& b : activeBrackets_) { - assertrx(b < container_.size()); + assertrx_throw(b < container_.size()); if (b >= pos) ++b; } for (size_t i = 0; i < pos; ++i) { @@ -414,9 +333,9 @@ class ExpressionTree { /// Insert value after the position template void InsertAfter(size_t pos, OperationType op, T&& v) { - assertrx(pos < container_.size()); + assertrx_throw(pos < container_.size()); for (unsigned& b : activeBrackets_) { - assertrx(b < container_.size()); + assertrx_throw(b < container_.size()); if (b > pos) ++b; } for (size_t i = 0; i < pos; ++i) { @@ -428,7 +347,7 @@ class ExpressionTree { template void Append(OperationType op, T&& v) { for (unsigned i : activeBrackets_) { - assertrx(i < container_.size()); + assertrx_throw(i < container_.size()); container_[i].Append(); } container_.emplace_back(op, std::forward(v)); @@ -437,22 +356,26 @@ class ExpressionTree { template void Append(OperationType op, const T& v) { for (unsigned i : activeBrackets_) { - assertrx(i < container_.size()); + assertrx_throw(i < container_.size()); container_[i].Append(); } container_.emplace_back(op, v); } + /// Appends value to the last openned subtree + template + void Append(OperationType op, Args&&... args) { + for (unsigned i : activeBrackets_) { + assertrx_throw(i < container_.size()); + container_[i].Append(); + } + container_.emplace_back(op, T{std::forward(args)...}); + } class const_iterator; /// Appends all nodes from the interval to the last openned subtree void Append(const_iterator begin, const_iterator end) { container_.reserve(container_.size() + (end.PlainIterator() - begin.PlainIterator())); append(begin, end); } - class iterator; - void LazyAppend(iterator begin, iterator end) { - container_.reserve(container_.size() + (end.PlainIterator() - begin.PlainIterator())); - lazyAppend(begin, end); - } /// Appends value as first child of the root template @@ -460,13 +383,24 @@ class ExpressionTree { for (unsigned& i : activeBrackets_) ++i; container_.emplace(container_.begin(), op, std::forward(v)); } + void PopBack() { + assertrx_throw(!container_.empty()); + for (unsigned i : activeBrackets_) { + assertrx_throw(i < container_.size()); + container_[i].Erase(1); + } + if (container_.back().IsSubTree() && !activeBrackets_.empty() && activeBrackets_.back() == container_.size() - 1) { + activeBrackets_.pop_back(); + } + container_.pop_back(); + } /// Enclose area in brackets template void EncloseInBracket(size_t from, size_t to, OperationType op, Args&&... args) { - assertrx(to > from); - assertrx(to <= container_.size()); + assertrx_throw(to > from); + assertrx_throw(to <= container_.size()); for (unsigned& b : activeBrackets_) { - assertrx(b < container_.size()); + assertrx_throw(b < container_.size()); if (b >= from) ++b; } for (size_t i = 0; i < from; ++i) { @@ -475,14 +409,14 @@ class ExpressionTree { if (bracketEnd >= to) { container_[i].Append(); } else { - assertrx(bracketEnd <= from); + assertrx_throw(bracketEnd <= from); } } } #ifndef NDEBUG for (size_t i = from; i < to; ++i) { if (container_[i].IsSubTree()) { - assertrx(Next(i) <= to); + assertrx_throw(Next(i) <= to); } } #endif @@ -492,7 +426,7 @@ class ExpressionTree { template void OpenBracket(OperationType op, Args&&... args) { for (unsigned i : activeBrackets_) { - assertrx(i < container_.size()); + assertrx_throw(i < container_.size()); container_[i].Append(); } activeBrackets_.push_back(container_.size()); @@ -510,55 +444,55 @@ class ExpressionTree { void Reserve(size_t s) { container_.reserve(s); } /// @return size of leaf of subtree beginning from i size_t Size(size_t i) const noexcept { - assertrx(i < Size()); + assertrx_throw(i < Size()); return container_[i].Size(); } /// @return beginning of next children of the same parent size_t Next(size_t i) const noexcept { - assertrx(i < Size()); + assertrx_throw(i < Size()); return i + Size(i); } template - bool HoldsOrReferTo(size_t i) const noexcept { - assertrx(i < Size()); - return container_[i].template HoldsOrReferTo(); + bool Is(size_t i) const noexcept { + assertrx_throw(i < Size()); + return container_[i].template Is(); } bool IsSubTree(size_t i) const noexcept { - assertrx(i < Size()); + assertrx_throw(i < Size()); return container_[i].IsSubTree(); } OperationType GetOperation(size_t i) const noexcept { - assertrx(i < Size()); + assertrx_throw(i < Size()); return container_[i].operation; } void SetOperation(OperationType op, size_t i) noexcept { - assertrx(i < Size()); + assertrx_throw(i < Size()); container_[i].operation = op; } template T& Get(size_t i) { - assertrx(i < Size()); + assertrx_throw(i < Size()); return container_[i].template Value(); } template const T& Get(size_t i) const { - assertrx(i < Size()); + assertrx_throw(i < Size()); return container_[i].template Value(); } template void SetValue(size_t i, T&& v) { - assertrx(i < Size()); + assertrx_throw(i < Size()); return container_[i].template SetValue(std::forward(v)); } void Erase(size_t from, size_t to) { - assertrx(to >= from); + assertrx_throw(to >= from); const size_t count = to - from; for (size_t i = 0; i < from; ++i) { if (container_[i].IsSubTree()) { if (Next(i) >= to) { container_[i].Erase(count); } else { - assertrx(Next(i) <= from); + assertrx_throw(Next(i) <= from); } } } @@ -573,12 +507,12 @@ class ExpressionTree { /// Execute appropriate functor depending on content type template R InvokeAppropriate(size_t i, Fs&&... funcs) { - assertrx(i < container_.size()); + assertrx_throw(i < container_.size()); return container_[i].template InvokeAppropriate(std::forward(funcs)...); } template R InvokeAppropriate(size_t i, Fs&&... funcs) const { - assertrx(i < container_.size()); + assertrx_throw(i < container_.size()); return container_[i].template InvokeAppropriate(std::forward(funcs)...); } /// Execute appropriate functor depending on content type for each node, skip if no appropriate functor @@ -608,12 +542,12 @@ class ExpressionTree { return *this; } const_iterator cbegin() const noexcept { - assertrx(it_->IsSubTree()); + assertrx_throw(it_->IsSubTree()); return it_ + 1; } const_iterator begin() const noexcept { return cbegin(); } const_iterator cend() const noexcept { - assertrx(it_->IsSubTree()); + assertrx_throw(it_->IsSubTree()); return it_ + it_->Size(); } const_iterator end() const noexcept { return cend(); } @@ -638,12 +572,12 @@ class ExpressionTree { } operator const_iterator() const noexcept { return const_iterator(it_); } iterator begin() const noexcept { - assertrx(it_->IsSubTree()); + assertrx_throw(it_->IsSubTree()); return it_ + 1; } const_iterator cbegin() const noexcept { return begin(); } iterator end() const noexcept { - assertrx(it_->IsSubTree()); + assertrx_throw(it_->IsSubTree()); return it_ + it_->Size(); } const_iterator cend() const noexcept { return end(); } @@ -691,7 +625,7 @@ class ExpressionTree { /// @return the last appended leaf or last closed subtree or last openned subtree if it is empty size_t lastAppendedElement() const noexcept { - assertrx(!container_.empty()); + assertrx_throw(!container_.empty()); size_t start = 0; // start of last openned subtree; if (!activeBrackets_.empty()) { start = activeBrackets_.back() + 1; @@ -714,28 +648,6 @@ class ExpressionTree { [this, op](const auto& v) -> void { this->Append(op, v); }); } } - - void lazyAppend(iterator begin, iterator end) { - for (; begin != end; ++begin) { - const OpType op = begin->operation; - begin->template InvokeAppropriate( - [this, &begin, op](const SubTree& b) { - OpenBracket(op); - std::get(container_.back().storage_).CopyPayloadFrom(b); - lazyAppend(begin.begin(), begin.end()); - CloseBracket(); - }, - [this, op](auto& v) -> void { this->Append(op, Ref>{v}); }); - } - } - - ExpressionTree makeLazyCopy() & { - ExpressionTree result; - result.container_.reserve(container_.size()); - for (Node& n : container_) result.container_.emplace_back(n.MakeLazyCopy()); - result.activeBrackets_ = activeBrackets_; - return result; - } }; } // namespace reindexer diff --git a/cpp_src/core/ft/areaholder.h b/cpp_src/core/ft/areaholder.h index e4db54a9f..fb186f6b0 100644 --- a/cpp_src/core/ft/areaholder.h +++ b/cpp_src/core/ft/areaholder.h @@ -39,7 +39,8 @@ class AreaBuffer { [[nodiscard]] bool Empty() const noexcept { return data_.empty(); } void Commit() { if (!data_.empty()) { - boost::sort::pdqsort(data_.begin(), data_.end(), [](const Area &rhs, const Area &lhs) { return rhs.start < lhs.start; }); + boost::sort::pdqsort_branchless(data_.begin(), data_.end(), + [](const Area &rhs, const Area &lhs) noexcept { return rhs.start < lhs.start; }); for (auto vit = data_.begin() + 1; vit != data_.end(); ++vit) { auto prev = vit - 1; if (vit->Concat(*prev)) { diff --git a/cpp_src/core/ft/ft_fast/dataprocessor.cc b/cpp_src/core/ft/ft_fast/dataprocessor.cc index 772b1c785..1b3df8eb0 100644 --- a/cpp_src/core/ft/ft_fast/dataprocessor.cc +++ b/cpp_src/core/ft/ft_fast/dataprocessor.cc @@ -149,13 +149,16 @@ size_t DataProcessor::buildWordsMap(words_map &words_um) { auto &vdocs = holder_.vdocs_; const int fieldscount = fieldSize_; size_t offset = holder_.vdocsOffset_; + auto cycleSize = vdocsTexts.size() / maxIndexWorkers + (vdocsTexts.size() % maxIndexWorkers ? 1 : 0); // build words map parallel in maxIndexWorkers threads - auto worker = [this, &ctxs, &vdocsTexts, offset, maxIndexWorkers, fieldscount, &cfg, &vdocs](int i) { + auto worker = [this, &ctxs, &vdocsTexts, offset, cycleSize, fieldscount, &cfg, &vdocs](int i) { auto ctx = &ctxs[i]; std::string word, str; std::vector wrds; std::vector virtualWords; - for (VDocIdType j = i, sz = VDocIdType(vdocsTexts.size()); j < sz; j += maxIndexWorkers) { + size_t start = cycleSize * i; + size_t fin = std::min(cycleSize * (i + 1), vdocsTexts.size()); + for (VDocIdType j = start; j < fin; ++j) { const size_t vdocId = offset + j; auto &vdoc = vdocs[vdocId]; vdoc.wordsCount.insert(vdoc.wordsCount.begin(), fieldscount, 0.0); diff --git a/cpp_src/core/ft/ft_fast/selecter.cc b/cpp_src/core/ft/ft_fast/selecter.cc index b460ca929..40082ae17 100644 --- a/cpp_src/core/ft/ft_fast/selecter.cc +++ b/cpp_src/core/ft/ft_fast/selecter.cc @@ -335,8 +335,8 @@ void Selecter::processLowRelVariants(FtSelectContext& ctx, const FtMerge return false; }); } else { - boost::sort::pdqsort(ctx.lowRelVariants.begin(), ctx.lowRelVariants.end(), - [](FtBoundVariantEntry& l, FtBoundVariantEntry& r) noexcept { return l.proc > r.proc; }); + boost::sort::pdqsort_branchless(ctx.lowRelVariants.begin(), ctx.lowRelVariants.end(), + [](FtBoundVariantEntry& l, FtBoundVariantEntry& r) noexcept { return l.proc > r.proc; }); } auto lastVariantLen = ctx.lowRelVariants.size() ? ctx.lowRelVariants[0].GetLenCached() : -1; @@ -790,7 +790,7 @@ std::pair Selecter::calcTermRank(const TextSearchResults& r if (!termRank) return std::make_pair(termRank, field); if (holder_.cfg_->summationRanksByFieldsRatio > 0) { - std::sort(ranksInFields.begin(), ranksInFields.end()); + boost::sort::pdqsort_branchless(ranksInFields.begin(), ranksInFields.end()); double k = holder_.cfg_->summationRanksByFieldsRatio; for (auto rank : ranksInFields) { termRank += (k * rank); @@ -921,9 +921,10 @@ void Selecter::mergeIterationGroup(TextSearchResults& rawRes, index_t ra mergedPosInfo.rank = 0; } else { auto& posTmp = mergedPosInfo.posTmp; - boost::sort::pdqsort( - posTmp.begin(), posTmp.end(), - [](const std::pair& l, const std::pair& r) { return l.first < r.first; }); + boost::sort::pdqsort_branchless(posTmp.begin(), posTmp.end(), + [](const std::pair& l, + const std::pair& r) noexcept { return l.first < r.first; }); + auto last = std::unique(posTmp.begin(), posTmp.end()); posTmp.resize(last - posTmp.begin()); @@ -984,9 +985,9 @@ void Selecter::mergeResultsPart(std::vector& rawResul merged.maxRank = m.proc; } } - - boost::sort::pdqsort(merged.begin(), merged.end(), - [](const IDataHolder::MergeInfo& lhs, const IDataHolder::MergeInfo& rhs) { return lhs.proc > rhs.proc; }); + boost::sort::pdqsort_branchless( + merged.begin(), merged.end(), + [](const IDataHolder::MergeInfo& lhs, const IDataHolder::MergeInfo& rhs) noexcept { return lhs.proc > rhs.proc; }); } template @@ -1244,12 +1245,11 @@ typename IDataHolder::MergeData Selecter::mergeResults(std::vector merged_rd; std::vector idoffsets; - for (auto& rawRes : rawResults) { - boost::sort::pdqsort(rawRes.begin(), rawRes.end(), - [](const TextSearchResult& lhs, const TextSearchResult& rhs) { return lhs.proc_ > rhs.proc_; }); + boost::sort::pdqsort_branchless( + rawRes.begin(), rawRes.end(), + [](const TextSearchResult& lhs, const TextSearchResult& rhs) noexcept { return lhs.proc_ > rhs.proc_; }); } - const auto maxMergedSize = std::min(size_t(holder_.cfg_->mergeLimit), totalORVids); merged.reserve(maxMergedSize); @@ -1332,8 +1332,9 @@ typename IDataHolder::MergeData Selecter::mergeResults(std::vector rhs.proc; }); + boost::sort::pdqsort_branchless( + merged.begin(), merged.end(), + [](const IDataHolder::MergeInfo& lhs, const IDataHolder::MergeInfo& rhs) noexcept { return lhs.proc > rhs.proc; }); return merged; } diff --git a/cpp_src/core/ft/ft_fuzzy/merger/basemerger.cc b/cpp_src/core/ft/ft_fuzzy/merger/basemerger.cc index 967161001..12a57498c 100644 --- a/cpp_src/core/ft/ft_fuzzy/merger/basemerger.cc +++ b/cpp_src/core/ft/ft_fuzzy/merger/basemerger.cc @@ -93,7 +93,7 @@ SearchResult BaseMerger::Merge(MergeCtx& ctx, bool inTransaction, const reindexe data_set.AddData(it->Id(), id_ctx); } } - boost::sort::pdqsort(data_set.data_->begin(), data_set.data_->end(), [](const MergedData& lhs, const MergedData& rhs) { + boost::sort::pdqsort(data_set.data_->begin(), data_set.data_->end(), [](const MergedData& lhs, const MergedData& rhs) noexcept { if (lhs.proc_ == rhs.proc_) { return lhs.id_ < rhs.id_; } diff --git a/cpp_src/core/ft/ftsetcashe.h b/cpp_src/core/ft/ftsetcashe.h index 810a06ccd..95e64f014 100644 --- a/cpp_src/core/ft/ftsetcashe.h +++ b/cpp_src/core/ft/ftsetcashe.h @@ -17,6 +17,6 @@ struct FtIdSetCacheVal { FtCtx::Data::Ptr ctx; }; -class FtIdSetCache : public LRUCache {}; +using FtIdSetCache = LRUCache; } // namespace reindexer diff --git a/cpp_src/core/ft/idrelset.cc b/cpp_src/core/ft/idrelset.cc index 7f31546d1..6b87bc15c 100644 --- a/cpp_src/core/ft/idrelset.cc +++ b/cpp_src/core/ft/idrelset.cc @@ -1,4 +1,3 @@ - #include "idrelset.h" #include #include "estl/h_vector.h" @@ -84,9 +83,4 @@ int IdRelSet::Add(VDocIdType id, int pos, int field) { return back().Size(); } -void IdRelType::SimpleCommit() { - boost::sort::pdqsort(pos_.begin(), pos_.end(), - [](const IdRelType::PosType& lhs, const IdRelType::PosType& rhs) { return lhs.pos() < rhs.pos(); }); -} - } // namespace reindexer diff --git a/cpp_src/core/ft/idrelset.h b/cpp_src/core/ft/idrelset.h index 05ea5f8b7..23bc811fa 100644 --- a/cpp_src/core/ft/idrelset.h +++ b/cpp_src/core/ft/idrelset.h @@ -102,11 +102,11 @@ class IdRelType { addField(field); } void SortAndUnique() { - boost::sort::pdqsort(pos_.begin(), pos_.end()); + boost::sort::pdqsort_branchless(pos_.begin(), pos_.end()); auto last = std::unique(pos_.begin(), pos_.end()); pos_.resize(last - pos_.begin()); } - void Clear() { + void Clear() noexcept { usedFieldsMask_ = 0; #ifdef REINDEXER_FT_EXTRA_DEBUG pos_.clear(); @@ -116,7 +116,11 @@ class IdRelType { } size_t Size() const noexcept { return pos_.size(); } size_t size() const noexcept { return pos_.size(); } - void SimpleCommit(); + void SimpleCommit() noexcept { + boost::sort::pdqsort_branchless( + pos_.begin(), pos_.end(), + [](const IdRelType::PosType& lhs, const IdRelType::PosType& rhs) noexcept { return lhs.pos() < rhs.pos(); }); + } const RVector& Pos() const noexcept { return pos_; } uint64_t UsedFieldsMask() const noexcept { return usedFieldsMask_; } size_t HeapSize() const noexcept { return heapSize(pos_); } @@ -141,7 +145,7 @@ class IdRelType { class IdRelSet : public std::vector { public: int Add(VDocIdType id, int pos, int field); - void SimpleCommit() { + void SimpleCommit() noexcept { for (auto& val : *this) val.SimpleCommit(); } diff --git a/cpp_src/core/ft/numtotext.cc b/cpp_src/core/ft/numtotext.cc index fc91c1848..44fe81df2 100644 --- a/cpp_src/core/ft/numtotext.cc +++ b/cpp_src/core/ft/numtotext.cc @@ -45,8 +45,9 @@ static std::string_view getNumorder(int numorder, int i) { return sextillion[i]; case Septillion: return septillion[i]; + default: + throw Error(errParams, "Incorrect order [%s]: too big", numorder); } - throw Error(errParams, "Incorrect order [%s]: too big", numorder); } RX_ALWAYS_INLINE int ansiCharacterToDigit(char ch) noexcept { return static_cast(ch - 48); } @@ -73,6 +74,8 @@ static std::vector getOrders(std::string_view str) { tempString += numStr[i + 1]; tempString += numStr[i]; break; + default: + throw Error(errLogic, "Unexpected lost characters number: %d", lostChars); } } orders.emplace_back(std::move(tempString)); diff --git a/cpp_src/core/iclientsstats.cc b/cpp_src/core/iclientsstats.cc index a6a2a826a..ca232b8e5 100644 --- a/cpp_src/core/iclientsstats.cc +++ b/cpp_src/core/iclientsstats.cc @@ -8,6 +8,7 @@ void ClientStat::GetJSON(WrSerializer& ser) const { JsonBuilder builder(ser); builder.Put("connection_id", connectionId); builder.Put("ip", ip); + builder.Put("protocol", protocol); builder.Put("user_name", userName); builder.Put("db_name", dbName); builder.Put("current_activity", currentActivity); diff --git a/cpp_src/core/iclientsstats.h b/cpp_src/core/iclientsstats.h index 63ef0301a..c96b7dead 100644 --- a/cpp_src/core/iclientsstats.h +++ b/cpp_src/core/iclientsstats.h @@ -6,11 +6,15 @@ namespace reindexer { +constexpr std::string_view kTcpProtocolName = "tcp"; +constexpr std::string_view kUnixProtocolName = "unix"; + class WrSerializer; struct ClientStat { void GetJSON(WrSerializer& ser) const; int connectionId = 0; + std::string_view protocol = kTcpProtocolName; std::string ip; std::string userName; std::string dbName; @@ -37,6 +41,7 @@ struct ClientConnectionStat { std::shared_ptr connectionStat; std::shared_ptr txStats; std::string ip; + std::string_view protocol = kTcpProtocolName; std::string userName; std::string dbName; std::string userRights; diff --git a/cpp_src/core/idset.cc b/cpp_src/core/idset.cc index c8576fc94..da7047de7 100644 --- a/cpp_src/core/idset.cc +++ b/cpp_src/core/idset.cc @@ -1,18 +1,8 @@ #include "core/idset.h" -#include #include "tools/errors.h" namespace reindexer { -void IdSet::Commit() { - if (!size() && set_) { - resize(0); - for (auto id : *set_) push_back(id); - } - - usingBtree_.store(false, std::memory_order_release); -} - std::string IdSetPlain::Dump() const { std::string buf = "["; diff --git a/cpp_src/core/idset.h b/cpp_src/core/idset.h index 6c193f9c4..e2e501c00 100644 --- a/cpp_src/core/idset.h +++ b/cpp_src/core/idset.h @@ -74,7 +74,7 @@ class IdSetPlain : protected base_idset { std::string Dump() const; protected: - IdSetPlain(base_idset &&idset) : base_idset(std::move(idset)) {} + IdSetPlain(base_idset &&idset) noexcept : base_idset(std::move(idset)) {} }; std::ostream &operator<<(std::ostream &, const IdSetPlain &); @@ -87,7 +87,7 @@ class IdSet : public IdSetPlain { public: using Ptr = intrusive_ptr>; - IdSet() : usingBtree_(false) {} + IdSet() noexcept : usingBtree_(false) {} IdSet(const IdSet &other) : IdSetPlain(other), set_(!other.set_ ? nullptr : new base_idsetset(*other.set_)), usingBtree_(other.usingBtree_.load()) {} IdSet(IdSet &&other) noexcept : IdSetPlain(std::move(other)), set_(std::move(other.set_)), usingBtree_(other.usingBtree_.load()) {} @@ -108,7 +108,7 @@ class IdSet : public IdSetPlain { return *this; } static Ptr BuildFromUnsorted(base_idset &&ids) { - boost::sort::pdqsort(ids.begin(), ids.end()); + boost::sort::pdqsort_branchless(ids.begin(), ids.end()); ids.erase(std::unique(ids.begin(), ids.end()), ids.end()); // TODO: It would be better to integrate unique into sort return make_intrusive>(std::move(ids)); } @@ -194,18 +194,24 @@ class IdSet : public IdSetPlain { base_idset::erase(d.first, d.second); return d.second - d.first; } else { - resize(0); + clear(); usingBtree_.store(true, std::memory_order_release); return set_->erase(id); } - return 0; } - void Commit(); - bool IsCommited() const { return !usingBtree_.load(std::memory_order_acquire); } - bool IsEmpty() const { return empty() && (!set_ || set_->empty()); } - size_t Size() const { return usingBtree_.load(std::memory_order_acquire) ? set_->size() : size(); } - size_t BTreeSize() const { return set_ ? sizeof(*set_.get()) + set_->size() * sizeof(int) : 0; } - const base_idsetset *BTree() const { return set_.get(); } + void Commit() { + if (!size() && set_) { + reserve(set_->size()); + for (auto id : *set_) push_back(id); + } + + usingBtree_.store(false, std::memory_order_release); + } + bool IsCommited() const noexcept { return !usingBtree_.load(std::memory_order_acquire); } + bool IsEmpty() const noexcept { return empty() && (!set_ || set_->empty()); } + size_t Size() const noexcept { return usingBtree_.load(std::memory_order_acquire) ? set_->size() : size(); } + size_t BTreeSize() const noexcept { return set_ ? sizeof(*set_.get()) + set_->size() * sizeof(int) : 0; } + const base_idsetset *BTree() const noexcept { return set_.get(); } void ReserveForSorted(int sortedIdxCount) { reserve(((set_ ? set_->size() : size())) * (sortedIdxCount + 1)); } protected: @@ -214,7 +220,7 @@ class IdSet : public IdSetPlain { template friend class BtreeIndexReverseIteratorImpl; - IdSet(base_idset &&idset) : IdSetPlain(std::move(idset)), usingBtree_(false) {} + IdSet(base_idset &&idset) noexcept : IdSetPlain(std::move(idset)), usingBtree_(false) {} std::unique_ptr set_; std::atomic usingBtree_; diff --git a/cpp_src/core/idsetcache.h b/cpp_src/core/idsetcache.h index 3c2fd9184..185073c22 100644 --- a/cpp_src/core/idsetcache.h +++ b/cpp_src/core/idsetcache.h @@ -83,8 +83,11 @@ struct hash_idset_cache_key { size_t operator()(const IdSetCacheKey &s) const { return (s.cond << 8) ^ (s.sort << 16) ^ s.keys->Hash(); } }; -class IdSetCache : public LRUCache { +using IdSetCacheBase = LRUCache; + +class IdSetCache : public IdSetCacheBase { public: + IdSetCache(size_t sizeLimit, uint32_t hitCount) : IdSetCacheBase(sizeLimit, hitCount) {} void ClearSorted(const std::bitset &s) { if (s.any()) { Clear([&s](const IdSetCacheKey &k) { return s.test(k.sort); }); diff --git a/cpp_src/core/index/index.cc b/cpp_src/core/index/index.cc index 552033fc2..cd3ba6d3c 100644 --- a/cpp_src/core/index/index.cc +++ b/cpp_src/core/index/index.cc @@ -1,5 +1,4 @@ #include "index.h" -#include "core/namespacedef.h" #include "indexordered.h" #include "indextext/fastindextext.h" #include "indextext/fuzzyindextext.h" @@ -10,8 +9,8 @@ namespace reindexer { -Index::Index(const IndexDef& idef, PayloadType payloadType, const FieldsSet& fields) - : type_(idef.Type()), name_(idef.name_), opts_(idef.opts_), payloadType_(std::move(payloadType)), fields_(fields) { +Index::Index(const IndexDef& idef, PayloadType&& payloadType, FieldsSet&& fields) + : type_(idef.Type()), name_(idef.name_), opts_(idef.opts_), payloadType_(std::move(payloadType)), fields_(std::move(fields)) { logPrintf(LogTrace, "Index::Index ('%s',%s,%s) %s%s%s", idef.name_, idef.indexType_, idef.fieldType_, idef.opts_.IsPK() ? ",pk" : "", idef.opts_.IsDense() ? ",dense" : "", idef.opts_.IsArray() ? ",array" : ""); } @@ -28,38 +27,39 @@ Index::Index(const Index& obj) selectKeyType_(obj.selectKeyType_), sortedIdxCount_(obj.sortedIdxCount_) {} -std::unique_ptr Index::New(const IndexDef& idef, PayloadType payloadType, const FieldsSet& fields) { +std::unique_ptr Index::New(const IndexDef& idef, PayloadType&& payloadType, FieldsSet&& fields, + const NamespaceCacheConfigData& cacheCfg) { switch (idef.Type()) { case IndexStrBTree: case IndexIntBTree: case IndexDoubleBTree: case IndexInt64BTree: case IndexCompositeBTree: - return IndexOrdered_New(idef, std::move(payloadType), fields); + return IndexOrdered_New(idef, std::move(payloadType), std::move(fields), cacheCfg); case IndexStrHash: case IndexIntHash: case IndexInt64Hash: case IndexCompositeHash: - return IndexUnordered_New(idef, std::move(payloadType), fields); + return IndexUnordered_New(idef, std::move(payloadType), std::move(fields), cacheCfg); case IndexIntStore: case IndexStrStore: case IndexInt64Store: case IndexDoubleStore: case IndexBool: case IndexUuidStore: - return IndexStore_New(idef, std::move(payloadType), fields); + return IndexStore_New(idef, std::move(payloadType), std::move(fields)); case IndexFastFT: case IndexCompositeFastFT: - return FastIndexText_New(idef, std::move(payloadType), fields); + return FastIndexText_New(idef, std::move(payloadType), std::move(fields), cacheCfg); case IndexFuzzyFT: case IndexCompositeFuzzyFT: - return FuzzyIndexText_New(idef, std::move(payloadType), fields); + return FuzzyIndexText_New(idef, std::move(payloadType), std::move(fields), cacheCfg); case IndexTtl: - return TtlIndex_New(idef, std::move(payloadType), fields); + return TtlIndex_New(idef, std::move(payloadType), std::move(fields), cacheCfg); case ::IndexRTree: - return IndexRTree_New(idef, std::move(payloadType), fields); + return IndexRTree_New(idef, std::move(payloadType), std::move(fields), cacheCfg); case IndexUuidHash: - return IndexUuid_New(idef, std::move(payloadType), fields); + return IndexUuid_New(idef, std::move(payloadType), std::move(fields), cacheCfg); } throw Error(errParams, "Ivalid index type %d for index '%s'", idef.Type(), idef.name_); } diff --git a/cpp_src/core/index/index.h b/cpp_src/core/index/index.h index a30594354..0e4fe3ccf 100644 --- a/cpp_src/core/index/index.h +++ b/cpp_src/core/index/index.h @@ -12,7 +12,6 @@ #include "core/payload/payloadiface.h" #include "core/perfstatcounter.h" #include "core/selectkeyresult.h" -#include "core/type_consts_helpers.h" #include "ft_preselect.h" #include "indexiterator.h" @@ -46,7 +45,7 @@ class Index { using KeyEntry = reindexer::KeyEntry; using KeyEntryPlain = reindexer::KeyEntry; - Index(const IndexDef& idef, PayloadType payloadType, const FieldsSet& fields); + Index(const IndexDef& idef, PayloadType&& payloadType, FieldsSet&& fields); Index(const Index&); Index& operator=(const Index&) = delete; virtual ~Index() = default; @@ -82,20 +81,24 @@ class Index { virtual bool IsDestroyPartSupported() const noexcept { return false; } virtual void AddDestroyTask(tsl::detail_sparse_hash::ThreadTaskQueue&) {} - const PayloadType& GetPayloadType() const { return payloadType_; } - void UpdatePayloadType(PayloadType payloadType) { payloadType_ = std::move(payloadType); } + const PayloadType& GetPayloadType() const& { return payloadType_; } + const PayloadType& GetPayloadType() const&& = delete; + void UpdatePayloadType(PayloadType&& payloadType) { payloadType_ = std::move(payloadType); } - static std::unique_ptr New(const IndexDef& idef, PayloadType payloadType, const FieldsSet& fields_); + static std::unique_ptr New(const IndexDef& idef, PayloadType&& payloadType, FieldsSet&& fields_, + const NamespaceCacheConfigData& cacheCfg); KeyValueType KeyType() const { return keyType_; } KeyValueType SelectKeyType() const { return selectKeyType_; } - const FieldsSet& Fields() const { return fields_; } - const std::string& Name() const { return name_; } + const FieldsSet& Fields() const& noexcept { return fields_; } + const FieldsSet& Fields() const&& = delete; + const std::string& Name() const& noexcept { return name_; } + const std::string& Name() const&& = delete; IndexType Type() const { return type_; } const std::vector& SortOrders() const { return sortOrders_; } const IndexOpts& Opts() const { return opts_; } virtual void SetOpts(const IndexOpts& opts) { opts_ = opts; } - virtual void SetFields(FieldsSet&& fields) { fields_ = std::move(fields); } + void SetFields(FieldsSet&& fields) { fields_ = std::move(fields); } [[nodiscard]] SortType SortId() const noexcept { return sortId_; } virtual void SetSortedIdxCount(int sortedIdxCount) { sortedIdxCount_ = sortedIdxCount; } virtual FtMergeStatuses GetFtMergeStatuses(const RdxContext&) { @@ -124,6 +127,7 @@ class Index { virtual bool IsBuilt() const noexcept { return isBuilt_; } virtual void MarkBuilt() noexcept { isBuilt_ = true; } virtual void EnableUpdatesCountingMode(bool) noexcept {} + virtual void ReconfigureCache(const NamespaceCacheConfigData& cacheCfg) = 0; virtual void Dump(std::ostream& os, std::string_view step = " ", std::string_view offset = "") const { dump(os, step, offset); } @@ -140,8 +144,12 @@ class Index { IndexOpts opts_; // Payload type of items mutable PayloadType payloadType_; - // Fields in index. + +private: + // Fields in index FieldsSet fields_; + +protected: // Perfstat counter PerfStatCounterMT commitPerfCounter_; PerfStatCounterMT selectPerfCounter_; diff --git a/cpp_src/core/index/indexordered.cc b/cpp_src/core/index/indexordered.cc index 828030b1b..643562e35 100644 --- a/cpp_src/core/index/indexordered.cc +++ b/cpp_src/core/index/indexordered.cc @@ -11,7 +11,7 @@ template Variant IndexOrdered::Upsert(const Variant &key, IdType id, bool &clearCache) { if (key.Type().Is()) { if (this->empty_ids_.Unsorted().Add(id, IdSet::Auto, this->sortedIdxCount_)) { - if (this->cache_) this->cache_.reset(); + this->cache_.reset(); clearCache = true; this->isBuilt_ = false; } @@ -28,7 +28,7 @@ Variant IndexOrdered::Upsert(const Variant &key, IdType id, bool &clearCache) if (keyIt->second.Unsorted().Add(id, this->opts_.IsPK() ? IdSet::Ordered : IdSet::Auto, this->sortedIdxCount_)) { this->isBuilt_ = false; - if (this->cache_) this->cache_.reset(); + this->cache_.reset(); clearCache = true; } this->tracker_.markUpdated(this->idx_map, keyIt); @@ -56,10 +56,6 @@ SelectKeyResults IndexOrdered::SelectKey(const VariantArray &keys, CondType c return IndexUnordered::SelectKey(keys, condition, sortId, opts, ctx, rdxCtx); } - if (keys.size() < 1) { - throw Error(errParams, "For condition required at least 1 argument, but provided 0"); - } - SelectKeyResult res; auto startIt = this->idx_map.begin(); auto endIt = this->idx_map.end(); @@ -81,7 +77,6 @@ SelectKeyResults IndexOrdered::SelectKey(const VariantArray &keys, CondType c if (startIt == this->idx_map.end()) startIt = this->idx_map.upper_bound(static_cast(key1)); break; case CondRange: { - if (keys.size() != 2) throw Error(errParams, "For ranged query reuqired 2 arguments, but provided %d", keys.size()); const auto &key2 = keys[1]; startIt = this->idx_map.find(static_cast(key1)); @@ -207,18 +202,20 @@ IndexIterator::Ptr IndexOrdered::CreateIterator() const { } template -static std::unique_ptr IndexOrdered_New(const IndexDef &idef, PayloadType payloadType, const FieldsSet &fields) { +static std::unique_ptr IndexOrdered_New(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields, + const NamespaceCacheConfigData &cacheCfg) { switch (idef.Type()) { case IndexIntBTree: - return std::unique_ptr{new IndexOrdered>(idef, std::move(payloadType), fields)}; + return std::make_unique>>(idef, std::move(payloadType), std::move(fields), cacheCfg); case IndexInt64BTree: - return std::unique_ptr{new IndexOrdered>(idef, std::move(payloadType), fields)}; + return std::make_unique>>(idef, std::move(payloadType), std::move(fields), + cacheCfg); case IndexStrBTree: - return std::unique_ptr{new IndexOrdered>(idef, std::move(payloadType), fields)}; + return std::make_unique>>(idef, std::move(payloadType), std::move(fields), cacheCfg); case IndexDoubleBTree: - return std::unique_ptr{new IndexOrdered>(idef, std::move(payloadType), fields)}; + return std::make_unique>>(idef, std::move(payloadType), std::move(fields), cacheCfg); case IndexCompositeBTree: - return std::unique_ptr{new IndexOrdered>(idef, std::move(payloadType), fields)}; + return std::make_unique>>(idef, std::move(payloadType), std::move(fields), cacheCfg); case IndexStrHash: case IndexIntHash: case IndexInt64Hash: @@ -242,9 +239,11 @@ static std::unique_ptr IndexOrdered_New(const IndexDef &idef, PayloadType } // NOLINTBEGIN(*cplusplus.NewDeleteLeaks) -std::unique_ptr IndexOrdered_New(const IndexDef &idef, PayloadType payloadType, const FieldsSet &fields) { - return (idef.opts_.IsPK() || idef.opts_.IsDense()) ? IndexOrdered_New(idef, std::move(payloadType), fields) - : IndexOrdered_New(idef, std::move(payloadType), fields); +std::unique_ptr IndexOrdered_New(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields, + const NamespaceCacheConfigData &cacheCfg) { + return (idef.opts_.IsPK() || idef.opts_.IsDense()) + ? IndexOrdered_New(idef, std::move(payloadType), std::move(fields), cacheCfg) + : IndexOrdered_New(idef, std::move(payloadType), std::move(fields), cacheCfg); } // NOLINTEND(*cplusplus.NewDeleteLeaks) diff --git a/cpp_src/core/index/indexordered.h b/cpp_src/core/index/indexordered.h index e8dc6d229..a12928024 100644 --- a/cpp_src/core/index/indexordered.h +++ b/cpp_src/core/index/indexordered.h @@ -10,18 +10,19 @@ class IndexOrdered : public IndexUnordered { using ref_type = typename IndexUnordered::ref_type; using key_type = typename IndexUnordered::key_type; - IndexOrdered(const IndexDef &idef, PayloadType payloadType, const FieldsSet &fields) - : IndexUnordered(idef, std::move(payloadType), fields) {} + IndexOrdered(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields, const NamespaceCacheConfigData &cacheCfg) + : IndexUnordered(idef, std::move(payloadType), std::move(fields), cacheCfg) {} SelectKeyResults SelectKey(const VariantArray &keys, CondType condition, SortType stype, Index::SelectOpts opts, const BaseFunctionCtx::Ptr &ctx, const RdxContext &) override; Variant Upsert(const Variant &key, IdType id, bool &clearCache) override; void MakeSortOrders(UpdateSortedContext &ctx) override; IndexIterator::Ptr CreateIterator() const override; - std::unique_ptr Clone() const override { return std::unique_ptr{new IndexOrdered(*this)}; } + std::unique_ptr Clone() const override { return std::make_unique>(*this); } bool IsOrdered() const noexcept override { return true; } }; -std::unique_ptr IndexOrdered_New(const IndexDef &idef, PayloadType payloadType, const FieldsSet &fields); +std::unique_ptr IndexOrdered_New(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields, + const NamespaceCacheConfigData &cacheCfg); } // namespace reindexer diff --git a/cpp_src/core/index/indexstore.cc b/cpp_src/core/index/indexstore.cc index 27fe6239f..df95f979c 100644 --- a/cpp_src/core/index/indexstore.cc +++ b/cpp_src/core/index/indexstore.cc @@ -7,8 +7,8 @@ namespace reindexer { template <> -IndexStore::IndexStore(const IndexDef &idef, PayloadType payloadType, const FieldsSet &fields) - : Index(idef, std::move(payloadType), fields) { +IndexStore::IndexStore(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields) + : Index(idef, std::move(payloadType), std::move(fields)) { keyType_ = selectKeyType_ = KeyValueType::Double{}; opts_.Array(true); } @@ -68,8 +68,8 @@ Variant IndexStore::Upsert(const Variant &key, IdType /*id*/, bool template Variant IndexStore::Upsert(const Variant &key, IdType id, bool & /*clearCache*/) { - if (!opts_.IsArray() && !opts_.IsDense() && !opts_.IsSparse() && key.Type().Is()) { - idx_data.resize(std::max(id + 1, int(idx_data.size()))); + if (!opts_.IsArray() && !opts_.IsDense() && !opts_.IsSparse() && !key.Type().Is()) { + idx_data.resize(std::max(id + 1, IdType(idx_data.size()))); idx_data[id] = static_cast(key); } return Variant(key); @@ -106,43 +106,8 @@ SelectKeyResults IndexStore::SelectKey(const VariantArray &keys, CondType con if (condition == CondAny && !this->opts_.IsArray() && !this->opts_.IsSparse() && !sopts.distinct) throw Error(errParams, "The 'NOT NULL' condition is suported only by 'sparse' or 'array' indexes"); - // TODO: it may be necessary to remove or change this switch after QueryEntry refactoring - switch (condition) { - case CondAny: - if (!this->opts_.IsArray() && !this->opts_.IsSparse() && !sopts.distinct) { - throw Error(errParams, "The 'NOT NULL' condition is suported only by 'sparse' or 'array' indexes"); - } - break; - case CondEmpty: - if (!this->opts_.IsArray() && !this->opts_.IsSparse()) { - throw Error(errParams, "The 'is NULL' condition is suported only by 'sparse' or 'array' indexes"); - } - break; - case CondAllSet: - case CondSet: - case CondEq: - break; - case CondRange: - case CondDWithin: - if (keys.size() != 2) { - throw Error(errParams, "For condition %s required exactly 2 arguments, but provided %d", CondTypeToStr(condition), - keys.size()); - } - break; - case CondLt: - case CondLe: - case CondGt: - case CondGe: - case CondLike: - if (keys.size() != 1) { - throw Error(errParams, "For condition %s required exactly 1 argument, but provided %d", CondTypeToStr(condition), - keys.size()); - } - break; - } - - res.comparators_.push_back(Comparator(condition, KeyType(), keys, opts_.IsArray(), sopts.distinct, payloadType_, fields_, - idx_data.size() ? idx_data.data() : nullptr, opts_.collateOpts_)); + res.comparators_.emplace_back(condition, KeyType(), keys, opts_.IsArray(), bool(sopts.distinct), payloadType_, Fields(), + idx_data.size() ? idx_data.data() : nullptr, opts_.collateOpts_); return SelectKeyResults(std::move(res)); } @@ -183,20 +148,20 @@ void IndexStore::AddDestroyTask(tsl::detail_sparse_hash::ThreadTaskQueue &q) (void)q; } -std::unique_ptr IndexStore_New(const IndexDef &idef, PayloadType payloadType, const FieldsSet &fields) { +std::unique_ptr IndexStore_New(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields) { switch (idef.Type()) { case IndexBool: - return std::unique_ptr{new IndexStore(idef, std::move(payloadType), fields)}; + return std::make_unique>(idef, std::move(payloadType), std::move(fields)); case IndexIntStore: - return std::unique_ptr{new IndexStore(idef, std::move(payloadType), fields)}; + return std::make_unique>(idef, std::move(payloadType), std::move(fields)); case IndexInt64Store: - return std::unique_ptr{new IndexStore(idef, std::move(payloadType), fields)}; + return std::make_unique>(idef, std::move(payloadType), std::move(fields)); case IndexDoubleStore: - return std::unique_ptr{new IndexStore(idef, std::move(payloadType), fields)}; + return std::make_unique>(idef, std::move(payloadType), std::move(fields)); case IndexStrStore: - return std::unique_ptr{new IndexStore(idef, std::move(payloadType), fields)}; + return std::make_unique>(idef, std::move(payloadType), std::move(fields)); case IndexUuidStore: - return std::unique_ptr{new IndexStore(idef, std::move(payloadType), fields)}; + return std::make_unique>(idef, std::move(payloadType), std::move(fields)); case IndexStrHash: case IndexStrBTree: case IndexIntBTree: diff --git a/cpp_src/core/index/indexstore.h b/cpp_src/core/index/indexstore.h index 832c11a4f..6c3a1b4f8 100644 --- a/cpp_src/core/index/indexstore.h +++ b/cpp_src/core/index/indexstore.h @@ -8,7 +8,8 @@ namespace reindexer { template class IndexStore : public Index { public: - IndexStore(const IndexDef &idef, PayloadType payloadType, const FieldsSet &fields) : Index(idef, std::move(payloadType), fields) { + IndexStore(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields) + : Index(idef, std::move(payloadType), std::move(fields)) { static T a; keyType_ = selectKeyType_ = Variant(a).Type(); } @@ -21,13 +22,14 @@ class IndexStore : public Index { const BaseFunctionCtx::Ptr &ctx, const RdxContext &) override; void Commit() override; void UpdateSortedIds(const UpdateSortedContext & /*ctx*/) override {} - std::unique_ptr Clone() const override { return std::unique_ptr{new IndexStore(*this)}; } + std::unique_ptr Clone() const override { return std::make_unique>(*this); } IndexMemStat GetMemStat(const RdxContext &) override; bool HoldsStrings() const noexcept override { return std::is_same_v || std::is_same_v; } void Dump(std::ostream &os, std::string_view step = " ", std::string_view offset = "") const override { dump(os, step, offset); } virtual void AddDestroyTask(tsl::detail_sparse_hash::ThreadTaskQueue &) override; virtual bool IsDestroyPartSupported() const noexcept override { return true; } virtual bool IsUuid() const noexcept override final { return std::is_same_v; } + virtual void ReconfigureCache(const NamespaceCacheConfigData &) override {} template struct HasAddTask : std::false_type {}; @@ -46,8 +48,8 @@ class IndexStore : public Index { }; template <> -IndexStore::IndexStore(const IndexDef &, PayloadType, const FieldsSet &); +IndexStore::IndexStore(const IndexDef &, PayloadType &&, FieldsSet &&); -std::unique_ptr IndexStore_New(const IndexDef &idef, PayloadType payloadType, const FieldsSet &fields_); +std::unique_ptr IndexStore_New(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&); } // namespace reindexer diff --git a/cpp_src/core/index/indextext/fastindextext.cc b/cpp_src/core/index/indextext/fastindextext.cc index 36db2dc49..c56dc44b8 100644 --- a/cpp_src/core/index/indextext/fastindextext.cc +++ b/cpp_src/core/index/indextext/fastindextext.cc @@ -123,9 +123,9 @@ template IndexMemStat FastIndexText::GetMemStat(const RdxContext &ctx) { auto ret = IndexUnordered::GetMemStat(ctx); - contexted_shared_lock lck(this->mtx_, &ctx); + contexted_shared_lock lck(this->mtx_, ctx); ret.fulltextSize = this->holder_->GetMemStat(); - if (this->cache_ft_) ret.idsetCache = this->cache_ft_->GetMemStat(); + ret.idsetCache = this->cache_ft_ ? this->cache_ft_->GetMemStat() : LRUCacheMemStat(); return ret; } @@ -135,7 +135,7 @@ IdSet::Ptr FastIndexText::Select(FtCtx::Ptr fctx, FtDSLQuery &&dsl, bool inTr fctx->GetData()->extraWordSymbols_ = this->getConfig()->extraWordSymbols; fctx->GetData()->isWordPositions_ = true; - auto mergeData = this->holder_->Select(std::move(dsl), this->fields_.size(), fctx->NeedArea(), getConfig()->maxAreasInDoc, + auto mergeData = this->holder_->Select(std::move(dsl), this->Fields().size(), fctx->NeedArea(), getConfig()->maxAreasInDoc, inTransaction, std::move(statuses.statuses), useExternSt, rdxCtx); // convert vids(uniq documents id) to ids (real ids) IdSet::Ptr mergedIds = make_intrusive>(); @@ -232,7 +232,7 @@ void FastIndexText::commitFulltextImpl() { } auto tm1 = high_resolution_clock::now(); - this->holder_->Process(this->fields_.size(), !this->opts_.IsDense()); + this->holder_->Process(this->Fields().size(), !this->opts_.IsDense()); if (this->holder_->NeedClear(this->tracker_.isCompleteUpdated())) { this->tracker_.clear(); } @@ -373,12 +373,15 @@ reindexer::FtPreselectT FastIndexText::FtPreselect(const RdxContext &rdxCtx) std::vector(holder_->rowId2Vdoc_.size(), false), &holder_->rowId2Vdoc_}; } -std::unique_ptr FastIndexText_New(const IndexDef &idef, PayloadType payloadType, const FieldsSet &fields) { +std::unique_ptr FastIndexText_New(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields, + const NamespaceCacheConfigData &cacheCfg) { switch (idef.Type()) { case IndexFastFT: - return std::unique_ptr{new FastIndexText>(idef, std::move(payloadType), fields)}; + return std::make_unique>>(idef, std::move(payloadType), std::move(fields), + cacheCfg); case IndexCompositeFastFT: - return std::unique_ptr{new FastIndexText>(idef, std::move(payloadType), fields)}; + return std::make_unique>>(idef, std::move(payloadType), std::move(fields), + cacheCfg); case IndexStrHash: case IndexStrBTree: case IndexIntBTree: diff --git a/cpp_src/core/index/indextext/fastindextext.h b/cpp_src/core/index/indextext/fastindextext.h index 5a6ebb50d..1a2bb913a 100644 --- a/cpp_src/core/index/indextext/fastindextext.h +++ b/cpp_src/core/index/indextext/fastindextext.h @@ -2,8 +2,6 @@ #include "core/ft/config/ftfastconfig.h" #include "core/ft/ft_fast/dataholder.h" -#include "core/ft/ft_fast/dataprocessor.h" -#include "core/ft/typos.h" #include "indextext.h" namespace reindexer { @@ -22,10 +20,11 @@ class FastIndexText : public IndexText { this->CommitFulltext(); } - FastIndexText(const IndexDef& idef, PayloadType payloadType, const FieldsSet& fields) : Base(idef, std::move(payloadType), fields) { + FastIndexText(const IndexDef& idef, PayloadType&& payloadType, FieldsSet&& fields, const NamespaceCacheConfigData& cacheCfg) + : Base(idef, std::move(payloadType), std::move(fields), cacheCfg) { initConfig(); } - std::unique_ptr Clone() const override { return std::unique_ptr{new FastIndexText(*this)}; } + std::unique_ptr Clone() const override { return std::make_unique>(*this); } IdSet::Ptr Select(FtCtx::Ptr fctx, FtDSLQuery&& dsl, bool inTransaction, FtMergeStatuses&&, FtUseExternStatuses, const RdxContext&) override final; IndexMemStat GetMemStat(const RdxContext&) override final; @@ -40,7 +39,7 @@ class FastIndexText : public IndexText { reindexer::FtPreselectT FtPreselect(const RdxContext& rdxCtx) override final; bool EnablePreselectBeforeFt() const override final { return getConfig()->enablePreselectBeforeFt; } -protected: +private: void commitFulltextImpl() override final; FtFastConfig* getConfig() const noexcept { return dynamic_cast(this->cfg_.get()); } void initConfig(const FtFastConfig* = nullptr); @@ -53,6 +52,7 @@ class FastIndexText : public IndexText { std::unique_ptr holder_; }; -std::unique_ptr FastIndexText_New(const IndexDef& idef, PayloadType payloadType, const FieldsSet& fields); +std::unique_ptr FastIndexText_New(const IndexDef& idef, PayloadType&& payloadType, FieldsSet&& fields, + const NamespaceCacheConfigData& cacheCfg); } // namespace reindexer diff --git a/cpp_src/core/index/indextext/fieldsgetter.h b/cpp_src/core/index/indextext/fieldsgetter.h index 791932b27..3af05f79f 100644 --- a/cpp_src/core/index/indextext/fieldsgetter.h +++ b/cpp_src/core/index/indextext/fieldsgetter.h @@ -1,6 +1,5 @@ #pragma once #include "core/ft/usingcontainer.h" -#include "core/index/payload_map.h" #include "core/payload/fieldsset.h" #include "vendor/utf8cpp/utf8.h" diff --git a/cpp_src/core/index/indextext/fuzzyindextext.cc b/cpp_src/core/index/indextext/fuzzyindextext.cc index 339ac1252..1e2344cf0 100644 --- a/cpp_src/core/index/indextext/fuzzyindextext.cc +++ b/cpp_src/core/index/indextext/fuzzyindextext.cc @@ -56,7 +56,7 @@ void FuzzyIndexText::commitFulltextImpl() { } template -void FuzzyIndexText::CreateConfig(const FtFuzzyConfig* cfg) { +void FuzzyIndexText::createConfig(const FtFuzzyConfig* cfg) { if (cfg) { this->cfg_.reset(new FtFuzzyConfig(*cfg)); return; @@ -65,13 +65,15 @@ void FuzzyIndexText::CreateConfig(const FtFuzzyConfig* cfg) { this->cfg_->parse(this->opts_.config, this->ftFields_); } -std::unique_ptr FuzzyIndexText_New(const IndexDef& idef, PayloadType payloadType, const FieldsSet& fields) { +std::unique_ptr FuzzyIndexText_New(const IndexDef& idef, PayloadType&& payloadType, FieldsSet&& fields, + const NamespaceCacheConfigData& cacheCfg) { switch (idef.Type()) { case IndexFuzzyFT: - return std::unique_ptr{new FuzzyIndexText>(idef, std::move(payloadType), fields)}; + return std::make_unique>>(idef, std::move(payloadType), std::move(fields), + cacheCfg); case IndexCompositeFuzzyFT: - return std::unique_ptr{ - new FuzzyIndexText>(idef, std::move(payloadType), fields)}; + return std::make_unique>>(idef, std::move(payloadType), + std::move(fields), cacheCfg); case IndexStrHash: case IndexStrBTree: case IndexIntBTree: diff --git a/cpp_src/core/index/indextext/fuzzyindextext.h b/cpp_src/core/index/indextext/fuzzyindextext.h index d1a94780a..0c5d90846 100644 --- a/cpp_src/core/index/indextext/fuzzyindextext.h +++ b/cpp_src/core/index/indextext/fuzzyindextext.h @@ -1,9 +1,6 @@ #pragma once -#include "core/ft/config/ftfastconfig.h" #include "core/ft/ft_fuzzy/searchengine.h" -#include "core/ft/ftsetcashe.h" -#include "core/ft/idrelset.h" #include "indextext.h" namespace reindexer { @@ -13,10 +10,11 @@ class FuzzyIndexText : public IndexText { using Base = IndexText; public: - FuzzyIndexText(const FuzzyIndexText& other) : Base(other) { CreateConfig(other.getConfig()); } + FuzzyIndexText(const FuzzyIndexText& other) : Base(other) { createConfig(other.getConfig()); } - FuzzyIndexText(const IndexDef& idef, PayloadType payloadType, const FieldsSet& fields) : Base(idef, std::move(payloadType), fields) { - CreateConfig(); + FuzzyIndexText(const IndexDef& idef, PayloadType&& payloadType, FieldsSet&& fields, const NamespaceCacheConfigData& cacheCfg) + : Base(idef, std::move(payloadType), std::move(fields), cacheCfg) { + createConfig(); } SelectKeyResults SelectKey(const VariantArray& /*keys*/, CondType, Index::SelectOpts, const BaseFunctionCtx::Ptr&, FtPreselectT&&, @@ -24,7 +22,7 @@ class FuzzyIndexText : public IndexText { assertrx(0); abort(); } - std::unique_ptr Clone() const override final { return std::unique_ptr{new FuzzyIndexText(*this)}; } + std::unique_ptr Clone() const override final { return std::make_unique>(*this); } IdSet::Ptr Select(FtCtx::Ptr fctx, FtDSLQuery&& dsl, bool inTransaction, FtMergeStatuses&&, FtUseExternStatuses, const RdxContext&) override final; Variant Upsert(const Variant& key, IdType id, bool& clearCache) override final { @@ -43,12 +41,13 @@ class FuzzyIndexText : public IndexText { protected: void commitFulltextImpl() override final; FtFuzzyConfig* getConfig() const noexcept { return dynamic_cast(this->cfg_.get()); } - void CreateConfig(const FtFuzzyConfig* cfg = nullptr); + void createConfig(const FtFuzzyConfig* cfg = nullptr); search_engine::SearchEngine engine_; std::vector vdocs_; }; -std::unique_ptr FuzzyIndexText_New(const IndexDef& idef, PayloadType payloadType, const FieldsSet& fields); +std::unique_ptr FuzzyIndexText_New(const IndexDef& idef, PayloadType&& payloadType, FieldsSet&& fields, + const NamespaceCacheConfigData& cacheCfg); } // namespace reindexer diff --git a/cpp_src/core/index/indextext/indextext.cc b/cpp_src/core/index/indextext/indextext.cc index 4a8a646db..4ef8d42ee 100644 --- a/cpp_src/core/index/indextext/indextext.cc +++ b/cpp_src/core/index/indextext/indextext.cc @@ -9,7 +9,11 @@ namespace reindexer { template -IndexText::IndexText(const IndexText &other) : IndexUnordered(other), cache_ft_(std::make_shared()) { +IndexText::IndexText(const IndexText &other) + : IndexUnordered(other), + cache_ft_(std::make_unique(other.cacheMaxSize_, other.hitsToCache_)), + cacheMaxSize_(other.cacheMaxSize_), + hitsToCache_(other.hitsToCache_) { initSearchers(); } // Generic implemetation for string index @@ -19,16 +23,17 @@ void IndexText::initSearchers() { size_t jsonPathIdx = 0; if (this->payloadType_) { - for (unsigned i = 0; i < this->fields_.size(); i++) { - auto fieldIdx = this->fields_[i]; + const auto &fields = this->Fields(); + for (unsigned i = 0, s = fields.size(); i < s; i++) { + auto fieldIdx = fields[i]; if (fieldIdx == IndexValueType::SetByJsonPath) { - assertrx(jsonPathIdx < this->fields_.getJsonPathsLength()); - ftFields_.emplace(this->fields_.getJsonPath(jsonPathIdx++), i); + assertrx(jsonPathIdx < fields.getJsonPathsLength()); + ftFields_.emplace(fields.getJsonPath(jsonPathIdx++), i); } else { ftFields_.emplace(this->payloadType_->Field(fieldIdx).Name(), i); } } - if rx_unlikely (ftFields_.size() != this->fields_.size()) { + if rx_unlikely (ftFields_.size() != fields.size()) { throw Error(errParams, "Composite fulltext index '%s' contains duplicated fields", this->name_); } if rx_unlikely (ftFields_.size() > kMaxFtCompositeFields) { @@ -55,6 +60,18 @@ void IndexText::SetOpts(const IndexOpts &opts) { } } +template +void IndexText::ReconfigureCache(const NamespaceCacheConfigData &cacheCfg) { + if (cacheMaxSize_ != cacheCfg.ftIdxCacheSize || hitsToCache_ != cacheCfg.ftIdxHitsToCache) { + cacheMaxSize_ = cacheCfg.ftIdxCacheSize; + hitsToCache_ = cacheCfg.ftIdxHitsToCache; + if (cache_ft_) { + cache_ft_ = std::make_unique(cacheMaxSize_, hitsToCache_); + } + } + Base::ReconfigureCache(cacheCfg); +} + template FtCtx::Ptr IndexText::prepareFtCtx(const BaseFunctionCtx::Ptr &ctx) { FtCtx::Ptr ftctx = reindexer::reinterpret_pointer_cast(ctx); @@ -178,7 +195,7 @@ SelectKeyResults IndexText::SelectKey(const VariantArray &keys, CondType cond template FieldsGetter IndexText::Getter() { - return FieldsGetter(this->fields_, this->payloadType_, this->KeyType()); + return FieldsGetter(this->Fields(), this->payloadType_, this->KeyType()); } template class IndexText>; diff --git a/cpp_src/core/index/indextext/indextext.h b/cpp_src/core/index/indextext/indextext.h index d09c61a26..b1458227d 100644 --- a/cpp_src/core/index/indextext/indextext.h +++ b/cpp_src/core/index/indextext/indextext.h @@ -8,7 +8,6 @@ #include "core/ft/ftsetcashe.h" #include "core/index/indexunordered.h" #include "core/selectfunc/ctx/ftctx.h" -#include "estl/fast_hash_map.h" #include "estl/shared_mutex.h" #include "fieldsgetter.h" @@ -20,8 +19,11 @@ class IndexText : public IndexUnordered { public: IndexText(const IndexText& other); - IndexText(const IndexDef& idef, PayloadType payloadType, const FieldsSet& fields) - : IndexUnordered(idef, std::move(payloadType), fields), cache_ft_(std::make_shared()) { + IndexText(const IndexDef& idef, PayloadType&& payloadType, FieldsSet&& fields, const NamespaceCacheConfigData& cacheCfg) + : IndexUnordered(idef, std::move(payloadType), std::move(fields), cacheCfg), + cache_ft_(std::make_unique(cacheCfg.ftIdxCacheSize, cacheCfg.ftIdxHitsToCache)), + cacheMaxSize_(cacheCfg.ftIdxCacheSize), + hitsToCache_(cacheCfg.ftIdxHitsToCache) { this->selectKeyType_ = KeyValueType::String{}; initSearchers(); } @@ -39,7 +41,7 @@ class IndexText : public IndexUnordered { // Rebuild will be done on first select } void CommitFulltext() override final { - cache_ft_ = std::make_shared(); + cache_ft_ = std::make_unique(cacheMaxSize_, hitsToCache_); commitFulltextImpl(); this->isBuilt_ = true; } @@ -51,7 +53,8 @@ class IndexText : public IndexUnordered { } void ClearCache(const std::bitset& s) override { Base::ClearCache(s); } void MarkBuilt() noexcept override { assertrx(0); } - bool IsFulltext() const noexcept override { return true; } + bool IsFulltext() const noexcept override final { return true; } + void ReconfigureCache(const NamespaceCacheConfigData& cacheCfg) override final; protected: using Mutex = MarkedMutex; @@ -66,7 +69,9 @@ class IndexText : public IndexUnordered { void initSearchers(); FieldsGetter Getter(); - std::shared_ptr cache_ft_; + std::unique_ptr cache_ft_; + size_t cacheMaxSize_; + uint32_t hitsToCache_; RHashMap ftFields_; std::unique_ptr cfg_; diff --git a/cpp_src/core/index/indexunordered.cc b/cpp_src/core/index/indexunordered.cc index b311fe25f..06af94780 100644 --- a/cpp_src/core/index/indexunordered.cc +++ b/cpp_src/core/index/indexunordered.cc @@ -3,7 +3,6 @@ #include "core/index/payload_map.h" #include "core/index/string_map.h" #include "core/indexdef.h" -#include "core/keyvalue/uuid.h" #include "core/rdxcontext.h" #include "rtree/greenesplitter.h" #include "rtree/linearsplitter.h" @@ -18,55 +17,95 @@ namespace reindexer { constexpr int kMaxIdsForDistinct = 500; template -IndexUnordered::IndexUnordered(const IndexDef &idef, PayloadType payloadType, const FieldsSet &fields) - : Base(idef, std::move(payloadType), fields), idx_map() { +IndexUnordered::IndexUnordered(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields, + const NamespaceCacheConfigData &cacheCfg) + : Base(idef, std::move(payloadType), std::move(fields)), + idx_map(), + cacheMaxSize_(cacheCfg.idxIdsetCacheSize), + hitsToCache_(cacheCfg.idxIdsetHitsToCache) { static_assert(!(is_str_map_v || is_payload_map_v)); } template <> -IndexUnordered>::IndexUnordered(const IndexDef &idef, PayloadType payloadType, const FieldsSet &fields) - : Base(idef, std::move(payloadType), fields), idx_map(idef.opts_.collateOpts_) {} +IndexUnordered>::IndexUnordered(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields, + const NamespaceCacheConfigData &cacheCfg) + : Base(idef, std::move(payloadType), std::move(fields)), + idx_map(idef.opts_.collateOpts_), + cacheMaxSize_(cacheCfg.idxIdsetCacheSize), + hitsToCache_(cacheCfg.idxIdsetHitsToCache) {} template <> -IndexUnordered>::IndexUnordered(const IndexDef &idef, PayloadType payloadType, const FieldsSet &fields) - : Base(idef, std::move(payloadType), fields), idx_map(idef.opts_.collateOpts_) {} +IndexUnordered>::IndexUnordered(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields, + const NamespaceCacheConfigData &cacheCfg) + : Base(idef, std::move(payloadType), std::move(fields)), + idx_map(idef.opts_.collateOpts_), + cacheMaxSize_(cacheCfg.idxIdsetCacheSize), + hitsToCache_(cacheCfg.idxIdsetHitsToCache) {} template <> -IndexUnordered>::IndexUnordered(const IndexDef &idef, PayloadType payloadType, const FieldsSet &fields) - : Base(idef, std::move(payloadType), fields), idx_map(idef.opts_.collateOpts_) {} +IndexUnordered>::IndexUnordered(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields, + const NamespaceCacheConfigData &cacheCfg) + : Base(idef, std::move(payloadType), std::move(fields)), + idx_map(idef.opts_.collateOpts_), + cacheMaxSize_(cacheCfg.idxIdsetCacheSize), + hitsToCache_(cacheCfg.idxIdsetHitsToCache) {} template <> -IndexUnordered>::IndexUnordered(const IndexDef &idef, PayloadType payloadType, - const FieldsSet &fields) - : Base(idef, std::move(payloadType), fields), idx_map(idef.opts_.collateOpts_) {} +IndexUnordered>::IndexUnordered(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields, + const NamespaceCacheConfigData &cacheCfg) + : Base(idef, std::move(payloadType), std::move(fields)), + idx_map(idef.opts_.collateOpts_), + cacheMaxSize_(cacheCfg.idxIdsetCacheSize), + hitsToCache_(cacheCfg.idxIdsetHitsToCache) {} template <> -IndexUnordered>::IndexUnordered(const IndexDef &idef, PayloadType payloadType, const FieldsSet &fields) - : Base(idef, std::move(payloadType), fields), idx_map(idef.opts_.collateOpts_) {} +IndexUnordered>::IndexUnordered(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields, + const NamespaceCacheConfigData &cacheCfg) + : Base(idef, std::move(payloadType), std::move(fields)), + idx_map(idef.opts_.collateOpts_), + cacheMaxSize_(cacheCfg.idxIdsetCacheSize), + hitsToCache_(cacheCfg.idxIdsetHitsToCache) {} template <> -IndexUnordered>::IndexUnordered(const IndexDef &idef, PayloadType payloadType, - const FieldsSet &fields) - : Base(idef, payloadType, fields), idx_map(std::move(payloadType), Base::Fields()) {} +IndexUnordered>::IndexUnordered(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields, + const NamespaceCacheConfigData &cacheCfg) + : Base(idef, std::move(payloadType), std::move(fields)), + idx_map(PayloadType{Base::GetPayloadType()}, FieldsSet{Base::Fields()}), + cacheMaxSize_(cacheCfg.idxIdsetCacheSize), + hitsToCache_(cacheCfg.idxIdsetHitsToCache) {} template <> -IndexUnordered>::IndexUnordered(const IndexDef &idef, PayloadType payloadType, - const FieldsSet &fields) - : Base(idef, payloadType, fields), idx_map(std::move(payloadType), Base::Fields()) {} +IndexUnordered>::IndexUnordered(const IndexDef &idef, PayloadType &&payloadType, + FieldsSet &&fields, const NamespaceCacheConfigData &cacheCfg) + : Base(idef, std::move(payloadType), std::move(fields)), + idx_map(PayloadType{Base::GetPayloadType()}, FieldsSet{Base::Fields()}), + cacheMaxSize_(cacheCfg.idxIdsetCacheSize), + hitsToCache_(cacheCfg.idxIdsetHitsToCache) {} template <> -IndexUnordered>::IndexUnordered(const IndexDef &idef, PayloadType payloadType, - const FieldsSet &fields) - : Base(idef, payloadType, fields), idx_map(std::move(payloadType), Base::Fields()) {} +IndexUnordered>::IndexUnordered(const IndexDef &idef, PayloadType &&payloadType, + FieldsSet &&fields, + const NamespaceCacheConfigData &cacheCfg) + : Base(idef, std::move(payloadType), std::move(fields)), + idx_map(PayloadType{Base::GetPayloadType()}, FieldsSet{Base::Fields()}), + cacheMaxSize_(cacheCfg.idxIdsetCacheSize), + hitsToCache_(cacheCfg.idxIdsetHitsToCache) {} template <> -IndexUnordered>::IndexUnordered(const IndexDef &idef, PayloadType payloadType, const FieldsSet &fields) - : Base(idef, payloadType, fields), idx_map(std::move(payloadType), Base::Fields()) {} +IndexUnordered>::IndexUnordered(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields, + const NamespaceCacheConfigData &cacheCfg) + : Base(idef, std::move(payloadType), std::move(fields)), + idx_map(PayloadType{Base::GetPayloadType()}, FieldsSet{Base::Fields()}), + cacheMaxSize_(cacheCfg.idxIdsetCacheSize), + hitsToCache_(cacheCfg.idxIdsetHitsToCache) {} template <> -IndexUnordered>::IndexUnordered(const IndexDef &idef, PayloadType payloadType, - const FieldsSet &fields) - : Base(idef, payloadType, fields), idx_map(std::move(payloadType), Base::Fields()) {} +IndexUnordered>::IndexUnordered(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields, + const NamespaceCacheConfigData &cacheCfg) + : Base(idef, std::move(payloadType), std::move(fields)), + idx_map(PayloadType{Base::GetPayloadType()}, FieldsSet{Base::Fields()}), + cacheMaxSize_(cacheCfg.idxIdsetCacheSize), + hitsToCache_(cacheCfg.idxIdsetHitsToCache) {} template bool IndexUnordered::HoldsStrings() const noexcept { @@ -79,7 +118,13 @@ bool IndexUnordered::HoldsStrings() const noexcept { template IndexUnordered::IndexUnordered(const IndexUnordered &other) - : Base(other), idx_map(other.idx_map), cache_(nullptr), empty_ids_(other.empty_ids_), tracker_(other.tracker_) {} + : Base(other), + idx_map(other.idx_map), + cache_(nullptr), + cacheMaxSize_(other.cacheMaxSize_), + hitsToCache_(other.hitsToCache_), + empty_ids_(other.empty_ids_), + tracker_(other.tracker_) {} template size_t heap_size(const key_type & /*kt*/) { @@ -130,7 +175,7 @@ Variant IndexUnordered::Upsert(const Variant &key, IdType id, bool &clearCach // reset cache if (key.Type().Is()) { // TODO maybe error or default value if the index is not sparse if (this->empty_ids_.Unsorted().Add(id, IdSet::Auto, this->sortedIdxCount_)) { - if (cache_) cache_.reset(); + cache_.reset(); clearCache = true; this->isBuilt_ = false; } @@ -146,7 +191,7 @@ Variant IndexUnordered::Upsert(const Variant &key, IdType id, bool &clearCach } if (keyIt->second.Unsorted().Add(id, this->opts_.IsPK() ? IdSet::Ordered : IdSet::Auto, this->sortedIdxCount_)) { - if (cache_) cache_.reset(); + cache_.reset(); clearCache = true; this->isBuilt_ = false; } @@ -168,7 +213,7 @@ void IndexUnordered::Delete(const Variant &key, IdType id, StringsHolder &str delcnt = this->empty_ids_.Unsorted().Erase(id); assertrx(delcnt); this->isBuilt_ = false; - if (cache_) cache_.reset(); + cache_.reset(); clearCache = true; return; } @@ -180,12 +225,12 @@ void IndexUnordered::Delete(const Variant &key, IdType id, StringsHolder &str delcnt = keyIt->second.Unsorted().Erase(id); (void)delcnt; this->isBuilt_ = false; - if (cache_) cache_.reset(); + cache_.reset(); clearCache = true; // TODO: we have to implement removal of composite indexes (doesn't work right now) assertf(this->opts_.IsArray() || this->Opts().IsSparse() || delcnt, "Delete unexists id from index '%s' id=%d,key=%s (%s)", this->name_, - id, key.As(this->payloadType_, this->fields_), - Variant(keyIt->first).As(this->payloadType_, this->fields_)); + id, key.As(this->payloadType_, this->Fields()), + Variant(keyIt->first).As(this->payloadType_, this->Fields())); if (keyIt->second.Unsorted().IsEmpty()) { this->tracker_.markDeleted(keyIt); @@ -345,7 +390,7 @@ template void IndexUnordered::Commit() { this->empty_ids_.Unsorted().Commit(); - if (!cache_) cache_.reset(new IdSetCache()); + if (!cache_) cache_.reset(new IdSetCache(cacheMaxSize_, hitsToCache_)); if (!tracker_.isUpdated()) return; @@ -433,18 +478,33 @@ void IndexUnordered::AddDestroyTask(tsl::detail_sparse_hash::ThreadTaskQueue (void)q; } +template +void IndexUnordered::ReconfigureCache(const NamespaceCacheConfigData &cacheCfg) { + if (cacheMaxSize_ != cacheCfg.idxIdsetCacheSize || hitsToCache_ != cacheCfg.idxIdsetHitsToCache) { + cacheMaxSize_ = cacheCfg.idxIdsetCacheSize; + hitsToCache_ = cacheCfg.idxIdsetHitsToCache; + if (cache_) { + cache_.reset(new IdSetCache(cacheMaxSize_, hitsToCache_)); + } + } +} + template -static std::unique_ptr IndexUnordered_New(const IndexDef &idef, PayloadType payloadType, const FieldsSet &fields) { +static std::unique_ptr IndexUnordered_New(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields, + const NamespaceCacheConfigData &cacheCfg) { switch (idef.Type()) { case IndexIntHash: - return std::unique_ptr{new IndexUnordered>(idef, std::move(payloadType), fields)}; + return std::make_unique>>(idef, std::move(payloadType), std::move(fields), + cacheCfg); case IndexInt64Hash: - return std::unique_ptr{ - new IndexUnordered>(idef, std::move(payloadType), fields)}; + return std::make_unique>>(idef, std::move(payloadType), + std::move(fields), cacheCfg); case IndexStrHash: - return std::unique_ptr{new IndexUnordered>(idef, std::move(payloadType), fields)}; + return std::make_unique>>(idef, std::move(payloadType), std::move(fields), + cacheCfg); case IndexCompositeHash: - return std::unique_ptr{new IndexUnordered>(idef, std::move(payloadType), fields)}; + return std::make_unique>>(idef, std::move(payloadType), std::move(fields), + cacheCfg); case IndexStrBTree: case IndexIntBTree: case IndexInt64BTree: @@ -469,9 +529,11 @@ static std::unique_ptr IndexUnordered_New(const IndexDef &idef, PayloadTy } // NOLINTBEGIN(*cplusplus.NewDeleteLeaks) -std::unique_ptr IndexUnordered_New(const IndexDef &idef, PayloadType payloadType, const FieldsSet &fields) { - return (idef.opts_.IsPK() || idef.opts_.IsDense()) ? IndexUnordered_New(idef, std::move(payloadType), fields) - : IndexUnordered_New(idef, std::move(payloadType), fields); +std::unique_ptr IndexUnordered_New(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields, + const NamespaceCacheConfigData &cacheCfg) { + return (idef.opts_.IsPK() || idef.opts_.IsDense()) + ? IndexUnordered_New(idef, std::move(payloadType), std::move(fields), cacheCfg) + : IndexUnordered_New(idef, std::move(payloadType), std::move(fields), cacheCfg); } // NOLINTEND(*cplusplus.NewDeleteLeaks) diff --git a/cpp_src/core/index/indexunordered.h b/cpp_src/core/index/indexunordered.h index 05cc89d95..11b8fbe74 100644 --- a/cpp_src/core/index/indexunordered.h +++ b/cpp_src/core/index/indexunordered.h @@ -26,7 +26,7 @@ class IndexUnordered : public IndexStore> { typename T::key_type>::type>::type>::type; using key_type = StoreIndexKeyType; - IndexUnordered(const IndexDef &idef, PayloadType payloadType, const FieldsSet &fields); + IndexUnordered(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields, const NamespaceCacheConfigData &cacheCfg); IndexUnordered(const IndexUnordered &other); Variant Upsert(const Variant &key, IdType id, bool &chearCache) override; @@ -35,7 +35,7 @@ class IndexUnordered : public IndexStore> { const BaseFunctionCtx::Ptr &ctx, const RdxContext &) override; void Commit() override; void UpdateSortedIds(const UpdateSortedContext &) override; - std::unique_ptr Clone() const override { return std::unique_ptr{new IndexUnordered(*this)}; } + std::unique_ptr Clone() const override { return std::make_unique>(*this); } IndexMemStat GetMemStat(const RdxContext &) override; size_t Size() const noexcept override final { return idx_map.size(); } void SetSortedIdxCount(int sortedIdxCount) override; @@ -49,6 +49,7 @@ class IndexUnordered : public IndexStore> { void AddDestroyTask(tsl::detail_sparse_hash::ThreadTaskQueue &) override; bool IsDestroyPartSupported() const noexcept override { return true; } + void ReconfigureCache(const NamespaceCacheConfigData &cacheCfg) override; protected: bool tryIdsetCache(const VariantArray &keys, CondType condition, SortType sortId, @@ -60,6 +61,8 @@ class IndexUnordered : public IndexStore> { T idx_map; // Merged idsets cache atomic_unique_ptr cache_; + size_t cacheMaxSize_; + uint32_t hitsToCache_; // Empty ids Index::KeyEntry empty_ids_; // Tracker of updates @@ -70,8 +73,9 @@ class IndexUnordered : public IndexStore> { void dump(S &os, std::string_view step, std::string_view offset) const; }; -constexpr inline unsigned maxSelectivityPercentForIdset() noexcept { return 30u; } +constexpr unsigned maxSelectivityPercentForIdset() noexcept { return 30u; } -std::unique_ptr IndexUnordered_New(const IndexDef &idef, PayloadType payloadType, const FieldsSet &fields); +std::unique_ptr IndexUnordered_New(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields, + const NamespaceCacheConfigData &cacheCfg); } // namespace reindexer diff --git a/cpp_src/core/index/keyentry.h b/cpp_src/core/index/keyentry.h index 079f543c6..9c500dba1 100644 --- a/cpp_src/core/index/keyentry.h +++ b/cpp_src/core/index/keyentry.h @@ -33,12 +33,14 @@ class KeyEntry { auto idsAsc = Sorted(ctx.getCurSortId()); size_t idx = 0; + const auto& ids2Sorts = ctx.ids2Sorts(); + [[maybe_unused]] const IdType maxRowId = IdType(ids2Sorts.size()); // For all ids of current key for (auto rowid : ids_) { - assertf(rowid < int(ctx.ids2Sorts().size()), "id=%d,ctx.ids2Sorts().size()=%d", rowid, ctx.ids2Sorts().size()); - idsAsc[idx++] = ctx.ids2Sorts()[rowid]; + assertf(rowid < maxRowId, "id=%d,ctx.ids2Sorts().size()=%d", rowid, maxRowId); + idsAsc[idx++] = ids2Sorts[rowid]; } - boost::sort::pdqsort(idsAsc.begin(), idsAsc.end()); + boost::sort::pdqsort_branchless(idsAsc.begin(), idsAsc.end()); } void Dump(std::ostream& os, std::string_view step, std::string_view offset) const { std::string newOffset; diff --git a/cpp_src/core/index/payload_map.h b/cpp_src/core/index/payload_map.h index 91132989f..357b671a3 100644 --- a/cpp_src/core/index/payload_map.h +++ b/cpp_src/core/index/payload_map.h @@ -12,7 +12,7 @@ namespace reindexer { class PayloadValueWithHash : public PayloadValue { public: PayloadValueWithHash() noexcept : PayloadValue() {} - PayloadValueWithHash(PayloadValue pv, const PayloadType &pt, const FieldsSet &fields) + PayloadValueWithHash(PayloadValue &&pv, const PayloadType &pt, const FieldsSet &fields) : PayloadValue(std::move(pv)), hash_(ConstPayload(pt, *static_cast(this)).GetHash(fields)) {} PayloadValueWithHash(const PayloadValueWithHash &o) noexcept : PayloadValue(o), hash_(o.hash_) {} PayloadValueWithHash(PayloadValueWithHash &&o) noexcept : PayloadValue(std::move(o)), hash_(o.hash_) {} @@ -29,7 +29,8 @@ class PayloadValueWithHash : public PayloadValue { struct equal_composite { using is_transparent = void; - equal_composite(PayloadType type, const FieldsSet &fields) : type_(std::move(type)), fields_(fields) {} + template + equal_composite(PT &&type, FS &&fields) : type_(std::forward(type)), fields_(std::forward(fields)) {} bool operator()(const PayloadValue &lhs, const PayloadValue &rhs) const { assertrx(type_); return ConstPayload(type_, lhs).IsEQ(rhs, fields_); @@ -50,7 +51,8 @@ struct equal_composite { FieldsSet fields_; }; struct hash_composite { - hash_composite(PayloadType type, const FieldsSet &fields) : type_(std::move(type)), fields_(fields) {} + template + hash_composite(PT &&type, FS &&fields) : type_(std::forward(type)), fields_(std::forward(fields)) {} size_t operator()(const PayloadValueWithHash &s) const { return s.GetHash(); } size_t operator()(const PayloadValue &s) const { assertrx(type_); @@ -61,7 +63,7 @@ struct hash_composite { }; struct less_composite { - less_composite(PayloadType type, const FieldsSet &fields) : type_(std::move(type)), fields_(fields) {} + less_composite(PayloadType &&type, FieldsSet &&fields) : type_(std::move(type)), fields_(std::move(fields)) {} bool operator()(const PayloadValue &lhs, const PayloadValue &rhs) const { assertrx(type_); assertrx(!lhs.IsFree()); @@ -78,7 +80,7 @@ class payload_str_fields_helper; template <> class payload_str_fields_helper { protected: - payload_str_fields_helper(PayloadType payloadType, const FieldsSet &fields) : payload_type_(std::move(payloadType)) { + payload_str_fields_helper(PayloadType &&payloadType, const FieldsSet &fields) : payload_type_(std::move(payloadType)) { if (fields.getTagsPathsLength() || fields.getJsonPathsLength()) { str_fields_.push_back(0); } @@ -153,13 +155,13 @@ class unordered_payload_map static_assert(std::is_nothrow_move_constructible>::value, "Nothrow movebale key and value required"); - unordered_payload_map(size_t size, PayloadType pt, const FieldsSet &f) - : base_hash_map(size, hash_composite(pt, f), equal_composite(pt, f)), - payload_str_fields_helper(pt, f), + unordered_payload_map(size_t size, PayloadType &&pt, FieldsSet &&f) + : base_hash_map(size, hash_composite(PayloadType{pt}, FieldsSet{f}), equal_composite(PayloadType{pt}, FieldsSet{f})), + payload_str_fields_helper(PayloadType{pt}, f), payloadType_(std::move(pt)), - fields_(f) {} + fields_(std::move(f)) {} - unordered_payload_map(PayloadType pt, const FieldsSet &f) : unordered_payload_map(1000, std::move(pt), f) {} + unordered_payload_map(PayloadType &&pt, FieldsSet &&f) : unordered_payload_map(1000, std::move(pt), std::move(f)) {} unordered_payload_map(const unordered_payload_map &other) : base_hash_map(other), payload_str_fields_helper(other), payloadType_(other.payloadType_), fields_(other.fields_) { @@ -185,7 +187,7 @@ class unordered_payload_map } template std::pair emplace(const PayloadValue &pl, V &&v) { - PayloadValueWithHash key(pl, payloadType_, fields_); + PayloadValueWithHash key(PayloadValue{pl}, payloadType_, fields_); auto res = base_hash_map::emplace(std::move(key), std::forward(v)); if (res.second) this->add_ref(res.first->first); return res; @@ -207,7 +209,7 @@ class unordered_payload_map } T1 &operator[](const PayloadValue &k) { - PayloadValueWithHash key(k, payloadType_, fields_); + PayloadValueWithHash key(PayloadValue{k}, payloadType_, fields_); return base_hash_map::operator[](std::move(key)); } T1 &operator[](PayloadValue &&k) { @@ -247,7 +249,8 @@ class payload_map : private btree::btree_map, using payload_str_fields_helper::have_str_fields; payload_map(PayloadType payloadType, const FieldsSet &fields) - : base_tree_map(less_composite(payloadType, fields)), payload_str_fields_helper(std::move(payloadType), fields) {} + : base_tree_map(less_composite(PayloadType{payloadType}, FieldsSet{fields})), + payload_str_fields_helper(std::move(payloadType), fields) {} payload_map(const payload_map &other) : base_tree_map(other), payload_str_fields_helper(other) { for (auto &item : *this) this->add_ref(const_cast(item.first)); } diff --git a/cpp_src/core/index/rtree/greenesplitter.h b/cpp_src/core/index/rtree/greenesplitter.h index ba707e781..c8c6a7179 100644 --- a/cpp_src/core/index/rtree/greenesplitter.h +++ b/cpp_src/core/index/rtree/greenesplitter.h @@ -1,6 +1,7 @@ #pragma once #include "splitter.h" +#include "vendor/sort/pdqsort.hpp" namespace reindexer { @@ -53,7 +54,7 @@ class GreeneSplitter : private Splitter secondSeedBoundRect.Left() + secondSeedBoundRect.Right()) { std::swap(seeds.first, seeds.second); } - std::sort(std::begin(indexes), std::end(indexes), [&src, this](size_t lhs, size_t rhs) { + boost::sort::pdqsort(std::begin(indexes), std::end(indexes), [&src, this](size_t lhs, size_t rhs) { return Base::getBoundRect(lhs < MaxEntries ? src[lhs] : this->appendingEntry_).Left() < Base::getBoundRect(rhs < MaxEntries ? src[rhs] : this->appendingEntry_).Left(); }); @@ -61,7 +62,7 @@ class GreeneSplitter : private Splitter secondSeedBoundRect.Bottom() + secondSeedBoundRect.Top()) { std::swap(seeds.first, seeds.second); } - std::sort(std::begin(indexes), std::end(indexes), [&src, this](size_t lhs, size_t rhs) { + boost::sort::pdqsort(std::begin(indexes), std::end(indexes), [&src, this](size_t lhs, size_t rhs) { return Base::getBoundRect(lhs < MaxEntries ? src[lhs] : this->appendingEntry_).Bottom() < Base::getBoundRect(rhs < MaxEntries ? src[rhs] : this->appendingEntry_).Bottom(); }); diff --git a/cpp_src/core/index/rtree/indexrtree.cc b/cpp_src/core/index/rtree/indexrtree.cc index cf895150c..aee04e1dc 100644 --- a/cpp_src/core/index/rtree/indexrtree.cc +++ b/cpp_src/core/index/rtree/indexrtree.cc @@ -78,7 +78,7 @@ void IndexRTree::Upsert(VariantArra if (keyIt->second.Unsorted().Add(id, this->opts_.IsPK() ? IdSet::Ordered : IdSet::Auto, this->sortedIdxCount_)) { this->isBuilt_ = false; // reset cache - if (this->cache_) this->cache_.reset(); + this->cache_.reset(); clearCache = true; } this->tracker_.markUpdated(this->idx_map, keyIt); @@ -99,7 +99,7 @@ void IndexRTree::Delete(const Varia const Point point = static_cast(keys); typename Map::iterator keyIt = this->idx_map.find(point); if (keyIt == this->idx_map.end()) return; - if (this->cache_) this->cache_.reset(); + this->cache_.reset(); clearCache = true; this->isBuilt_ = false; @@ -108,8 +108,8 @@ void IndexRTree::Delete(const Varia (void)delcnt; // TODO: we have to implement removal of composite indexes (doesn't work right now) assertf(this->Opts().IsSparse() || delcnt, "Delete unexists id from index '%s' id=%d,key=%s (%s)", this->name_, id, - Variant(keys).template As(this->payloadType_, this->fields_), - Variant(keyIt->first).As(this->payloadType_, this->fields_)); + Variant(keys).template As(this->payloadType_, this->Fields()), + Variant(keyIt->first).As(this->payloadType_, this->Fields())); if (keyIt->second.Unsorted().IsEmpty()) { this->tracker_.markDeleted(keyIt); @@ -120,41 +120,44 @@ void IndexRTree::Delete(const Varia } } -std::unique_ptr IndexRTree_New(const IndexDef &idef, PayloadType payloadType, const FieldsSet &fields) { +std::unique_ptr IndexRTree_New(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields, + const NamespaceCacheConfigData &cacheCfg) { switch (idef.opts_.RTreeType()) { case IndexOpts::Linear: if (idef.opts_.IsPK() || idef.opts_.IsDense()) { - return std::unique_ptr{ - new IndexRTree(idef, std::move(payloadType), fields)}; + return std::make_unique>(idef, std::move(payloadType), + std::move(fields), cacheCfg); } else { - return std::unique_ptr{new IndexRTree(idef, std::move(payloadType), fields)}; + return std::make_unique>(idef, std::move(payloadType), std::move(fields), + cacheCfg); } case IndexOpts::Quadratic: if (idef.opts_.IsPK() || idef.opts_.IsDense()) { - return std::unique_ptr{ - new IndexRTree(idef, std::move(payloadType), fields)}; - } else { - return std::unique_ptr{ - new IndexRTree(idef, std::move(payloadType), fields)}; + return std::make_unique>(idef, std::move(payloadType), + std::move(fields), cacheCfg); } + return std::make_unique>(idef, std::move(payloadType), std::move(fields), + cacheCfg); case IndexOpts::Greene: if (idef.opts_.IsPK() || idef.opts_.IsDense()) { - return std::unique_ptr{ - new IndexRTree(idef, std::move(payloadType), fields)}; + return std::make_unique>(idef, std::move(payloadType), + std::move(fields), cacheCfg); } else { - return std::unique_ptr{new IndexRTree(idef, std::move(payloadType), fields)}; + return std::make_unique>(idef, std::move(payloadType), std::move(fields), + cacheCfg); } case IndexOpts::RStar: if (idef.opts_.IsPK() || idef.opts_.IsDense()) { - return std::unique_ptr{ - new IndexRTree(idef, std::move(payloadType), fields)}; + return std::make_unique>(idef, std::move(payloadType), + std::move(fields), cacheCfg); } else { - return std::unique_ptr{new IndexRTree(idef, std::move(payloadType), fields)}; + return std::make_unique>(idef, std::move(payloadType), std::move(fields), + cacheCfg); } - default: - assertrx(0); - abort(); } + + assertrx(0); + std::abort(); } template class IndexRTree; diff --git a/cpp_src/core/index/rtree/indexrtree.h b/cpp_src/core/index/rtree/indexrtree.h index effdbd04d..9d4b23986 100644 --- a/cpp_src/core/index/rtree/indexrtree.h +++ b/cpp_src/core/index/rtree/indexrtree.h @@ -11,8 +11,8 @@ class IndexRTree : public IndexUnordered; public: - IndexRTree(const IndexDef &idef, PayloadType payloadType, const FieldsSet &fields) - : IndexUnordered{idef, std::move(payloadType), fields} {} + IndexRTree(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields, const NamespaceCacheConfigData &cacheCfg) + : IndexUnordered{idef, std::move(payloadType), std::move(fields), cacheCfg} {} SelectKeyResults SelectKey(const VariantArray &keys, CondType, SortType, Index::SelectOpts, const BaseFunctionCtx::Ptr &, const RdxContext &) override; @@ -21,9 +21,10 @@ class IndexRTree : public IndexUnordered::Delete; void Delete(const VariantArray &keys, IdType id, StringsHolder &, bool &clearCache) override; - std::unique_ptr Clone() const override { return std::unique_ptr{new IndexRTree(*this)}; } + std::unique_ptr Clone() const override { return std::make_unique(*this); } }; -std::unique_ptr IndexRTree_New(const IndexDef &idef, PayloadType payloadType, const FieldsSet &fields); +std::unique_ptr IndexRTree_New(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields, + const NamespaceCacheConfigData &cacheCfg); } // namespace reindexer diff --git a/cpp_src/core/index/rtree/rstarsplitter.h b/cpp_src/core/index/rtree/rstarsplitter.h index a44c9748e..427a4add3 100644 --- a/cpp_src/core/index/rtree/rstarsplitter.h +++ b/cpp_src/core/index/rtree/rstarsplitter.h @@ -1,6 +1,7 @@ #pragma once #include "splitter.h" +#include "vendor/sort/pdqsort.hpp" namespace reindexer { @@ -19,12 +20,12 @@ class RStarSplitter : private Splitter TtlIndex_New(const IndexDef &idef, PayloadType payloadType, const FieldsSet &fields) { +std::unique_ptr TtlIndex_New(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields, + const NamespaceCacheConfigData &cacheCfg) { if (idef.opts_.IsPK() || idef.opts_.IsDense()) { - return std::unique_ptr{new TtlIndex>(idef, std::move(payloadType), fields)}; + return std::make_unique>>(idef, std::move(payloadType), std::move(fields), + cacheCfg); } - return std::unique_ptr{new TtlIndex>(idef, std::move(payloadType), fields)}; + return std::make_unique>>(idef, std::move(payloadType), std::move(fields), cacheCfg); } } // namespace reindexer diff --git a/cpp_src/core/index/ttlindex.h b/cpp_src/core/index/ttlindex.h index acc0b1a72..b59b6947c 100644 --- a/cpp_src/core/index/ttlindex.h +++ b/cpp_src/core/index/ttlindex.h @@ -10,11 +10,11 @@ namespace reindexer { template class TtlIndex : public IndexOrdered { public: - TtlIndex(const IndexDef &idef, PayloadType payloadType, const FieldsSet &fields) - : IndexOrdered(idef, std::move(payloadType), fields), expireAfter_(idef.expireAfter_) {} + TtlIndex(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields, const NamespaceCacheConfigData &cacheCfg) + : IndexOrdered(idef, std::move(payloadType), std::move(fields), cacheCfg), expireAfter_(idef.expireAfter_) {} TtlIndex(const TtlIndex &other) : IndexOrdered(other), expireAfter_(other.expireAfter_) {} int64_t GetTTLValue() const noexcept override { return expireAfter_; } - std::unique_ptr Clone() const override { return std::unique_ptr{new TtlIndex(*this)}; } + std::unique_ptr Clone() const override { return std::make_unique>(*this); } void UpdateExpireAfter(int64_t v) noexcept { expireAfter_ = v; } private: @@ -22,7 +22,8 @@ class TtlIndex : public IndexOrdered { int64_t expireAfter_ = 0; }; -std::unique_ptr TtlIndex_New(const IndexDef &idef, PayloadType payloadType, const FieldsSet &fields); +std::unique_ptr TtlIndex_New(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields, + const NamespaceCacheConfigData &cacheCfg); void UpdateExpireAfter(Index *i, int64_t v); } // namespace reindexer diff --git a/cpp_src/core/index/uuid_index.cc b/cpp_src/core/index/uuid_index.cc index 57ff52cb4..7622020d3 100644 --- a/cpp_src/core/index/uuid_index.cc +++ b/cpp_src/core/index/uuid_index.cc @@ -10,8 +10,9 @@ void UuidIndex::Upsert(VariantArray &result, const VariantArray &keys, IdType id } } -std::unique_ptr IndexUuid_New(const IndexDef &idef, PayloadType payloadType, const FieldsSet &fields) { - return std::unique_ptr{new UuidIndex{idef, std::move(payloadType), fields}}; +std::unique_ptr IndexUuid_New(const IndexDef &idef, PayloadType &&payloadType, FieldsSet &&fields, + const NamespaceCacheConfigData &cacheCfg) { + return std::make_unique(idef, std::move(payloadType), std::move(fields), cacheCfg); } } // namespace reindexer diff --git a/cpp_src/core/index/uuid_index.h b/cpp_src/core/index/uuid_index.h index 1352c3f43..fb2a4411d 100644 --- a/cpp_src/core/index/uuid_index.h +++ b/cpp_src/core/index/uuid_index.h @@ -1,7 +1,6 @@ #pragma once #include "core/index/indexunordered.h" -#include "core/keyvalue/uuid.h" namespace reindexer { @@ -9,12 +8,14 @@ class UuidIndex : public IndexUnordered using Base = IndexUnordered>; public: - UuidIndex(const IndexDef& idef, PayloadType payloadType, const FieldsSet& fields) : Base{idef, std::move(payloadType), fields} {} - std::unique_ptr Clone() const override { return std::unique_ptr{new UuidIndex{*this}}; } + UuidIndex(const IndexDef& idef, PayloadType&& payloadType, FieldsSet&& fields, const NamespaceCacheConfigData& cacheCfg) + : Base{idef, std::move(payloadType), std::move(fields), cacheCfg} {} + std::unique_ptr Clone() const override { return std::make_unique(*this); } using Base::Upsert; void Upsert(VariantArray& result, const VariantArray& keys, IdType id, bool& clearCache) override; // TODO delete this after #1353 }; -std::unique_ptr IndexUuid_New(const IndexDef& idef, PayloadType payloadType, const FieldsSet& fields); +std::unique_ptr IndexUuid_New(const IndexDef& idef, PayloadType&& payloadType, FieldsSet&& fields, + const NamespaceCacheConfigData& cacheCfg); } // namespace reindexer diff --git a/cpp_src/core/indexdef.cc b/cpp_src/core/indexdef.cc index afe75ba9b..909d11da7 100644 --- a/cpp_src/core/indexdef.cc +++ b/cpp_src/core/indexdef.cc @@ -111,9 +111,9 @@ IndexDef::IndexDef(std::string name, JsonPaths jsonPaths, IndexType type, IndexO this->FromType(type); } -bool IndexDef::IsEqual(const IndexDef &other, bool skipConfig) const { +bool IndexDef::IsEqual(const IndexDef &other, IndexComparison cmpType) const { return name_ == other.name_ && jsonPaths_ == other.jsonPaths_ && Type() == other.Type() && fieldType_ == other.fieldType_ && - opts_.IsEqual(other.opts_, skipConfig) && expireAfter_ == other.expireAfter_; + opts_.IsEqual(other.opts_, cmpType) && expireAfter_ == other.expireAfter_; } IndexType IndexDef::Type() const { diff --git a/cpp_src/core/indexdef.h b/cpp_src/core/indexdef.h index 66b478083..7551bb13f 100644 --- a/cpp_src/core/indexdef.h +++ b/cpp_src/core/indexdef.h @@ -26,9 +26,7 @@ struct IndexDef { int64_t expireAfter = 0) noexcept; IndexDef(std::string name, std::string indexType, std::string fieldType, IndexOpts opts); IndexDef(std::string name, JsonPaths jsonPaths, IndexType type, IndexOpts opts); - bool operator==(const IndexDef &other) const { return IsEqual(other, false); } - bool operator!=(const IndexDef &other) const { return !IsEqual(other, false); } - bool IsEqual(const IndexDef &other, bool skipConfig) const; + bool IsEqual(const IndexDef &other, IndexComparison cmpType) const; IndexType Type() const; std::string getCollateMode() const; const std::vector &Conditions() const; diff --git a/cpp_src/core/indexopts.cc b/cpp_src/core/indexopts.cc index 11948330f..6e8a3cafc 100644 --- a/cpp_src/core/indexopts.cc +++ b/cpp_src/core/indexopts.cc @@ -22,8 +22,9 @@ IndexOpts::IndexOpts(uint8_t flags, CollateMode mode, RTreeIndexType rtreeType) IndexOpts::IndexOpts(const std::string& sortOrderUTF8, uint8_t flags, RTreeIndexType rtreeType) : options(flags), collateOpts_(sortOrderUTF8), rtreeType_(rtreeType) {} -bool IndexOpts::IsEqual(const IndexOpts& other, bool skipConfig) const { - return options == other.options && (skipConfig || config == other.config) && collateOpts_.mode == other.collateOpts_.mode && +bool IndexOpts::IsEqual(const IndexOpts& other, IndexComparison cmpType) const noexcept { + return options == other.options && (cmpType == IndexComparison::SkipConfig || config == other.config) && + collateOpts_.mode == other.collateOpts_.mode && collateOpts_.sortOrderTable.GetSortOrderCharacters() == other.collateOpts_.sortOrderTable.GetSortOrderCharacters() && rtreeType_ == other.rtreeType_; } diff --git a/cpp_src/core/indexopts.h b/cpp_src/core/indexopts.h index 22d86ec92..6b6a8c299 100644 --- a/cpp_src/core/indexopts.h +++ b/cpp_src/core/indexopts.h @@ -12,6 +12,8 @@ struct CollateOpts { void Dump(T& os) const; }; +enum class IndexComparison { WithConfig, SkipConfig }; + /// Cpp version of IndexOpts: includes /// sort order table which is not possible /// to link in C-GO version because of templates @@ -37,8 +39,7 @@ struct IndexOpts { IndexOpts& SetConfig(const std::string& config); CollateMode GetCollateMode() const noexcept; - bool operator==(const IndexOpts& other) const { return IsEqual(other, false); } - bool IsEqual(const IndexOpts& other, bool skipConfig) const; + bool IsEqual(const IndexOpts& other, IndexComparison cmpType) const noexcept; template void Dump(T& os) const; @@ -60,7 +61,6 @@ T& operator<<(T& os, IndexOpts::RTreeIndexType t) { return os << "Greene"; case IndexOpts::RStar: return os << "RStar"; - default: - abort(); } + std::abort(); } diff --git a/cpp_src/core/item.cc b/cpp_src/core/item.cc index 379f441ce..c74904379 100644 --- a/cpp_src/core/item.cc +++ b/cpp_src/core/item.cc @@ -3,7 +3,6 @@ #include "core/itemimpl.h" #include "core/keyvalue/p_string.h" #include "core/namespace/namespace.h" -#include "core/rdxcontext.h" #include "tools/catch_and_return.h" namespace reindexer { @@ -96,7 +95,7 @@ Item::FieldRef &Item::FieldRef::operator=(span arr) { pl.Set(field_, pos++, Variant(elem)); } } else { - if (!itemImpl_->holder_) itemImpl_->holder_.reset(new std::deque); + if (!itemImpl_->holder_) itemImpl_->holder_ = std::make_unique>(); for (auto &elem : arr) { if constexpr (std::is_same_v) { itemImpl_->holder_->push_back(elem.toString()); diff --git a/cpp_src/core/itemimpl.cc b/cpp_src/core/itemimpl.cc index be2458028..9e0cd204d 100644 --- a/cpp_src/core/itemimpl.cc +++ b/cpp_src/core/itemimpl.cc @@ -20,7 +20,7 @@ void ItemImpl::SetField(int field, const VariantArray &krs) { !payloadType_.Field(field).Type().Is()) { VariantArray krsCopy; krsCopy.reserve(krs.size()); - if (!holder_) holder_.reset(new std::deque); + if (!holder_) holder_ = std::make_unique>(); for (auto &kr : krs) { holder_->push_back(kr.As()); krsCopy.emplace_back(p_string{&holder_->back()}); @@ -43,7 +43,8 @@ void ItemImpl::ModifyField(const IndexedTagsPath &tagsPath, const VariantArray & ser_.Reset(); ser_.PutUInt32(0); WrSerializer generatedCjson; - std::string_view cjson(pl.Get(0, 0)); + const auto cjsonV = pl.Get(0, 0); + std::string_view cjson(cjsonV); if (cjson.empty()) { buildPayloadTuple(pl, &tagsMatcher_, generatedCjson); cjson = generatedCjson.Slice(); @@ -157,10 +158,23 @@ void ItemImpl::FromCJSON(std::string_view slice, bool pkOnly, Recoder *recoder) Serializer rdser(data); Payload pl = GetPayload(); - CJsonDecoder decoder(tagsMatcher_, pkOnly ? &pkFields_ : nullptr, recoder); + if (!holder_) holder_ = std::make_unique>(); + CJsonDecoder decoder(tagsMatcher_, *holder_); + ser_.Reset(); ser_.PutUInt32(0); - decoder.Decode(pl, rdser, ser_); + if (pkOnly && !pkFields_.empty()) { + if rx_unlikely (recoder) { + throw Error(errParams, "ItemImpl::FromCJSON: pkOnly mode is not compatible with non-null recoder"); + } + decoder.Decode(pl, rdser, ser_, CJsonDecoder::RestrictingFilter(pkFields_)); + } else { + if (recoder) { + decoder.Decode(pl, rdser, ser_, CJsonDecoder::DummyFilter(), CJsonDecoder::DefaultRecoder(*recoder)); + } else { + decoder.Decode<>(pl, rdser, ser_); + } + } if (!rdser.Eof()) throw Error(errParseJson, "Internal error - left unparsed data %d", rdser.Pos()); @@ -207,7 +221,7 @@ Error ItemImpl::FromJSON(std::string_view slice, char **endp, bool pkOnly) { } // Split parsed json into indexes and tuple - JsonDecoder decoder(tagsMatcher_, pkOnly ? &pkFields_ : nullptr); + JsonDecoder decoder(tagsMatcher_, pkOnly && !pkFields_.empty() ? &pkFields_ : nullptr); Payload pl = GetPayload(); ser_.Reset(); diff --git a/cpp_src/core/itemimpl.h b/cpp_src/core/itemimpl.h index 955fd1c4d..ce3d0b10b 100644 --- a/cpp_src/core/itemimpl.h +++ b/cpp_src/core/itemimpl.h @@ -88,6 +88,7 @@ class ItemImpl : public ItemImplRawData { std::shared_ptr GetSchema() const noexcept { return schema_; } TagsMatcher &tagsMatcher() noexcept { return tagsMatcher_; } + std::shared_ptr &schema() noexcept { return schema_; } void SetPrecepts(std::vector &&precepts) { precepts_ = std::move(precepts); diff --git a/cpp_src/core/itemmodifier.cc b/cpp_src/core/itemmodifier.cc index fccc8950d..48a62e6c5 100644 --- a/cpp_src/core/itemmodifier.cc +++ b/cpp_src/core/itemmodifier.cc @@ -1,16 +1,127 @@ #include "itemmodifier.h" +#include "core/itemimpl.h" #include "core/namespace/namespaceimpl.h" #include "core/query/expressionevaluator.h" #include "core/selectfunc/functionexecutor.h" #include "index/index.h" -#include "tools/logger.h" namespace reindexer { +class ItemModifier::RollBack_ModifiedPayload : private RollBackBase { +public: + RollBack_ModifiedPayload(ItemModifier &modifier, IdType id) noexcept : modifier_{modifier}, itemId_{id} {} + RollBack_ModifiedPayload(RollBack_ModifiedPayload &&) noexcept = default; + ~RollBack_ModifiedPayload() { RollBack(); } + + void RollBack() { + if (IsDisabled()) return; + auto indexesCacheCleaner{modifier_.ns_.GetIndexesCacheCleaner()}; + const std::vector &data = modifier_.rollBackIndexData_.IndexStatus(); + PayloadValue &plValue = modifier_.ns_.items_[itemId_]; + NamespaceImpl::IndexesStorage &indexes = modifier_.ns_.indexes_; + + Payload plSave(modifier_.ns_.payloadType_, modifier_.rollBackIndexData_.GetPayloadValueBackup()); + + Payload plCur(modifier_.ns_.payloadType_, plValue); + VariantArray cjsonKref; + plCur.Get(0, cjsonKref); + + VariantArray res; + + for (size_t i = indexes.firstCompositePos(); i < size_t(indexes.totalSize()) && i < data.size(); ++i) { + if (data[i]) { + bool needClearCache{false}; + indexes[i]->Delete(Variant(plValue), itemId_, *modifier_.ns_.strHolder(), needClearCache); + if (needClearCache && indexes[i]->IsOrdered()) { + indexesCacheCleaner.Add(indexes[i]->SortId()); + } + } + } + + for (size_t i = 1; i < data.size() && i < size_t(indexes.firstCompositePos()); i++) { + if (data[i]) { + bool needClearCache{false}; + ConstPayload cpl = ConstPayload(modifier_.ns_.payloadType_, plValue); + VariantArray vals; + if (indexes[i]->Opts().IsSparse()) { + try { + cpl.GetByJsonPath(indexes[i]->Fields().getTagsPath(0), vals, indexes[i]->KeyType()); + } catch (const Error &) { + vals.resize(0); + } + modifier_.rollBackIndexData_.CjsonChanged(); + } else { + cpl.Get(i, vals); + } + indexes[i]->Delete(vals, itemId_, *modifier_.ns_.strHolder(), needClearCache); + if (needClearCache && indexes[i]->IsOrdered()) { + indexesCacheCleaner.Add(indexes[i]->SortId()); + } + + VariantArray oldData; + if (indexes[i]->Opts().IsSparse()) { + try { + plSave.GetByJsonPath(indexes[i]->Fields().getTagsPath(0), oldData, indexes[i]->KeyType()); + } catch (const Error &) { + oldData.resize(0); + } + } else { + plSave.Get(i, oldData); + } + VariantArray result; + indexes[i]->Upsert(result, oldData, itemId_, needClearCache); + if (!indexes[i]->Opts().IsSparse()) { + Payload pl{modifier_.ns_.payloadType_, modifier_.ns_.items_[itemId_]}; + pl.Set(i, result); + } + if (needClearCache && indexes[i]->IsOrdered()) { + indexesCacheCleaner.Add(indexes[i]->SortId()); + } + } + } + if (modifier_.rollBackIndexData_.IsCjsonChanged()) { + const Variant &v = cjsonKref.front(); + bool needClearCache{false}; + indexes[0]->Delete(v, itemId_, *modifier_.ns_.strHolder(), needClearCache); + VariantArray keys; + plSave.Get(0, keys); + indexes[0]->Upsert(res, keys, itemId_, needClearCache); + plCur.Set(0, res); + } + + for (size_t i = indexes.firstCompositePos(); i < size_t(indexes.totalSize()) && i < data.size(); ++i) { + if (data[i]) { + bool needClearCache{false}; + indexes[i]->Upsert(Variant(modifier_.rollBackIndexData_.GetPayloadValueBackup()), itemId_, needClearCache); + if (needClearCache && indexes[i]->IsOrdered()) { + indexesCacheCleaner.Add(indexes[i]->SortId()); + } + } + } + } + using RollBackBase::Disable; + + RollBack_ModifiedPayload(const RollBack_ModifiedPayload &) = delete; + RollBack_ModifiedPayload operator=(const RollBack_ModifiedPayload &) = delete; + RollBack_ModifiedPayload operator=(RollBack_ModifiedPayload &&) = delete; + +private: + ItemModifier &modifier_; + IdType itemId_; +}; + ItemModifier::FieldData::FieldData(const UpdateEntry &entry, NamespaceImpl &ns) : entry_(entry), tagsPathWithLastIndex_{std::nullopt}, arrayIndex_(IndexValueType::NotSet), isIndex_(false) { - if (ns.getIndexByName(entry_.Column(), fieldIndex_)) { + if (ns.tryGetIndexByName(entry_.Column(), fieldIndex_)) { isIndex_ = true; + auto jsonPathsSize = (ns.indexes_[fieldIndex_]->Opts().IsSparse() || static_cast(fieldIndex_) >= ns.payloadType_.NumFields()) + ? ns.indexes_[fieldIndex_]->Fields().size() + : ns.payloadType_.Field(fieldIndex_).JsonPaths().size(); + + if (jsonPathsSize != 1) { + throw Error(errParams, "Ambiguity when updating field with several json paths by index name: '%s'", entry_.Column()); + } + if (!entry.IsExpression()) { const auto &fields{ns.indexes_[fieldIndex_]->Fields()}; if (fields.size() != 1) { @@ -23,11 +134,7 @@ ItemModifier::FieldData::FieldData(const UpdateEntry &entry, NamespaceImpl &ns) tagsPath_ = IndexedTagsPath{fields.getTagsPath(0)}; } } else { - const auto &fld{ns.payloadType_.Field(fieldIndex_)}; - for (const auto &jp : fld.JsonPaths()) { - tagsPath_ = ns.tagsMatcher_.path2indexedtag(jp, nullptr, true); - if (!tagsPath_.empty()) break; - } + tagsPath_ = ns.tagsMatcher_.path2indexedtag(ns.payloadType_.Field(fieldIndex_).JsonPaths()[0], nullptr, true); } if (tagsPath_.empty()) { throw Error(errParams, "Cannot find field by json: '%s'", entry_.Column()); @@ -110,7 +217,7 @@ void ItemModifier::FieldData::updateTagsPath(TagsMatcher &tm, const IndexExpress ItemModifier::ItemModifier(const std::vector &updateEntries, NamespaceImpl &ns, h_vector &replUpdates, const NsContext &ctx) - : ns_(ns), updateEntries_(updateEntries) { + : ns_(ns), updateEntries_(updateEntries), rollBackIndexData_(ns_.indexes_.totalSize()) { const auto oldTmV = ns_.tagsMatcher_.version(); for (const UpdateEntry &updateField : updateEntries_) { fieldsToModify_.emplace_back(updateField, ns_); @@ -118,11 +225,13 @@ ItemModifier::ItemModifier(const std::vector &updateEntries, Namesp ns_.replicateTmUpdateIfRequired(replUpdates, oldTmV, ctx); } -void ItemModifier::Modify(IdType itemId, const RdxContext &ctx, h_vector &replUpdates) { +[[nodiscard]] bool ItemModifier::Modify(IdType itemId, const NsContext &ctx, h_vector &replUpdates) { PayloadValue &pv = ns_.items_[itemId]; Payload pl(ns_.payloadType_, pv); pv.Clone(pl.RealSize()); + rollBackIndexData_.Reset(pv); + RollBack_ModifiedPayload rollBack = RollBack_ModifiedPayload(*this, itemId); FunctionExecutor funcExecutor(ns_, replUpdates); ExpressionEvaluator ev(ns_.payloadType_, ns_.tagsMatcher_, funcExecutor); @@ -144,17 +253,23 @@ void ItemModifier::Modify(IdType itemId, const RdxContext &ctx, h_vector &replUpdates, const RdxContext &ctx) { +void ItemModifier::modifyCJSON(IdType id, FieldData &field, VariantArray &values, h_vector &replUpdates, + const NsContext &ctx) { PayloadValue &plData = ns_.items_[id]; Payload pl(*ns_.payloadType_.get(), plData); VariantArray cjsonKref; @@ -166,7 +281,7 @@ void ItemModifier::modifyCJSON(PayloadValue &pv, IdType id, FieldData &field, Va cjsonCache_.Assign(std::string_view(p_string(v))); } - ItemImpl itemimpl(ns_.payloadType_, pv, ns_.tagsMatcher_); + ItemImpl itemimpl(ns_.payloadType_, plData, ns_.tagsMatcher_); itemimpl.ModifyField(field.tagspath(), values, field.details().Mode()); Item item = ns_.newItem(); @@ -175,10 +290,11 @@ void ItemModifier::modifyCJSON(PayloadValue &pv, IdType id, FieldData &field, Va pl.Set(0, cjsonKref); throw err; } + item.setID(id); ItemImpl *impl = item.impl_; ns_.setFieldsBasedOnPrecepts(impl, replUpdates, ctx); - ns_.updateTagsMatcherFromItem(impl, NsContext(ctx)); + ns_.updateTagsMatcherFromItem(impl, ctx); Payload plNew = impl->GetPayload(); plData.Clone(pl.RealSize()); @@ -207,6 +323,7 @@ void ItemModifier::modifyCJSON(PayloadValue &pv, IdType id, FieldData &field, Va if (!needUpdateCompIndexes[i - ns_.indexes_.firstCompositePos()]) continue; } bool needClearCache{false}; + rollBackIndexData_.IndexAndCJsonChanged(i, ns_.indexes_[i]->Opts().IsPK()); ns_.indexes_[i]->Delete(Variant(plData), id, *strHolder, needClearCache); if (needClearCache && ns_.indexes_[i]->IsOrdered()) indexesCacheCleaner.Add(ns_.indexes_[i]->SortId()); } @@ -215,6 +332,7 @@ void ItemModifier::modifyCJSON(PayloadValue &pv, IdType id, FieldData &field, Va const int borderIdx = ns_.indexes_.totalSize() > 1 ? 1 : 0; int fieldIdx = borderIdx; do { + // update the indexes, and then tuple (1,2,...,0) fieldIdx %= ns_.indexes_.firstCompositePos(); Index &index = *(ns_.indexes_[fieldIdx]); bool isIndexSparse = index.Opts().IsSparse(); @@ -237,6 +355,7 @@ void ItemModifier::modifyCJSON(PayloadValue &pv, IdType id, FieldData &field, Va if ((fieldIdx == 0) && (cjsonCache_.Size() > 0)) { bool needClearCache{false}; + rollBackIndexData_.CjsonChanged(); index.Delete(Variant(cjsonCache_.Get()), id, *strHolder, needClearCache); if (needClearCache && index.IsOrdered()) indexesCacheCleaner.Add(index.SortId()); } else { @@ -251,12 +370,14 @@ void ItemModifier::modifyCJSON(PayloadValue &pv, IdType id, FieldData &field, Va } if (ns_.krefs == ns_.skrefs) continue; bool needClearCache{false}; + rollBackIndexData_.IndexChanged(fieldIdx, index.Opts().IsPK()); index.Delete(ns_.krefs, id, *strHolder, needClearCache); if (needClearCache && index.IsOrdered()) indexesCacheCleaner.Add(index.SortId()); } ns_.krefs.resize(0); bool needClearCache{false}; + rollBackIndexData_.IndexChanged(fieldIdx, index.Opts().IsPK()); index.Upsert(ns_.krefs, ns_.skrefs, id, needClearCache); if (needClearCache && index.IsOrdered()) indexesCacheCleaner.Add(index.SortId()); @@ -268,11 +389,11 @@ void ItemModifier::modifyCJSON(PayloadValue &pv, IdType id, FieldData &field, Va for (int i = ns_.indexes_.firstCompositePos(); i < ns_.indexes_.totalSize(); ++i) { if (!needUpdateCompIndexes[i - ns_.indexes_.firstCompositePos()]) continue; bool needClearCache{false}; - ns_.indexes_[i]->Upsert(Variant(pv), id, needClearCache); + ns_.indexes_[i]->Upsert(Variant(plData), id, needClearCache); if (needClearCache && ns_.indexes_[i]->IsOrdered()) indexesCacheCleaner.Add(ns_.indexes_[i]->SortId()); } - impl->RealValue() = pv; + impl->RealValue() = plData; } void ItemModifier::modifyField(IdType itemId, FieldData &field, Payload &pl, VariantArray &values) { @@ -317,6 +438,7 @@ void ItemModifier::modifyField(IdType itemId, FieldData &field, Payload &pl, Var if (!needUpdateCompIndexes[idxId]) continue; } bool needClearCache{false}; + rollBackIndexData_.IndexAndCJsonChanged(i, compositeIdx->Opts().IsPK()); compositeIdx->Delete(Variant(ns_.items_[itemId]), itemId, *strHolder, needClearCache); if (needClearCache && compositeIdx->IsOrdered()) indexesCacheCleaner.Add(compositeIdx->SortId()); } @@ -326,6 +448,7 @@ void ItemModifier::modifyField(IdType itemId, FieldData &field, Payload &pl, Var if (!needUpdateCompIndexes[i - firstCompositePos]) continue; bool needClearCache{false}; auto &compositeIdx = ns_.indexes_[i]; + rollBackIndexData_.IndexChanged(i, compositeIdx->Opts().IsPK()); compositeIdx->Upsert(Variant(ns_.items_[itemId]), itemId, needClearCache); if (needClearCache && compositeIdx->IsOrdered()) indexesCacheCleaner.Add(compositeIdx->SortId()); } @@ -353,7 +476,6 @@ void ItemModifier::modifyField(IdType itemId, FieldData &field, Payload &pl, Var tupleValue = tupleIdx->Upsert(item.GetField(0), itemId, needClearCache); if (needClearCache && tupleIdx->IsOrdered()) indexesCacheCleaner.Add(tupleIdx->SortId()); pl.Set(0, std::move(tupleValue)); - ns_.tagsMatcher_.try_merge(item.tagsMatcher()); if (exception) { std::rethrow_exception(exception); } @@ -430,11 +552,13 @@ void ItemModifier::modifyIndexValues(IdType itemId, const FieldData &field, Vari if (!ns_.skrefs.empty()) { bool needClearCache{false}; + rollBackIndexData_.IndexChanged(field.index(), index.Opts().IsPK()); index.Delete(ns_.skrefs.front(), itemId, *strHolder, needClearCache); if (needClearCache && index.IsOrdered()) indexesCacheCleaner.Add(index.SortId()); } bool needClearCache{false}; + rollBackIndexData_.IndexChanged(field.index(), index.Opts().IsPK()); index.Upsert(ns_.krefs, values, itemId, needClearCache); if (needClearCache && index.IsOrdered()) indexesCacheCleaner.Add(index.SortId()); @@ -459,14 +583,36 @@ void ItemModifier::modifyIndexValues(IdType itemId, const FieldData &field, Vari } else { pl.Get(field.index(), ns_.skrefs, true); } + + // Required when updating index array field with several tagpaths + VariantArray concatValues; + int offset = -1, length = -1; + pl.GetIndexedArrayData(field.tagspathWithLastIndex(), field.index(), offset, length); + const bool kConcatIndexValues = + index.Opts().IsArray() && !updateArrayPart && + (length < int(ns_.skrefs.size())); // (length < int(ns_.skrefs.size()) - condition to avoid coping + // when length and ns_.skrefs.size() are equal; then concatValues == values + if (kConcatIndexValues) { + if (offset < 0 || length < 0) { + concatValues = ns_.skrefs; + concatValues.insert(concatValues.end(), values.begin(), values.end()); + } else { + concatValues.insert(concatValues.end(), ns_.skrefs.begin(), ns_.skrefs.begin() + offset); + concatValues.insert(concatValues.end(), values.begin(), values.end()); + concatValues.insert(concatValues.end(), ns_.skrefs.begin() + offset + length, ns_.skrefs.end()); + } + } + if (!ns_.skrefs.empty()) { bool needClearCache{false}; + rollBackIndexData_.IndexChanged(field.index(), index.Opts().IsPK()); index.Delete(ns_.skrefs, itemId, *strHolder, needClearCache); if (needClearCache && index.IsOrdered()) indexesCacheCleaner.Add(index.SortId()); } bool needClearCache{false}; - index.Upsert(ns_.krefs, values, itemId, needClearCache); + rollBackIndexData_.IndexChanged(field.index(), index.Opts().IsPK()); + index.Upsert(ns_.krefs, kConcatIndexValues ? concatValues : values, itemId, needClearCache); if (needClearCache && index.IsOrdered()) indexesCacheCleaner.Add(index.SortId()); if (!index.Opts().IsSparse()) { pl.Set(field.index(), ns_.krefs); diff --git a/cpp_src/core/itemmodifier.h b/cpp_src/core/itemmodifier.h index ed0dc8a42..dc17eaa6c 100644 --- a/cpp_src/core/itemmodifier.h +++ b/cpp_src/core/itemmodifier.h @@ -20,7 +20,8 @@ class ItemModifier { ItemModifier(ItemModifier &&) = delete; ItemModifier &operator=(ItemModifier &&) = delete; - void Modify(IdType itemId, const RdxContext &ctx, h_vector &pendedRepl); + [[nodiscard]] bool Modify(IdType itemId, const NsContext &ctx, h_vector &pendedRepl); + PayloadValue &GetPayloadValueBackup() { return rollBackIndexData_.GetPayloadValueBackup(); } private: struct FieldData { @@ -64,14 +65,50 @@ class ItemModifier { }; void modifyField(IdType itemId, FieldData &field, Payload &pl, VariantArray &values); - void modifyCJSON(PayloadValue &pv, IdType itemId, FieldData &field, VariantArray &values, - h_vector &pendedRepl, const RdxContext &); + void modifyCJSON(IdType itemId, FieldData &field, VariantArray &values, h_vector &pendedRepl, + const NsContext &); void modifyIndexValues(IdType itemId, const FieldData &field, VariantArray &values, Payload &pl); NamespaceImpl &ns_; const std::vector &updateEntries_; std::vector fieldsToModify_; CJsonCache cjsonCache_; + + class RollBack_ModifiedPayload; + + class IndexRollBack { + public: + IndexRollBack(int indexCount) { data_.resize(indexCount); } + void Reset(PayloadValue &pv) { + pvSave_ = pv; + pvSave_.Clone(); + std::fill(data_.begin(), data_.end(), false); + cjsonChanged_ = false; + pkModified_ = false; + } + void IndexChanged(size_t index, bool isPk) noexcept { + data_[index] = true; + pkModified_ = pkModified_ || isPk; + } + void IndexAndCJsonChanged(size_t index, bool isPk) noexcept { + data_[index] = true; + cjsonChanged_ = true; + pkModified_ = pkModified_ || isPk; + } + void CjsonChanged() noexcept { cjsonChanged_ = true; } + PayloadValue &GetPayloadValueBackup() noexcept { return pvSave_; } + const std::vector &IndexStatus() const noexcept { return data_; } + bool IsCjsonChanged() const noexcept { return cjsonChanged_; } + bool IsPkModified() const noexcept { return pkModified_; } + + private: + std::vector data_; + PayloadValue pvSave_; + bool cjsonChanged_ = false; + bool pkModified_ = false; + }; + + IndexRollBack rollBackIndexData_; }; } // namespace reindexer diff --git a/cpp_src/core/joincache.h b/cpp_src/core/joincache.h index 8ec4030f2..974373e37 100644 --- a/cpp_src/core/joincache.h +++ b/cpp_src/core/joincache.h @@ -54,14 +54,8 @@ struct JoinCacheVal { bool inited = false; std::shared_ptr preResult; }; -typedef LRUCache MainLruCache; -class JoinCache : public MainLruCache { -public: - JoinCache() : MainLruCache(kDefaultCacheSizeLimit * 2, 2) {} - - typedef std::shared_ptr Ptr; -}; +using JoinCache = LRUCache; struct JoinCacheRes { bool haveData = false; diff --git a/cpp_src/core/key_value_type.h b/cpp_src/core/key_value_type.h index 1f44c8d73..251627268 100644 --- a/cpp_src/core/key_value_type.h +++ b/cpp_src/core/key_value_type.h @@ -83,7 +83,7 @@ class KeyValueType { Composite, Tuple, Uuid - } value_; + } value_{KVT::Undefined}; constexpr explicit KeyValueType(KVT v) noexcept : value_{v} {} [[nodiscard]] static KeyValueType fromNumber(int n) { @@ -144,8 +144,9 @@ class KeyValueType { case TAG_ARRAY: case TAG_OBJECT: case TAG_END: - throw Error(errParams, "Invalid tag type value for KeyValueType: " + std::string{TagTypeToStr(t)}); + break; } + throw Error(errParams, "Invalid tag type value for KeyValueType: " + std::string{TagTypeToStr(t)}); } template @@ -228,7 +229,7 @@ class KeyValueType { return v.value_ == value_; } [[nodiscard]] bool IsSame(KeyValueType other) const noexcept { return value_ == other.value_; } - [[nodiscard]] TagType ToTagType() const { + [[nodiscard]] TagType ToTagType() const noexcept { switch (value_) { case KVT::Int64: case KVT::Int: diff --git a/cpp_src/core/keyvalue/geometry.h b/cpp_src/core/keyvalue/geometry.h index af779eba8..ab5ec57a3 100644 --- a/cpp_src/core/keyvalue/geometry.h +++ b/cpp_src/core/keyvalue/geometry.h @@ -11,7 +11,8 @@ namespace reindexer { class Point { public: - explicit Point(double x = 0.0, double y = 0.0) : x_(x), y_(y) { + Point() noexcept : x_(0.0), y_(0.0) {} + explicit Point(double x, double y) : x_(x), y_(y) { validate(x, "x"); validate(y, "y"); } diff --git a/cpp_src/core/keyvalue/uuid.h b/cpp_src/core/keyvalue/uuid.h index a782599d3..efc7de6a8 100644 --- a/cpp_src/core/keyvalue/uuid.h +++ b/cpp_src/core/keyvalue/uuid.h @@ -89,7 +89,7 @@ class Uuid { uint64_t data_[2]; }; -inline std::ostream& operator<<(std::ostream& os, const Uuid& uuid) { return os << std::string{uuid}; } +inline std::ostream& operator<<(std::ostream& os, const Uuid& uuid) { return os << '\'' << std::string{uuid} << '\''; } } // namespace reindexer diff --git a/cpp_src/core/keyvalue/variant.cc b/cpp_src/core/keyvalue/variant.cc index c1ee01540..2e6eb87f4 100644 --- a/cpp_src/core/keyvalue/variant.cc +++ b/cpp_src/core/keyvalue/variant.cc @@ -566,8 +566,14 @@ Variant Variant::convert(KeyValueType type, const PayloadType *payloadType, cons Variant &Variant::convert(KeyValueType type, const PayloadType *payloadType, const FieldsSet *fields) & { if (isUuid()) { type.EvaluateOneOf([&](KeyValueType::Uuid) noexcept {}, [&](KeyValueType::String) { *this = Variant{std::string{Uuid{*this}}}; }, + [&](KeyValueType::Composite) { + assertrx_throw(payloadType && fields); + Variant tmp{VariantArray{std::move(*this)}}; + tmp.convertToComposite(*payloadType, *fields); + *this = std::move(tmp); + }, [type](OneOf) { + KeyValueType::Tuple, KeyValueType::Undefined, KeyValueType::Null>) { throw Error(errParams, "Can't convert Variant from type '%s' to type '%s'", KeyValueType{KeyValueType::Uuid{}}.Name(), type.Name()); }); @@ -579,12 +585,22 @@ Variant &Variant::convert(KeyValueType type, const PayloadType *payloadType, con [&](KeyValueType::Int64) { *this = Variant(As()); }, [&](KeyValueType::Double) { *this = Variant(As()); }, [&](KeyValueType::String) { *this = Variant(As()); }, [&](KeyValueType::Composite) { - if (variant_.type.Is()) { - assertrx(payloadType && fields); - convertToComposite(payloadType, fields); - } else { - throw Error(errParams, "Can't convert Variant from type '%s' to type '%s'", variant_.type.Name(), type.Name()); - } + variant_.type.EvaluateOneOf( + [&](KeyValueType::Tuple) { + assertrx(payloadType && fields); + convertToComposite(*payloadType, *fields); + }, + [](KeyValueType::Composite) noexcept {}, + [&](OneOf) { + assertrx(payloadType && fields); + Variant tmp{VariantArray{std::move(*this)}}; + tmp.convertToComposite(*payloadType, *fields); + *this = std::move(tmp); + }, + [&](OneOf) { + throw Error(errParams, "Can't convert Variant from type '%s' to type '%s'", variant_.type.Name(), type.Name()); + }); }, [&](KeyValueType::Uuid) { *this = Variant{As()}; }, [&](OneOf) { @@ -593,7 +609,7 @@ Variant &Variant::convert(KeyValueType type, const PayloadType *payloadType, con return *this; } -void Variant::convertToComposite(const PayloadType *payloadType, const FieldsSet *fields) { +void Variant::convertToComposite(const PayloadType &payloadType, const FieldsSet &fields) { assertrx(!isUuid()); assertrx(variant_.type.Is() && variant_.hold == 1); key_string val = *cast(); @@ -601,24 +617,24 @@ void Variant::convertToComposite(const PayloadType *payloadType, const FieldsSet if (variant_.hold == 1) free(); // Alloc usual payloadvalue + extra memory for hold string - auto &pv = *new (cast()) PayloadValue(payloadType->TotalSize() + val->size()); + auto &pv = *new (cast()) PayloadValue(payloadType.TotalSize() + val->size()); variant_.hold = 1; variant_.type = KeyValueType::Composite{}; // Copy serializer buffer with strings to extra payloadvalue memory - char *data = reinterpret_cast(pv.Ptr() + payloadType->TotalSize()); + char *data = reinterpret_cast(pv.Ptr() + payloadType.TotalSize()); memcpy(data, val->data(), val->size()); Serializer ser(std::string_view(data, val->size())); size_t count = ser.GetVarUint(); - if (count != fields->size()) { - throw Error(errLogic, "Invalid count of arguments for composite index, expected %d, got %d", fields->size(), count); + if (count != fields.size()) { + throw Error(errLogic, "Invalid count of arguments for composite index, expected %d, got %d", fields.size(), count); } - Payload pl(*payloadType, pv); + Payload pl(payloadType, pv); - for (auto field : *fields) { + for (auto field : fields) { if (field != IndexValueType::SetByJsonPath) { pl.Set(field, ser.GetVariant()); } else { @@ -675,14 +691,14 @@ Variant::operator const PayloadValue &() const noexcept { } template -void Variant::Dump(T &os) const { +void Variant::Dump(T &os, CheckIsStringPrintable checkPrintableString) const { if (isUuid()) { os << Uuid{*this}; } else { variant_.type.EvaluateOneOf( [&](KeyValueType::String) { p_string str(*this); - if (isPrintable(str)) { + if (checkPrintableString == CheckIsStringPrintable::No || isPrintable(str)) { os << '\'' << std::string_view(str) << '\''; } else { os << "slice{len:" << str.length() << "}"; @@ -695,23 +711,23 @@ void Variant::Dump(T &os) const { } } -template void Variant::Dump(WrSerializer &) const; -template void Variant::Dump(std::ostream &) const; -template void Variant::Dump(std::stringstream &) const; +template void Variant::Dump(WrSerializer &, CheckIsStringPrintable) const; +template void Variant::Dump(std::ostream &, CheckIsStringPrintable) const; +template void Variant::Dump(std::stringstream &, CheckIsStringPrintable) const; template -void VariantArray::Dump(T &os) const { +void VariantArray::Dump(T &os, CheckIsStringPrintable checkPrintableString) const { os << '{'; for (auto &arg : *this) { if (&arg != &at(0)) os << ", "; - arg.Dump(os); + arg.Dump(os, checkPrintableString); } os << '}'; } -template void VariantArray::Dump(WrSerializer &) const; -template void VariantArray::Dump(std::ostream &) const; -template void VariantArray::Dump(std::stringstream &) const; +template void VariantArray::Dump(WrSerializer &, CheckIsStringPrintable) const; +template void VariantArray::Dump(std::ostream &, CheckIsStringPrintable) const; +template void VariantArray::Dump(std::stringstream &, CheckIsStringPrintable) const; VariantArray::VariantArray(Point p) noexcept { emplace_back(p.X()); diff --git a/cpp_src/core/keyvalue/variant.h b/cpp_src/core/keyvalue/variant.h index 42d447816..aee77e581 100644 --- a/cpp_src/core/keyvalue/variant.h +++ b/cpp_src/core/keyvalue/variant.h @@ -3,7 +3,6 @@ #include "core/indexopts.h" #include "core/key_value_type.h" #include "estl/h_vector.h" -#include "tools/errors.h" namespace reindexer { @@ -19,6 +18,7 @@ class Point; class Uuid; enum class WithString : bool { No = false, Yes = true }; +enum class CheckIsStringPrintable : bool { No = false, Yes = true }; class Variant { friend Uuid; @@ -64,7 +64,7 @@ class Variant { if (!isUuid() && variant_.hold != 0) free(); } - Variant &operator=(Variant &&other) &noexcept { + Variant &operator=(Variant &&other) & noexcept { if (this == &other) return *this; if (isUuid()) { if (other.isUuid()) { @@ -142,11 +142,11 @@ class Variant { bool IsNullValue() const noexcept { return Type().Is(); } template - void Dump(T &os) const; + void Dump(T &os, CheckIsStringPrintable checkPrintableString = CheckIsStringPrintable::Yes) const; private: bool isUuid() const noexcept { return uuid_.isUuid != 0; } - void convertToComposite(const PayloadType *, const FieldsSet *); + void convertToComposite(const PayloadType &, const FieldsSet &); void free() noexcept; void copy(const Variant &other); template @@ -214,6 +214,8 @@ template <> std::string Variant::As() const; class VariantArray : public h_vector { + using Base = h_vector; + public: VariantArray() noexcept = default; VariantArray(const VariantArray &) = default; @@ -221,30 +223,30 @@ class VariantArray : public h_vector { VariantArray &operator=(const VariantArray &) = default; VariantArray &operator=(VariantArray &&) = default; - template - VariantArray(std::initializer_list l) { - reserve(l.size()); - for (auto &v : l) { - emplace_back(std::move(v)); - } - } explicit VariantArray(Point) noexcept; explicit operator Point() const; - VariantArray &MarkArray(bool v = true) &noexcept { + VariantArray &MarkArray(bool v = true) & noexcept { isArrayValue = v; return *this; } - VariantArray &&MarkArray(bool v = true) &&noexcept { + VariantArray &&MarkArray(bool v = true) && noexcept { isArrayValue = v; return std::move(*this); } void MarkObject() noexcept { isObjectValue = true; } - using h_vector::h_vector; - using h_vector::operator==; - using h_vector::operator!=; + using Base::Base; + using Base::operator==; + using Base::operator!=; + template + void clear() noexcept { + isArrayValue = isObjectValue = false; + Base::clear(); + } size_t Hash() const noexcept { size_t ret = this->size(); - for (size_t i = 0; i < this->size(); ++i) ret = (ret * 127) ^ this->at(i).Hash(); + for (auto &v : *this) { + ret = (ret * 127) ^ v.Hash(); + } return ret; } bool IsArrayValue() const noexcept { return isArrayValue || (!isObjectValue && size() > 1); } @@ -252,7 +254,7 @@ class VariantArray : public h_vector { bool IsNullValue() const noexcept { return size() == 1 && front().IsNullValue(); } KeyValueType ArrayType() const noexcept { return empty() ? KeyValueType::Null{} : front().Type(); } template - void Dump(T &os) const; + void Dump(T &os, CheckIsStringPrintable checkPrintableString = CheckIsStringPrintable::Yes) const; template int RelaxCompare(const VariantArray &other, const CollateOpts & = CollateOpts{}) const; void EnsureHold() { @@ -262,6 +264,15 @@ class VariantArray : public h_vector { static VariantArray Create(Ts &&...vs) { return VariantArray{Variant{std::forward(vs)}...}; } + template + static VariantArray Create(std::initializer_list vs) { + VariantArray res; + res.reserve(vs.size()); + for (auto &v : vs) { + res.emplace_back(std::move(v)); + } + return res; + } void Clear() noexcept { clear(); isArrayValue = false; diff --git a/cpp_src/core/lrucache.cc b/cpp_src/core/lrucache.cc index ecfc29ac8..9ab9316c4 100644 --- a/cpp_src/core/lrucache.cc +++ b/cpp_src/core/lrucache.cc @@ -9,7 +9,7 @@ namespace reindexer { -const int kMaxHitCountToCache = 1024; +constexpr uint32_t kMaxHitCountToCache = 1024; template typename LRUCache::Iterator LRUCache::Get(const K &key) { @@ -29,7 +29,7 @@ typename LRUCache::Iterator LRUCache::Get( it->second.lruPos = std::prev(lru_.end()); } - if (++it->second.hitCount < hitCountToCache_) { + if (++it->second.hitCount < int(hitCountToCache_)) { return Iterator(); } ++getCount_; @@ -57,10 +57,10 @@ void LRUCache::Put(const K &key, V &&v) { eraseLRU(); if rx_unlikely (putCount_ * 16 > getCount_ && eraseCount_) { - logPrintf(LogWarning, "IdSetCache::eraseLRU () cache invalidates too fast eraseCount=%d,putCount=%d,getCount=%d", eraseCount_, - putCount_, eraseCount_); + logPrintf(LogWarning, "IdSetCache::eraseLRU () cache invalidates too fast eraseCount=%d,putCount=%d,getCount=%d,hitCountToCache=%d", + eraseCount_, putCount_, eraseCount_, hitCountToCache_); eraseCount_ = 0; - hitCountToCache_ = std::min(hitCountToCache_ * 2, kMaxHitCountToCache); + hitCountToCache_ = hitCountToCache_ ? std::min(hitCountToCache_ * 2, kMaxHitCountToCache) : 2; putCount_ = 0; getCount_ = 0; } @@ -128,7 +128,7 @@ LRUCacheMemStat LRUCache::GetMemStat() { } template class LRUCache; template class LRUCache; -template class LRUCache; +template class LRUCache; template class LRUCache; } // namespace reindexer diff --git a/cpp_src/core/lrucache.h b/cpp_src/core/lrucache.h index e81fa741f..4cac5b979 100644 --- a/cpp_src/core/lrucache.h +++ b/cpp_src/core/lrucache.h @@ -4,20 +4,18 @@ #include #include #include +#include "dbconfig.h" #include "namespace/namespacestat.h" namespace reindexer { -constexpr size_t kDefaultCacheSizeLimit = 1024 * 1024 * 128; -constexpr int kDefaultHitCountToCache = 2; constexpr size_t kElemSizeOverhead = 256; template class LRUCache { public: using Key = K; - LRUCache(size_t sizeLimit = kDefaultCacheSizeLimit, int hitCount = kDefaultHitCountToCache) noexcept - : totalCacheSize_(0), cacheSizeLimit_(sizeLimit), hitCountToCache_(hitCount) {} + LRUCache(size_t sizeLimit, uint32_t hitCount) noexcept : totalCacheSize_(0), cacheSizeLimit_(sizeLimit), hitCountToCache_(hitCount) {} struct Iterator { Iterator(bool k = false, const V &v = V()) : valid(k), val(v) {} Iterator(const Iterator &other) = delete; @@ -118,7 +116,7 @@ class LRUCache { mutable std::mutex lock_; size_t totalCacheSize_; const size_t cacheSizeLimit_; - int hitCountToCache_; + uint32_t hitCountToCache_; uint64_t getCount_ = 0, putCount_ = 0, eraseCount_ = 0; }; diff --git a/cpp_src/core/namespace/asyncstorage.cc b/cpp_src/core/namespace/asyncstorage.cc index 3bd989cab..b6745a8bb 100644 --- a/cpp_src/core/namespace/asyncstorage.cc +++ b/cpp_src/core/namespace/asyncstorage.cc @@ -52,7 +52,6 @@ void AsyncStorage::Destroy() { throwOnStorageCopy(); if (storage_) { - tryReopenStorage(); clearUpdates(); storage_->Destroy(path_); reset(); diff --git a/cpp_src/core/namespace/incarnationtags.h b/cpp_src/core/namespace/incarnationtags.h new file mode 100644 index 000000000..d1c2d1333 --- /dev/null +++ b/cpp_src/core/namespace/incarnationtags.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include "estl/h_vector.h" +#include "tools/lsn.h" + +namespace reindexer { + +struct NsIncarnationTagData { + int shardId = ShardingKeyType::NotSetShard; + h_vector tags; +}; + +using NsShardsIncarnationTags = h_vector; + +} // namespace reindexer diff --git a/cpp_src/core/namespace/itemsloader.cc b/cpp_src/core/namespace/itemsloader.cc index faa3765c6..f835db592 100644 --- a/cpp_src/core/namespace/itemsloader.cc +++ b/cpp_src/core/namespace/itemsloader.cc @@ -49,7 +49,7 @@ void ItemsLoader::reading() { auto dbIter = ns_.storage_.GetCursor(opts); unsigned sliceId = 0; for (dbIter->Seek(kRxStorageItemPrefix); - dbIter->Valid() && dbIter->GetComparator().Compare(dbIter->Key(), std::string_view(kRxStorageItemPrefix "\xFF")) < 0; + dbIter->Valid() && dbIter->GetComparator().Compare(dbIter->Key(), std::string_view(kRxStorageItemPrefix "\xFF\xFF\xFF\xFF")) < 0; dbIter->Next()) { std::string_view dataSlice = dbIter->Value(); if (dataSlice.size() > 0) { diff --git a/cpp_src/core/namespace/itemsloader.h b/cpp_src/core/namespace/itemsloader.h index 0432a9310..3d39c27c8 100644 --- a/cpp_src/core/namespace/itemsloader.h +++ b/cpp_src/core/namespace/itemsloader.h @@ -1,6 +1,7 @@ #pragma once #include +#include "core/itemimpl.h" #include "namespaceimpl.h" namespace reindexer { diff --git a/cpp_src/core/namespace/namespace.cc b/cpp_src/core/namespace/namespace.cc index 087306c8c..6be89c132 100644 --- a/cpp_src/core/namespace/namespace.cc +++ b/cpp_src/core/namespace/namespace.cc @@ -1,6 +1,5 @@ #include "namespace.h" #include "core/querystat.h" -#include "core/storage/storagefactory.h" #include "snapshot/snapshothandler.h" #include "snapshot/snapshotrecord.h" #include "tools/flagguard.h" @@ -9,8 +8,6 @@ namespace reindexer { -#define handleInvalidation(Fn) nsFuncWrapper - void Namespace::CommitTransaction(LocalTransaction& tx, LocalQueryResults& result, const NsContext& ctx) { auto nsl = atomicLoadMainNs(); bool enablePerfCounters = nsl->enablePerfCounters_.load(std::memory_order_relaxed); @@ -25,7 +22,7 @@ void Namespace::CommitTransaction(LocalTransaction& tx, LocalQueryResults& resul if (needNamespaceCopy(nsl, tx)) { PerfStatCalculatorMT calc(nsl->updatePerfCounter_, enablePerfCounters); - auto clonerLck = statCalculator.CreateLock(clonerMtx_, &ctx.rdxContext); + auto clonerLck = statCalculator.CreateLock(clonerMtx_, ctx.rdxContext); nsl = ns_; if (needNamespaceCopy(nsl, tx)) { @@ -37,6 +34,8 @@ void Namespace::CommitTransaction(LocalTransaction& tx, LocalQueryResults& resul CounterGuardAIR32 cg(nsl->cancelCommitCnt_); try { auto nsRlck = statCalculator.CreateLock(*nsl, &NamespaceImpl::rLock, ctx.rdxContext); + tx.ValidatePK(nsl->pkFields()); + auto storageLock = statCalculator.CreateLock(nsl->storage_, &AsyncStorage::FullLock); cg.Reset(); @@ -62,7 +61,7 @@ void Namespace::CommitTransaction(LocalTransaction& tx, LocalQueryResults& resul hasCopy_.store(false, std::memory_order_release); if (!nsl->repl_.temporary && !nsCtx.inSnapshot) { // If commit happens in ns copy, than the copier have to handle replication - auto err = ns_->clusterizator_->Replicate( + auto err = ns_->clusterizator_.Replicate( cluster::UpdateRecord{cluster::UpdateRecord::Type::CommitTx, ns_->name_, ns_->wal_.LastLSN(), ns_->repl_.nsVersion, ctx.rdxContext.EmmiterServerId()}, [&clonerLck, &storageLock, &nsRlck]() { @@ -100,11 +99,11 @@ void Namespace::CommitTransaction(LocalTransaction& tx, LocalQueryResults& resul return; } } - handleInvalidation(NamespaceImpl::CommitTransaction)(tx, result, ctx, statCalculator); + nsFuncWrapper<&NamespaceImpl::CommitTransaction>(tx, result, ctx, statCalculator); } NamespacePerfStat Namespace::GetPerfStat(const RdxContext& ctx) { - NamespacePerfStat stats = handleInvalidation(NamespaceImpl::GetPerfStat)(ctx); + NamespacePerfStat stats = nsFuncWrapper<&NamespaceImpl::GetPerfStat>(ctx); stats.transactions = txStatsCounter_.Get(); auto copyStats = copyStatsCounter_.Get(); stats.transactions.totalCopyCount = copyStats.totalHitCount; @@ -121,7 +120,7 @@ NamespacePerfStat Namespace::GetPerfStat(const RdxContext& ctx) { void Namespace::ApplySnapshotChunk(const SnapshotChunk& ch, bool isInitialLeaderSync, const RdxContext& ctx) { if (!ch.IsTx() || ch.IsShallow() || !ch.IsWAL()) { - return handleInvalidation(NamespaceImpl::ApplySnapshotChunk)(ch, isInitialLeaderSync, ctx); + return nsFuncWrapper<&NamespaceImpl::ApplySnapshotChunk>(ch, isInitialLeaderSync, ctx); } else { SnapshotTxHandler handler(*this); handler.ApplyChunk(ch, isInitialLeaderSync, ctx); @@ -142,11 +141,14 @@ void Namespace::doRename(const Namespace::Ptr& dst, const std::string& newName, logPrintf(LogTrace, "[rename] Trying to rename namespace '%s'...", GetName(ctx)); std::string dbpath; const auto flushOpts = StorageFlushOpts().WithImmediateReopen(); - awaitMainNs(ctx)->storage_.Flush(flushOpts); - auto lck = handleInvalidation(NamespaceImpl::dataWLock)(ctx, true); - auto& srcNs = *atomicLoadMainNs(); // -V758 - srcNs.storage_.Flush(flushOpts); // Repeat flush, to raise any disk errors before attempt to close storage - logPrintf(LogTrace, "[rename] All the flushes done for '%s'...", srcNs.name_); + auto lck = nsFuncWrapper<&NamespaceImpl::dataWLock>(ctx, true); + auto srcNsPtr = atomicLoadMainNs(); + auto& srcNs = *srcNsPtr; + srcNs.storage_.Flush(flushOpts); // Repeat flush, to raise any disk errors before attempt to close storage + auto storageStatus = srcNs.storage_.GetStatusCached(); + if (!storageStatus.err.ok()) { + throw Error(storageStatus.err.code(), "Unable to flush storage before rename: %s", storageStatus.err.what()); + } NamespaceImpl::Locker::WLockT dstLck; NamespaceImpl::Ptr dstNs; if (dst) { diff --git a/cpp_src/core/namespace/namespace.h b/cpp_src/core/namespace/namespace.h index b3cbe1f56..ff2a6bbd5 100644 --- a/cpp_src/core/namespace/namespace.h +++ b/cpp_src/core/namespace/namespace.h @@ -6,112 +6,176 @@ #include "core/queryresults/queryresults.h" #include "core/querystat.h" #include "core/transaction/txstats.h" -#include "estl/shared_mutex.h" #include "namespaceimpl.h" #include "tools/flagguard.h" namespace reindexer { -#define handleInvalidation(Fn) nsFuncWrapper - class Namespace { + template + auto nsFuncWrapper(Args &&...args) const { + while (true) { + try { + auto ns = atomicLoadMainNs(); + return (*ns.*fn)(std::forward(args)...); + } catch (const Error &e) { + if (e.code() != errNamespaceInvalidated) { + throw; + } else { + std::this_thread::yield(); + } + } + } + } + + template + void nsFuncWrapper(Item &item, LocalQueryResults &qr, const RdxContext &ctx) const { + nsFuncWrapper(item, qr, ctx); + } + template + void nsFuncWrapper(const Query &query, LocalQueryResults &qr, const RdxContext &ctx) const { + nsFuncWrapper(query, qr, ctx); + } + template + void nsFuncWrapper(T &v, LocalQueryResults &qr, const RdxContext &ctx) const { + NsContext nsCtx(ctx); + while (true) { + NamespaceImpl::Ptr ns; + bool added = false; + try { + ns = atomicLoadMainNs(); + + PerfStatCalculatorMT calc(ns->updatePerfCounter_, ns->enablePerfCounters_); + NamespaceImpl::UpdatesContainer pendedRepl; + + CounterGuardAIR32 cg(ns->cancelCommitCnt_); + if constexpr (std::is_same_v) { + auto wlck = ns->dataWLock(nsCtx.rdxContext); + cg.Reset(); + qr.AddNamespace(ns, true); + added = true; + (*ns.*fn)(v, enumVal, pendedRepl, nsCtx); + qr.AddItem(v, true, false); + ns->replicate(std::move(pendedRepl), std::move(wlck), true, nullptr, nsCtx); + } else { + auto params = longUpdDelLoggingParams_.load(std::memory_order_relaxed); + const bool isEnabled = params.thresholdUs >= 0 && !isSystemNamespaceNameFast(v.NsName()); + auto statCalculator = QueryStatCalculator(long_actions::MakeLogger(v, std::move(params)), isEnabled); + auto wlck = statCalculator.CreateLock(*ns, &NamespaceImpl::dataWLock, nsCtx.rdxContext, false); + cg.Reset(); + qr.AddNamespace(ns, true); + added = true; + (*ns.*fn)(v, qr, pendedRepl, nsCtx); + ns->replicate(std::move(pendedRepl), std::move(wlck), true, statCalculator, nsCtx); + } + return; + } catch (const Error &e) { + if (e.code() != errNamespaceInvalidated) { + throw; + } else { + if (added) qr.RemoveNamespace(ns.get()); + std::this_thread::yield(); + } + } + } + } + public: using Ptr = shared_ptr; - Namespace(const std::string &name, std::optional stateToken, cluster::INsDataReplicator *clusterizator, +#ifdef REINDEX_WITH_V3_FOLLOWERS + Namespace(const std::string &name, std::optional stateToken, cluster::INsDataReplicator &clusterizator, + BackgroundNamespaceDeleter &bgDeleter, UpdatesObservers &observers) + : ns_(make_intrusive(name, std::move(stateToken), clusterizator, observers)), bgDeleter_(bgDeleter) {} +#else // REINDEX_WITH_V3_FOLLOWERS + Namespace(const std::string &name, std::optional stateToken, cluster::INsDataReplicator &clusterizator, BackgroundNamespaceDeleter &bgDeleter) : ns_(make_intrusive(name, std::move(stateToken), clusterizator)), bgDeleter_(bgDeleter) {} +#endif // REINDEX_WITH_V3_FOLLOWERS void CommitTransaction(LocalTransaction &tx, LocalQueryResults &result, const NsContext &ctx); - std::string GetName(const RdxContext &ctx) const { return handleInvalidation(NamespaceImpl::GetName)(ctx); } - bool IsSystem(const RdxContext &ctx) const { return handleInvalidation(NamespaceImpl::IsSystem)(ctx); } - bool IsTemporary(const RdxContext &ctx) const { return handleInvalidation(NamespaceImpl::IsTemporary)(ctx); } - void SetNsVersion(lsn_t version, const RdxContext &ctx) { handleInvalidation(NamespaceImpl::SetNsVersion)(version, ctx); } + std::string GetName(const RdxContext &ctx) const { return nsFuncWrapper<&NamespaceImpl::GetName>(ctx); } + bool IsSystem(const RdxContext &ctx) const { return nsFuncWrapper<&NamespaceImpl::IsSystem>(ctx); } + bool IsTemporary(const RdxContext &ctx) const { return nsFuncWrapper<&NamespaceImpl::IsTemporary>(ctx); } + void SetNsVersion(lsn_t version, const RdxContext &ctx) { nsFuncWrapper<&NamespaceImpl::SetNsVersion>(version, ctx); } void EnableStorage(const std::string &path, StorageOpts opts, StorageType storageType, const RdxContext &ctx) { - handleInvalidation(NamespaceImpl::EnableStorage)(path, opts, storageType, ctx); + nsFuncWrapper<&NamespaceImpl::EnableStorage>(path, opts, storageType, ctx); } void LoadFromStorage(unsigned threadsCount, const RdxContext &ctx) { - handleInvalidation(NamespaceImpl::LoadFromStorage)(threadsCount, ctx); - } - void DeleteStorage(const RdxContext &ctx) { handleInvalidation(NamespaceImpl::DeleteStorage)(ctx); } - uint32_t GetItemsCount() { return handleInvalidation(NamespaceImpl::GetItemsCount)(); } - void AddIndex(const IndexDef &indexDef, const RdxContext &ctx) { handleInvalidation(NamespaceImpl::AddIndex)(indexDef, ctx); } - void UpdateIndex(const IndexDef &indexDef, const RdxContext &ctx) { handleInvalidation(NamespaceImpl::UpdateIndex)(indexDef, ctx); } - void DropIndex(const IndexDef &indexDef, const RdxContext &ctx) { handleInvalidation(NamespaceImpl::DropIndex)(indexDef, ctx); } - void SetSchema(std::string_view schema, const RdxContext &ctx) { handleInvalidation(NamespaceImpl::SetSchema)(schema, ctx); } - std::string GetSchema(int format, const RdxContext &ctx) { return handleInvalidation(NamespaceImpl::GetSchema)(format, ctx); } - std::shared_ptr GetSchemaPtr(const RdxContext &ctx) { return handleInvalidation(NamespaceImpl::GetSchemaPtr)(ctx); } - void Insert(Item &item, const RdxContext &ctx) { handleInvalidation(NamespaceImpl::Insert)(item, ctx); } + nsFuncWrapper<&NamespaceImpl::LoadFromStorage>(threadsCount, ctx); + } + void DeleteStorage(const RdxContext &ctx) { nsFuncWrapper<&NamespaceImpl::DeleteStorage>(ctx); } + uint32_t GetItemsCount() { return nsFuncWrapper<&NamespaceImpl::GetItemsCount>(); } + void AddIndex(const IndexDef &indexDef, const RdxContext &ctx) { nsFuncWrapper<&NamespaceImpl::AddIndex>(indexDef, ctx); } + void UpdateIndex(const IndexDef &indexDef, const RdxContext &ctx) { nsFuncWrapper<&NamespaceImpl::UpdateIndex>(indexDef, ctx); } + void DropIndex(const IndexDef &indexDef, const RdxContext &ctx) { nsFuncWrapper<&NamespaceImpl::DropIndex>(indexDef, ctx); } + void SetSchema(std::string_view schema, const RdxContext &ctx) { nsFuncWrapper<&NamespaceImpl::SetSchema>(schema, ctx); } + std::string GetSchema(int format, const RdxContext &ctx) { return nsFuncWrapper<&NamespaceImpl::GetSchema>(format, ctx); } + std::shared_ptr GetSchemaPtr(const RdxContext &ctx) { return nsFuncWrapper<&NamespaceImpl::GetSchemaPtr>(ctx); } + void Insert(Item &item, const RdxContext &ctx) { nsFuncWrapper<&NamespaceImpl::Insert>(item, ctx); } void Insert(Item &item, LocalQueryResults &qr, const RdxContext &ctx) { nsFuncWrapper<&NamespaceImpl::modifyItem, ModeInsert>(item, qr, ctx); } - void Update(Item &item, const RdxContext &ctx) { - nsFuncWrapper(item, ctx); - } + void Update(Item &item, const RdxContext &ctx) { nsFuncWrapper<&NamespaceImpl::Update>(item, ctx); } void Update(Item &item, LocalQueryResults &qr, const RdxContext &ctx) { nsFuncWrapper<&NamespaceImpl::modifyItem, ItemModifyMode::ModeUpdate>(item, qr, ctx); } void Update(const Query &query, LocalQueryResults &result, const RdxContext &ctx) { nsFuncWrapper<&NamespaceImpl::doUpdate, QueryType::QueryUpdate>(query, result, ctx); } - void Upsert(Item &item, const RdxContext &ctx) { handleInvalidation(NamespaceImpl::Upsert)(item, ctx); } + void Upsert(Item &item, const RdxContext &ctx) { nsFuncWrapper<&NamespaceImpl::Upsert>(item, ctx); } void Upsert(Item &item, LocalQueryResults &qr, const RdxContext &ctx) { nsFuncWrapper<&NamespaceImpl::modifyItem, ItemModifyMode::ModeUpsert>(item, qr, ctx); } - void Delete(Item &item, const RdxContext &ctx) { - nsFuncWrapper(item, ctx); - } + void Delete(Item &item, const RdxContext &ctx) { nsFuncWrapper<&NamespaceImpl::Delete>(item, ctx); } void Delete(Item &item, LocalQueryResults &qr, const RdxContext &ctx) { nsFuncWrapper<&NamespaceImpl::modifyItem, ItemModifyMode::ModeDelete>(item, qr, ctx); } void Delete(const Query &query, LocalQueryResults &result, const RdxContext &ctx) { nsFuncWrapper<&NamespaceImpl::doDelete, QueryType::QueryDelete>(query, result, ctx); } - void Truncate(const RdxContext &ctx) { handleInvalidation(NamespaceImpl::Truncate)(ctx); } + void Truncate(const RdxContext &ctx) { nsFuncWrapper<&NamespaceImpl::Truncate>(ctx); } void Select(LocalQueryResults &result, SelectCtx ¶ms, const RdxContext &ctx) { - handleInvalidation(NamespaceImpl::Select)(result, params, ctx); + nsFuncWrapper<&NamespaceImpl::Select>(result, params, ctx); } - NamespaceDef GetDefinition(const RdxContext &ctx) { return handleInvalidation(NamespaceImpl::GetDefinition)(ctx); } - NamespaceMemStat GetMemStat(const RdxContext &ctx) { return handleInvalidation(NamespaceImpl::GetMemStat)(ctx); } + NamespaceDef GetDefinition(const RdxContext &ctx) { return nsFuncWrapper<&NamespaceImpl::GetDefinition>(ctx); } + NamespaceMemStat GetMemStat(const RdxContext &ctx) { return nsFuncWrapper<&NamespaceImpl::GetMemStat>(ctx); } NamespacePerfStat GetPerfStat(const RdxContext &ctx); void ResetPerfStat(const RdxContext &ctx) { txStatsCounter_.Reset(); commitStatsCounter_.Reset(); copyStatsCounter_.Reset(); - handleInvalidation(NamespaceImpl::ResetPerfStat)(ctx); + nsFuncWrapper<&NamespaceImpl::ResetPerfStat>(ctx); } - std::vector EnumMeta(const RdxContext &ctx) { return handleInvalidation(NamespaceImpl::EnumMeta)(ctx); } + std::vector EnumMeta(const RdxContext &ctx) { return nsFuncWrapper<&NamespaceImpl::EnumMeta>(ctx); } void BackgroundRoutine(RdxActivityContext *ctx) { if (hasCopy_.load(std::memory_order_acquire)) { return; } - handleInvalidation(NamespaceImpl::BackgroundRoutine)(ctx); + nsFuncWrapper<&NamespaceImpl::BackgroundRoutine>(ctx); } void StorageFlushingRoutine() { if (hasCopy_.load(std::memory_order_acquire)) { return; } - handleInvalidation(NamespaceImpl::StorageFlushingRoutine)(); + nsFuncWrapper<&NamespaceImpl::StorageFlushingRoutine>(); } - void CloseStorage(const RdxContext &ctx) { handleInvalidation(NamespaceImpl::CloseStorage)(ctx); } - LocalTransaction NewTransaction(const RdxContext &ctx) { return handleInvalidation(NamespaceImpl::NewTransaction)(ctx); } + void CloseStorage(const RdxContext &ctx) { nsFuncWrapper<&NamespaceImpl::CloseStorage>(ctx); } + LocalTransaction NewTransaction(const RdxContext &ctx) { return nsFuncWrapper<&NamespaceImpl::NewTransaction>(ctx); } - Item NewItem(const RdxContext &ctx) { return handleInvalidation(NamespaceImpl::NewItem)(ctx); } - void ToPool(ItemImpl *item) { handleInvalidation(NamespaceImpl::ToPool)(item); } - std::string GetMeta(const std::string &key, const RdxContext &ctx) { return handleInvalidation(NamespaceImpl::GetMeta)(key, ctx); } + Item NewItem(const RdxContext &ctx) { return nsFuncWrapper<&NamespaceImpl::NewItem>(ctx); } + void ToPool(ItemImpl *item) { nsFuncWrapper<&NamespaceImpl::ToPool>(item); } + std::string GetMeta(const std::string &key, const RdxContext &ctx) { return nsFuncWrapper<&NamespaceImpl::GetMeta>(key, ctx); } void PutMeta(const std::string &key, std::string_view data, const RdxContext &ctx) { - handleInvalidation(NamespaceImpl::PutMeta)(key, data, ctx); - } - int getIndexByName(std::string_view index) const { - return nsFuncWrapper(index); - } - bool getIndexByName(std::string_view name, int &index) const { - return nsFuncWrapper(name, index); - } - void FillResult(LocalQueryResults &result, const IdSet &ids) const { handleInvalidation(NamespaceImpl::FillResult)(result, ids); } - void EnablePerfCounters(bool enable = true) { handleInvalidation(NamespaceImpl::EnablePerfCounters)(enable); } - ReplicationState GetReplState(const RdxContext &ctx) const { return handleInvalidation(NamespaceImpl::GetReplState)(ctx); } - ReplicationStateV2 GetReplStateV2(const RdxContext &ctx) const { return handleInvalidation(NamespaceImpl::GetReplStateV2)(ctx); } + nsFuncWrapper<&NamespaceImpl::PutMeta>(key, data, ctx); + } + int getIndexByName(std::string_view index) const { return nsFuncWrapper<&NamespaceImpl::getIndexByName>(index); } + bool getIndexByName(std::string_view name, int &index) const { return nsFuncWrapper<&NamespaceImpl::tryGetIndexByName>(name, index); } + void FillResult(LocalQueryResults &result, const IdSet &ids) const { nsFuncWrapper<&NamespaceImpl::FillResult>(result, ids); } + void EnablePerfCounters(bool enable = true) { nsFuncWrapper<&NamespaceImpl::EnablePerfCounters>(enable); } + ReplicationState GetReplState(const RdxContext &ctx) const { return nsFuncWrapper<&NamespaceImpl::GetReplState>(ctx); } + ReplicationStateV2 GetReplStateV2(const RdxContext &ctx) const { return nsFuncWrapper<&NamespaceImpl::GetReplStateV2>(ctx); } void Rename(const Namespace::Ptr &dst, const std::string &storagePath, const std::function)> &replicateCb, const RdxContext &ctx) { if (this == dst.get() || dst == nullptr) { @@ -128,128 +192,57 @@ class Namespace { } void OnConfigUpdated(DBConfigProvider &configProvider, const RdxContext &ctx) { NamespaceConfigData configData; - configProvider.GetNamespaceConfig(GetName(ctx), configData); + const auto nsName = GetName(ctx); + std::string_view realNsName(nsName); + if (isTmpNamespaceNameFast(nsName)) { + realNsName = demangleTmpNamespaceName(realNsName); + } + configProvider.GetNamespaceConfig(realNsName, configData); startCopyPolicyTxSize_.store(configData.startCopyPolicyTxSize, std::memory_order_relaxed); copyPolicyMultiplier_.store(configData.copyPolicyMultiplier, std::memory_order_relaxed); txSizeToAlwaysCopy_.store(configData.txSizeToAlwaysCopy, std::memory_order_relaxed); longTxLoggingParams_.store(configProvider.GetTxLoggingParams(), std::memory_order_relaxed); longUpdDelLoggingParams_.store(configProvider.GetUpdDelLoggingParams(), std::memory_order_relaxed); - handleInvalidation(NamespaceImpl::OnConfigUpdated)(configProvider, ctx); + nsFuncWrapper<&NamespaceImpl::OnConfigUpdated>(configProvider, ctx); } - StorageOpts GetStorageOpts(const RdxContext &ctx) { return handleInvalidation(NamespaceImpl::GetStorageOpts)(ctx); } - void Refill(std::vector &items, const RdxContext &ctx) { handleInvalidation(NamespaceImpl::Refill)(items, ctx); } + StorageOpts GetStorageOpts(const RdxContext &ctx) { return nsFuncWrapper<&NamespaceImpl::GetStorageOpts>(ctx); } + void Refill(std::vector &items, const RdxContext &ctx) { nsFuncWrapper<&NamespaceImpl::Refill>(items, ctx); } Error SetClusterizationStatus(ClusterizationStatus &&status, const RdxContext &ctx) { - return handleInvalidation(NamespaceImpl::SetClusterizationStatus)(std::move(status), ctx); + return nsFuncWrapper<&NamespaceImpl::SetClusterizationStatus>(std::move(status), ctx); } void GetSnapshot(Snapshot &snapshot, const SnapshotOpts &opts, const RdxContext &ctx) { - return handleInvalidation(NamespaceImpl::GetSnapshot)(snapshot, opts, ctx); + return nsFuncWrapper<&NamespaceImpl::GetSnapshot>(snapshot, opts, ctx); } void ApplySnapshotChunk(const SnapshotChunk &ch, bool isInitialLeaderSync, const RdxContext &ctx); void SetTagsMatcher(TagsMatcher &&tm, const RdxContext &ctx) { - return handleInvalidation(NamespaceImpl::SetTagsMatcher)(std::move(tm), ctx); + return nsFuncWrapper<&NamespaceImpl::SetTagsMatcher>(std::move(tm), ctx); } - std::set GetFTIndexes(const RdxContext &ctx) const { - return nsFuncWrapper (NamespaceImpl::*)(const RdxContext &) const, &NamespaceImpl::GetFTIndexes>(ctx); - } + std::set GetFTIndexes(const RdxContext &ctx) const { return nsFuncWrapper<&NamespaceImpl::GetFTIndexes>(ctx); } void DumpIndex(std::ostream &os, std::string_view index, const RdxContext &ctx) { - return handleInvalidation(NamespaceImpl::DumpIndex)(os, index, ctx); + return nsFuncWrapper<&NamespaceImpl::DumpIndex>(os, index, ctx); } - void SetDestroyFlag() { return handleInvalidation(NamespaceImpl::SetDestroyFlag)(); } + void SetDestroyFlag() { return nsFuncWrapper<&NamespaceImpl::SetDestroyFlag>(); } protected: friend class ReindexerImpl; friend class LocalQueryResults; friend class ShardingProxy; - void updateSelectTime() const { handleInvalidation(NamespaceImpl::updateSelectTime)(); } + void updateSelectTime() const { nsFuncWrapper<&NamespaceImpl::updateSelectTime>(); } NamespaceImpl::Ptr getMainNs() const { return atomicLoadMainNs(); } NamespaceImpl::Ptr awaitMainNs(const RdxContext &ctx) const { if (hasCopy_.load(std::memory_order_acquire)) { - contexted_unique_lock lck(clonerMtx_, &ctx); + contexted_unique_lock lck(clonerMtx_, ctx); assertrx(!hasCopy_.load(std::memory_order_acquire)); return ns_; } return atomicLoadMainNs(); } - PayloadType getPayloadType(const RdxContext &ctx) const { - return nsFuncWrapper(ctx); - } + PayloadType getPayloadType(const RdxContext &ctx) const { return nsFuncWrapper<&NamespaceImpl::getPayloadType>(ctx); } private: - template - typename std::invoke_result::type nsFuncWrapper(Args &&...args) const { - while (true) { - try { - auto ns = atomicLoadMainNs(); - return (*ns.*fn)(std::forward(args)...); - } catch (const Error &e) { - if (e.code() != errNamespaceInvalidated) { - throw; - } else { - std::this_thread::yield(); - } - } - } - } - - template - void nsFuncWrapper(Item &item, LocalQueryResults &qr, const RdxContext &ctx) const { - nsFuncWrapper(item, qr, ctx); - } - template - void nsFuncWrapper(const Query &query, LocalQueryResults &qr, const RdxContext &ctx) const { - nsFuncWrapper(query, qr, ctx); - } - template , ItemModifyMode, QueryType> enumVal> - void nsFuncWrapper(T &v, LocalQueryResults &qr, const RdxContext &ctx) const { - NsContext nsCtx(ctx); - while (true) { - NamespaceImpl::Ptr ns; - bool added = false; - try { - ns = atomicLoadMainNs(); - - PerfStatCalculatorMT calc(ns->updatePerfCounter_, ns->enablePerfCounters_); - NamespaceImpl::UpdatesContainer pendedRepl; - - CounterGuardAIR32 cg(ns->cancelCommitCnt_); - if constexpr (std::is_same_v) { - auto wlck = ns->dataWLock(nsCtx.rdxContext); - cg.Reset(); - qr.AddNamespace(ns, true); - added = true; - (*ns.*fn)(v, enumVal, pendedRepl, nsCtx); - qr.AddItem(v, true, false); - ns->replicate(std::move(pendedRepl), std::move(wlck), true, nullptr, nsCtx); - } else { - auto params = longUpdDelLoggingParams_.load(std::memory_order_relaxed); - const bool isEnabled = params.thresholdUs >= 0 && !isSystemNamespaceNameFast(v._namespace); - auto statCalculator = QueryStatCalculator(long_actions::MakeLogger(v, std::move(params)), isEnabled); - auto wlck = statCalculator.CreateLock(*ns, &NamespaceImpl::dataWLock, nsCtx.rdxContext, false); - cg.Reset(); - qr.AddNamespace(ns, true); - added = true; - (*ns.*fn)(v, qr, pendedRepl, nsCtx); - ns->replicate(std::move(pendedRepl), std::move(wlck), true, statCalculator, nsCtx); - } - return; - } catch (const Error &e) { - if (e.code() != errNamespaceInvalidated) { - throw; - } else { - if (added) qr.RemoveNamespace(ns.get()); - std::this_thread::yield(); - } - } - } - } - bool needNamespaceCopy(const NamespaceImpl::Ptr &ns, const LocalTransaction &tx) const noexcept; void doRename(const Namespace::Ptr &dst, const std::string &newName, const std::string &storagePath, const std::function)> &replicateCb, const RdxContext &ctx); @@ -279,6 +272,4 @@ class Namespace { BackgroundNamespaceDeleter &bgDeleter_; }; -#undef handleInvalidation - } // namespace reindexer diff --git a/cpp_src/core/namespace/namespaceimpl.cc b/cpp_src/core/namespace/namespaceimpl.cc index 2f6b3d3a4..e3e12b030 100644 --- a/cpp_src/core/namespace/namespaceimpl.cc +++ b/cpp_src/core/namespace/namespaceimpl.cc @@ -11,6 +11,7 @@ #include "core/index/ttlindex.h" #include "core/itemimpl.h" #include "core/itemmodifier.h" +#include "core/nsselecter/crashqueryreporter.h" #include "core/nsselecter/nsselecter.h" #include "core/payload/payloadiface.h" #include "core/querystat.h" @@ -27,6 +28,10 @@ #include "tools/timetools.h" #include "wal/walselecter.h" +#ifdef REINDEX_WITH_V3_FOLLOWERS +#include "replv3/updatesobserver.h" +#endif // REINDEX_WITH_V3_FOLLOWERS + using std::chrono::duration_cast; using std::chrono::high_resolution_clock; using std::chrono::microseconds; @@ -73,16 +78,17 @@ NamespaceImpl::NamespaceImpl(const NamespaceImpl& src, AsyncStorage::FullLockT& storage_{src.storage_, storageLock}, replStateUpdates_{src.replStateUpdates_.load()}, meta_{src.meta_}, - queryTotalCountCache_{std::make_shared()}, sparseIndexesCount_{src.sparseIndexesCount_}, krefs(src.krefs), skrefs(src.skrefs), sysRecordsVersions_{src.sysRecordsVersions_}, locker_(src.clusterizator_, *this), schema_(src.schema_), - joinCache_{std::make_shared()}, enablePerfCounters_{src.enablePerfCounters_.load()}, config_{src.config_}, + queryCountCache_{ + std::make_unique(config_.cacheConfig.queryCountCacheSize, config_.cacheConfig.queryCountHitsToCache)}, + joinCache_{std::make_unique(config_.cacheConfig.joinCacheSize, config_.cacheConfig.joinHitsToCache)}, wal_{src.wal_, storage_}, repl_{src.repl_}, storageOpts_{src.storageOpts_}, @@ -97,24 +103,43 @@ NamespaceImpl::NamespaceImpl(const NamespaceImpl& src, AsyncStorage::FullLockT& strHolder_{makeStringsHolder()}, nsUpdateSortedContextMemory_{0}, clusterizator_{src.clusterizator_}, - dbDestroyed_(false) { + dbDestroyed_(false), + incarnationTag_(src.incarnationTag_) +#ifdef REINDEX_WITH_V3_FOLLOWERS + , + observers_(src.observers_) +#endif // REINDEX_WITH_V3_FOLLOWERS +{ for (auto& idxIt : src.indexes_) indexes_.push_back(idxIt->Clone()); markUpdated(true); - logPrintf(LogInfo, "Namespace::CopyContentsFrom (%s).Workers: %d, timeout: %d, tm_statetoken: %08X", name_, - config_.optimizationSortWorkers, config_.optimizationTimeout, tagsMatcher_.stateToken()); + logPrintf(LogInfo, "Namespace::CopyContentsFrom (%s).Workers: %d, timeout: %d, tm: { state_token: 0x%08X, version: %d }", name_, + config_.optimizationSortWorkers, config_.optimizationTimeout, tagsMatcher_.stateToken(), tagsMatcher_.version()); +} + +static int64_t GetCurrentTimeUS() noexcept { + using std::chrono::duration_cast; + using std::chrono::microseconds; + using std::chrono::system_clock; + return duration_cast(system_clock::now().time_since_epoch()).count(); } -NamespaceImpl::NamespaceImpl(const std::string& name, std::optional stateToken, cluster::INsDataReplicator* clusterizator) +NamespaceImpl::NamespaceImpl(const std::string& name, std::optional stateToken, cluster::INsDataReplicator& clusterizator +#ifdef REINDEX_WITH_V3_FOLLOWERS + , + UpdatesObservers& observers +#endif // REINDEX_WITH_V3_FOLLOWERS + ) : intrusive_atomic_rc_base(), indexes_(*this), name_(name), payloadType_(name), tagsMatcher_(payloadType_, stateToken.has_value() ? stateToken.value() : tools::RandomGenerator::gets32()), - queryTotalCountCache_(std::make_shared()), locker_(clusterizator, *this), - joinCache_(std::make_shared()), enablePerfCounters_(false), + queryCountCache_( + std::make_unique(config_.cacheConfig.queryCountCacheSize, config_.cacheConfig.queryCountHitsToCache)), + joinCache_(std::make_unique(config_.cacheConfig.joinCacheSize, config_.cacheConfig.joinHitsToCache)), wal_(config_.walSize), lastSelectTime_{0}, cancelCommitCnt_{0}, @@ -122,7 +147,13 @@ NamespaceImpl::NamespaceImpl(const std::string& name, std::optional sta nsIsLoading_(false), strHolder_{makeStringsHolder()}, clusterizator_(clusterizator), - dbDestroyed_(false) { + dbDestroyed_(false), + incarnationTag_(GetCurrentTimeUS() % lsn_t::kDefaultCounter, 0) +#ifdef REINDEX_WITH_V3_FOLLOWERS + , + observers_(observers) +#endif // REINDEX_WITH_V3_FOLLOWERS +{ logPrintf(LogTrace, "NamespaceImpl::NamespaceImpl (%s)", name_); FlagGuardT nsLoadingGuard(nsIsLoading_); items_.reserve(10000); @@ -134,9 +165,9 @@ NamespaceImpl::NamespaceImpl(const std::string& name, std::optional sta addIndex(tupleIndexDef, false); updateSelectTime(); - logPrintf(LogInfo, "Namespace::Construct (%s).Workers: %d, timeout: %d, tm_statetoken: %08X(%s)", name_, + logPrintf(LogInfo, "Namespace::Construct (%s).Workers: %d, timeout: %d, tm: { state_token: 0x%08X (%s), version: %d }", name_, config_.optimizationSortWorkers, config_.optimizationTimeout, tagsMatcher_.stateToken(), - stateToken.has_value() ? "preset" : "rand"); + stateToken.has_value() ? "preset" : "rand", tagsMatcher_.version()); } NamespaceImpl::~NamespaceImpl() { @@ -251,6 +282,9 @@ void NamespaceImpl::OnConfigUpdated(DBConfigProvider& configProvider, const RdxC config_.optimizationSortWorkers, configData.optimizationSortWorkers, config_.optimizationTimeout, configData.optimizationTimeout); } + const bool needReconfigureIdxCache = !config_.cacheConfig.IsIndexesCacheEqual(configData.cacheConfig); + const bool needReconfigureJoinCache = !config_.cacheConfig.IsJoinCacheEqual(configData.cacheConfig); + const bool needReconfigureQueryCountCache = !config_.cacheConfig.IsQueryCountCacheEqual(configData.cacheConfig); config_ = configData; storageOpts_.LazyLoad(configData.lazyLoad); storageOpts_.noQueryIdleThresholdSec = configData.noQueryIdleThreshold; @@ -259,6 +293,27 @@ void NamespaceImpl::OnConfigUpdated(DBConfigProvider& configProvider, const RdxC for (auto& idx : indexes_) { idx->EnableUpdatesCountingMode(configData.idxUpdatesCountingMode); } + if (needReconfigureIdxCache) { + for (auto& idx : indexes_) { + idx->ReconfigureCache(config_.cacheConfig); + } + logPrintf(LogTrace, + "[%s] Indexes cache has been reconfigured. IdSets cache (for each index): { max_size %lu KB; hits: %u }. FullTextIdSets " + "cache (for each ft-index): { max_size %lu KB; hits: %u }", + name_, config_.cacheConfig.idxIdsetCacheSize / 1024, config_.cacheConfig.idxIdsetHitsToCache, + config_.cacheConfig.ftIdxCacheSize / 1024, config_.cacheConfig.ftIdxHitsToCache); + } + if (needReconfigureJoinCache) { + joinCache_ = std::make_unique(config_.cacheConfig.joinCacheSize, config_.cacheConfig.joinHitsToCache); + logPrintf(LogTrace, "[%s] Join cache has been reconfigured: { max_size %lu KB; hits: %u }", name_, + config_.cacheConfig.joinCacheSize / 1024, config_.cacheConfig.joinHitsToCache); + } + if (needReconfigureQueryCountCache) { + queryCountCache_ = + std::make_unique(config_.cacheConfig.queryCountCacheSize, config_.cacheConfig.queryCountHitsToCache); + logPrintf(LogTrace, "[%s] Queries count cache has been reconfigured: { max_size %lu KB; hits: %u }", name_, + config_.cacheConfig.queryCountCacheSize / 1024, config_.cacheConfig.queryCountHitsToCache); + } if (needReoptimizeIndexes) { updateSortedIdxCount(); @@ -284,12 +339,15 @@ void NamespaceImpl::OnConfigUpdated(DBConfigProvider& configProvider, const RdxC } logPrintf(LogWarning, "[repl:%s]:%d Changing serverId to %d. Tm_statetoken: %08X", name_, wal_.GetServer(), serverId, tagsMatcher_.stateToken()); + const auto oldServerId = wal_.GetServer(); try { wal_.SetServer(serverId); + incarnationTag_.SetServer(serverId); } catch (const Error& err) { logPrintf(LogError, "[repl:%s]:%d Failed to change serverId to %d: %s. Resetting to 0(zero).", name_, wal_.GetServer(), serverId, err.what()); - wal_.SetServer(0); + wal_.SetServer(oldServerId); + incarnationTag_.SetServer(oldServerId); } replStateUpdates_.fetch_add(1, std::memory_order_release); } @@ -309,8 +367,10 @@ class NamespaceImpl::RollBack_recreateCompositeIndexes : private RollBackBase { if (idx->HoldsStrings()) { ns_.strHolder_->Add(std::move(idx)); } + // NOLINTBEGIN(bugprone-empty-catch) } catch (...) { } + // NOLINTEND(bugprone-empty-catch) } } void RollBack() noexcept { @@ -364,8 +424,8 @@ NamespaceImpl::RollBack_recreateCompositeIndexes NamespaceImpl::re indexDef.opts_ = index->Opts(); indexDef.FromType(index->Type()); - createFieldsSet>(indexDef.name_, index->Type(), index->Fields(), fields); - auto newIndex{Index::New(indexDef, payloadType_, fields)}; + createCompositeFieldsSet>(indexDef.name_, index->Fields(), fields); + auto newIndex{Index::New(indexDef, PayloadType{payloadType_}, FieldsSet{fields}, config_.cacheConfig)}; rollbacker.SaveIndex(std::move(index)); std::swap(index, newIndex); @@ -403,7 +463,7 @@ class NamespaceImpl::RollBack_updateItems : private RollBackBase { } rollbacker_recreateCompositeIndexes_.RollBack(); for (auto& idx : ns_.indexes_) { - idx->UpdatePayloadType(ns_.payloadType_); + idx->UpdatePayloadType(PayloadType{ns_.payloadType_}); } Disable(); } @@ -456,7 +516,7 @@ NamespaceImpl::RollBack_updateItems NamespaceImpl::updateItems(con repl_.dataHash, itemsDataSize_}; for (auto& idx : indexes_) { - idx->UpdatePayloadType(payloadType_); + idx->UpdatePayloadType(PayloadType{payloadType_}); } VariantArray skrefsDel, skrefsUps; @@ -564,15 +624,21 @@ void NamespaceImpl::AddIndex(const IndexDef& indexDef, const RdxContext& ctx) { auto wlck = dataWLock(ctx, true); - const bool checkIdxEqualityNow = ctx.GetOriginLSN().isEmpty() && repl_.clusterStatus.role != ClusterizationStatus::Role::None; + bool checkIdxEqualityNow = ctx.GetOriginLSN().isEmpty(); // Check index existance before cluster role check, to allow followers "add" their indexes locally - if (checkIdxEqualityNow && checkIfSameIndexExists(indexDef, false)) { - if (ctx.HasEmmiterServer()) { - // Make sure, that index was already replicated to emmiter - pendedRepl.emplace_back(UpdateRecord::Type::EmptyUpdate, name_, ctx.EmmiterServerId()); - replicate(std::move(pendedRepl), std::move(wlck), false, nullptr, ctx); + // FT indexes may have different config, it will be ignored during comparison + bool requireTtlUpdate = false; + if (checkIdxEqualityNow && checkIfSameIndexExists(indexDef, &requireTtlUpdate)) { + if (requireTtlUpdate && repl_.clusterStatus.role == ClusterizationStatus::Role::None) { + checkIdxEqualityNow = false; + } else { + if (ctx.HasEmmiterServer()) { + // Make sure, that index was already replicated to emmiter + pendedRepl.emplace_back(UpdateRecord::Type::EmptyUpdate, name_, ctx.EmmiterServerId()); + replicate(std::move(pendedRepl), std::move(wlck), false, nullptr, ctx); + } + return; } - return; } checkClusterStatus(ctx); @@ -590,9 +656,11 @@ void NamespaceImpl::DumpIndex(std::ostream& os, std::string_view index, const Rd void NamespaceImpl::UpdateIndex(const IndexDef& indexDef, const RdxContext& ctx) { UpdatesContainer pendedRepl; auto wlck = dataWLock(ctx); - doUpdateIndex(indexDef, pendedRepl, ctx); - saveIndexesToStorage(); - replicate(std::move(pendedRepl), std::move(wlck), false, nullptr, ctx); + + if (doUpdateIndex(indexDef, pendedRepl, ctx)) { + saveIndexesToStorage(); + replicate(std::move(pendedRepl), std::move(wlck), false, nullptr, ctx); + } } void NamespaceImpl::DropIndex(const IndexDef& indexDef, const RdxContext& ctx) { @@ -605,22 +673,28 @@ void NamespaceImpl::DropIndex(const IndexDef& indexDef, const RdxContext& ctx) { void NamespaceImpl::SetSchema(std::string_view schema, const RdxContext& ctx) { UpdatesContainer pendedRepl; + auto wlck = dataWLock(ctx, true); - if (ctx.GetOriginLSN().isEmpty() && schema_ && - schema_->GetJSON() == Schema::AppendProtobufNumber(schema, schema_->GetProtobufNsNumber())) { - if (ctx.HasEmmiterServer()) { - // Make sure, that schema was already replicated to emmiter - pendedRepl.emplace_back(UpdateRecord::Type::EmptyUpdate, name_, ctx.EmmiterServerId()); - replicate(std::move(pendedRepl), std::move(wlck), false, nullptr, ctx); + if (ctx.GetOriginLSN().isEmpty()) { + if (schema_ && schema_->GetJSON() == Schema::AppendProtobufNumber(schema, schema_->GetProtobufNsNumber())) { + if (repl_.clusterStatus.role != ClusterizationStatus::Role::None) { + logPrintf(LogWarning, + "[repl:%s]:%d Attempt to set new JSON-schema for the replicated namespace via user interface, which does not " + "correspond to the current schema. New schema was ignored to avoid force syncs", + name_, wal_.GetServer()); + return; + } + if (ctx.HasEmmiterServer()) { + // Make sure, that schema was already replicated to emmiter + pendedRepl.emplace_back(UpdateRecord::Type::EmptyUpdate, name_, ctx.EmmiterServerId()); + replicate(std::move(pendedRepl), std::move(wlck), false, nullptr, ctx); + } + return; } - return; } - checkClusterStatus(ctx); - setSchema(schema, pendedRepl, ctx); - saveSchemaToStorage(); replicate(std::move(pendedRepl), std::move(wlck), false, nullptr, ctx); } @@ -751,22 +825,28 @@ void NamespaceImpl::verifyCompositeIndex(const IndexDef& indexDef) const { throw Error{errParams, "Composite index cannot be sparse. Use non-sparse composite instead"}; } for (const auto& jp : indexDef.jsonPaths_) { - const auto it = indexesNames_.find(jp); - if (it == indexesNames_.end()) { + int idx; + if (!tryGetIndexByName(jp, idx)) { if (!IsFullText(indexDef.Type())) { throw Error(errParams, "Composite indexes over non-indexed field ('%s') are not supported yet (except for full-text indexes). Create " "at least column index('-') over each field inside the composite index", jp); } - continue; - } - const auto& idx = indexes_[it->second]; - if (idx->IsUuid() && type != IndexCompositeHash) { - throw Error{errParams, "Only hash index allowed on UUID field"}; - } - if (IsComposite(idx->Type())) { - throw Error(errParams, "Cannot create composite index '%s' over the other composite '%s'", indexDef.name_, idx->Name()); + } else { + const auto& index = *indexes_[idx]; + if (index.Opts().IsSparse()) { + throw Error(errParams, "Composite indexes over sparse indexed field ('%s') are not supported yet", jp); + } + if (type != IndexCompositeHash && index.IsUuid()) { + throw Error{errParams, "Only hash index allowed on UUID field"}; + } + if (index.Opts().IsArray() && !IsFullText(type)) { + throw Error(errParams, "Cannot add array subindex '%s' to not fulltext composite index '%s'", jp, indexDef.name_); + } + if (IsComposite(index.Type())) { + throw Error(errParams, "Cannot create composite index '%s' over the other composite '%s'", indexDef.name_, index.Name()); + } } } } @@ -852,13 +932,13 @@ void NamespaceImpl::verifyUpdateIndex(const IndexDef& indexDef) const { return; } - const auto newIndex = std::unique_ptr(Index::New(indexDef, PayloadType(), FieldsSet())); + const auto newIndex = std::unique_ptr(Index::New(indexDef, PayloadType(), FieldsSet(), config_.cacheConfig)); if (indexDef.opts_.IsSparse()) { if (indexDef.jsonPaths_.size() != 1) { throw Error(errParams, "Sparse index must have exactly 1 JSON-path, but %d paths found for '%s'", indexDef.jsonPaths_.size(), indexDef.name_); } - const auto newSparseIndex = std::unique_ptr(Index::New(indexDef, payloadType_, {})); + const auto newSparseIndex = std::unique_ptr(Index::New(indexDef, PayloadType{payloadType_}, {}, config_.cacheConfig)); } else { FieldsSet changedFields{idxNameIt->second}; PayloadType newPlType = payloadType_; @@ -881,14 +961,18 @@ class NamespaceImpl::RollBack_insertIndex : private RollBackBase { if (insertedIdxName_) { try { ns_.indexesNames_.erase(*insertedIdxName_); + // NOLINTBEGIN(bugprone-empty-catch) } catch (...) { } + // NOLINTEND(bugprone-empty-catch) } if (pkIndexNameInserted_) { try { ns_.indexesNames_.erase(kPKIndexName); + // NOLINTBEGIN(bugprone-empty-catch) } catch (...) { } + // NOLINTEND(bugprone-empty-catch) } for (auto& n : ns_.indexesNames_) { if (n.second > insertedIdxNo_) { @@ -897,8 +981,10 @@ class NamespaceImpl::RollBack_insertIndex : private RollBackBase { } try { ns_.indexes_.erase(insertedIndex_); + // NOLINTBEGIN(bugprone-empty-catch) } catch (...) { } + // NOLINTEND(bugprone-empty-catch) Disable(); } void PkIndexNameInserted() noexcept { pkIndexNameInserted_ = true; } @@ -986,7 +1072,7 @@ class NamespaceImpl::RollBack_addIndex : private RollBackBase { }; void NamespaceImpl::addIndex(const IndexDef& indexDef, bool disableTmVersionInc, bool skipEqualityCheck) { - if (bool requireTtlUpdate = false; !skipEqualityCheck && checkIfSameIndexExists(indexDef, true, &requireTtlUpdate)) { + if (bool requireTtlUpdate = false; !skipEqualityCheck && checkIfSameIndexExists(indexDef, &requireTtlUpdate)) { if (requireTtlUpdate) { auto idxNameIt = indexesNames_.find(indexDef.name_); assertrx(idxNameIt != indexesNames_.end()); @@ -1026,20 +1112,20 @@ void NamespaceImpl::addIndex(const IndexDef& indexDef, bool disableTmVersionInc, TagsPath tagsPath = tagsMatcher_.path2tag(jsonPaths[0], true); assertrx(tagsPath.size() > 0); fields.push_back(std::move(tagsPath)); - auto newIndex = Index::New(indexDef, payloadType_, fields); + auto newIndex = Index::New(indexDef, PayloadType{payloadType_}, std::move(fields), config_.cacheConfig); rollbacker.RollBacker_insertIndex(insertIndex(std::move(newIndex), idxNo, indexName)); ++sparseIndexesCount_; rollbacker.NeedDecreaseSparseIndexCount(); fillSparseIndex(*indexes_[idxNo], jsonPaths[0]); } else { PayloadType oldPlType = payloadType_; - auto newIndex = Index::New(indexDef, PayloadType(), FieldsSet()); + auto newIndex = Index::New(indexDef, PayloadType(), FieldsSet(), config_.cacheConfig); payloadType_.Add(PayloadFieldType{newIndex->KeyType(), indexName, jsonPaths, newIndex->Opts().IsArray()}); rollbacker.SetOldPayloadType(std::move(oldPlType)); tagsMatcher_.UpdatePayloadType(payloadType_, disableTmVersionInc ? NeedChangeTmVersion::No : NeedChangeTmVersion::Increment); rollbacker.NeedResetPayloadTypeInTagsMatcher(disableTmVersionInc); newIndex->SetFields(FieldsSet{idxNo}); - newIndex->UpdatePayloadType(payloadType_); + newIndex->UpdatePayloadType(PayloadType(payloadType_)); FieldsSet changedFields{0, idxNo}; rollbacker.RollBacker_insertIndex(insertIndex(std::move(newIndex), idxNo, indexName)); @@ -1072,31 +1158,36 @@ void NamespaceImpl::doAddIndex(const IndexDef& indexDef, bool skipEqualityCheck, indexDef); } -void NamespaceImpl::updateIndex(const IndexDef& indexDef, bool disableTmVersionInc) { +bool NamespaceImpl::updateIndex(const IndexDef& indexDef, bool disableTmVersionInc) { const std::string& indexName = indexDef.name_; IndexDef foundIndex = getIndexDefinition(indexName); - if (indexDef.IsEqual(foundIndex, true)) { + if (indexDef.IsEqual(foundIndex, IndexComparison::SkipConfig)) { // Index has not been changed - if (!indexDef.IsEqual(foundIndex, false)) { + if (!indexDef.IsEqual(foundIndex, IndexComparison::WithConfig)) { // Only index config changed // Just call SetOpts indexes_[getIndexByName(indexName)]->SetOpts(indexDef.opts_); + return true; } - return; + return false; } verifyUpdateIndex(indexDef); dropIndex(indexDef, disableTmVersionInc); addIndex(indexDef, disableTmVersionInc); + return true; } -void NamespaceImpl::doUpdateIndex(const IndexDef& indexDef, UpdatesContainer& pendedRepl, const NsContext& ctx) { - updateIndex(indexDef, ctx.inSnapshot); - addToWAL(indexDef, WalIndexUpdate, ctx.rdxContext); - pendedRepl.emplace_back(UpdateRecord::Type::IndexUpdate, name_, wal_.LastLSN(), repl_.nsVersion, ctx.rdxContext.EmmiterServerId(), - indexDef); +bool NamespaceImpl::doUpdateIndex(const IndexDef& indexDef, UpdatesContainer& pendedRepl, const NsContext& ctx) { + if (updateIndex(indexDef, ctx.inSnapshot) || !ctx.GetOriginLSN().isEmpty()) { + addToWAL(indexDef, WalIndexUpdate, ctx.rdxContext); + pendedRepl.emplace_back(UpdateRecord::Type::IndexUpdate, name_, wal_.LastLSN(), repl_.nsVersion, ctx.rdxContext.EmmiterServerId(), + indexDef); + return true; + } + return false; } IndexDef NamespaceImpl::getIndexDefinition(const std::string& indexName) const { @@ -1110,27 +1201,19 @@ IndexDef NamespaceImpl::getIndexDefinition(const std::string& indexName) const { void NamespaceImpl::verifyUpdateCompositeIndex(const IndexDef& indexDef) const { verifyCompositeIndex(indexDef); - IndexType type = indexDef.Type(); - - for (auto& jsonPathOrSubIdx : indexDef.jsonPaths_) { - auto idxNameIt = indexesNames_.find(jsonPathOrSubIdx); - if (idxNameIt != indexesNames_.end() && !indexes_[idxNameIt->second]->Opts().IsSparse() && - indexes_[idxNameIt->second]->Opts().IsArray() && (type == IndexCompositeBTree || type == IndexCompositeHash)) { - throw Error(errParams, "Cannot add array subindex '%s' to composite index '%s'", jsonPathOrSubIdx, indexDef.name_); - } - } - const auto newIndex = std::unique_ptr(Index::New(indexDef, payloadType_, {})); + const auto newIndex = std::unique_ptr(Index::New(indexDef, PayloadType{payloadType_}, {}, config_.cacheConfig)); } void NamespaceImpl::addCompositeIndex(const IndexDef& indexDef) { const auto& indexName = indexDef.name_; FieldsSet fields; - createFieldsSet(indexName, indexDef.Type(), indexDef.jsonPaths_, fields); + createCompositeFieldsSet(indexName, indexDef.jsonPaths_, fields); assertrx(indexesNames_.find(indexName) == indexesNames_.end()); const int idxPos = indexes_.size(); - auto insertIndex_rollbacker{insertIndex(Index::New(indexDef, payloadType_, fields), idxPos, indexName)}; + auto insertIndex_rollbacker{ + insertIndex(Index::New(indexDef, PayloadType{payloadType_}, FieldsSet{fields}, config_.cacheConfig), idxPos, indexName)}; auto indexesCacheCleaner{GetIndexesCacheCleaner()}; for (IdType rowId = 0; rowId < int(items_.size()); rowId++) { @@ -1150,7 +1233,7 @@ void NamespaceImpl::addCompositeIndex(const IndexDef& indexDef) { } template -void NamespaceImpl::createFieldsSet(const std::string& idxName, IndexType type, const PathsT& paths, FieldsSet& fields) { +void NamespaceImpl::createCompositeFieldsSet(const std::string& idxName, const PathsT& paths, FieldsSet& fields) { fields.clear(); const JsonPathsContainerT* jsonPaths = nullptr; @@ -1165,52 +1248,39 @@ void NamespaceImpl::createFieldsSet(const std::string& idxName, IndexType type, } for (const auto& jsonPathOrSubIdx : *jsonPaths) { - auto idxNameIt = indexesNames_.find(jsonPathOrSubIdx); - if (idxNameIt == indexesNames_.end() || idxName == jsonPathOrSubIdx) { + int idx; + if (!getScalarIndexByName(jsonPathOrSubIdx, idx) /* || idxName == jsonPathOrSubIdx*/) { // TODO may be uncomment TagsPath tagsPath = tagsMatcher_.path2tag(jsonPathOrSubIdx, true); if (tagsPath.empty()) { throw Error(errLogic, "Unable to get or create json-path '%s' for composite index '%s'", jsonPathOrSubIdx, idxName); } fields.push_back(tagsPath); fields.push_back(jsonPathOrSubIdx); - } else if (indexes_[idxNameIt->second]->Opts().IsSparse() && !indexes_[idxNameIt->second]->Opts().IsArray()) { - fields.push_back(jsonPathOrSubIdx); - fields.push_back(indexes_[idxNameIt->second]->Fields().getTagsPath(0)); } else { - if (indexes_[idxNameIt->second]->Opts().IsArray() && (type == IndexCompositeBTree || type == IndexCompositeHash)) { - throw Error(errParams, "Cannot add array subindex '%s' to composite index '%s'", jsonPathOrSubIdx, idxName); - } - fields.push_back(idxNameIt->second); + const auto& idxFields = indexes_[idx]->Fields(); + assertrx_throw(idxFields.size() == 1); + assertrx_throw(idxFields[0] >= 0); + fields.push_back(idxFields[0]); } } assertrx(fields.getJsonPathsLength() == fields.getTagsPathsLength()); } -bool NamespaceImpl::checkIfSameIndexExists(const IndexDef& indexDef, bool discardConfig, bool* requireTtlUpdate) { +bool NamespaceImpl::checkIfSameIndexExists(const IndexDef& indexDef, bool* requireTtlUpdate) { auto idxNameIt = indexesNames_.find(indexDef.name_); if (idxNameIt != indexesNames_.end()) { IndexDef oldIndexDef = getIndexDefinition(indexDef.name_); - bool indexIsSame = false; - if (discardConfig) { - IndexDef newIndexDef = indexDef; - oldIndexDef.opts_.config = ""; - newIndexDef.opts_.config = ""; - if (requireTtlUpdate && oldIndexDef.Type() == IndexTtl) { - if (oldIndexDef.expireAfter_ != newIndexDef.expireAfter_) { - *requireTtlUpdate = true; - } - oldIndexDef.expireAfter_ = newIndexDef.expireAfter_; + if (oldIndexDef.Type() == IndexTtl && indexDef.Type() == IndexTtl) { + if (requireTtlUpdate && oldIndexDef.expireAfter_ != indexDef.expireAfter_) { + *requireTtlUpdate = true; } - indexIsSame = (newIndexDef == oldIndexDef); - } else { - indexIsSame = (indexDef == oldIndexDef); + oldIndexDef.expireAfter_ = indexDef.expireAfter_; } - if (indexIsSame) { + if (indexDef.IsEqual(oldIndexDef, IndexComparison::SkipConfig)) { return true; - } else { - throw Error(errConflict, "Index '%s.%s' already exists with different settings", name_, indexDef.name_); } + throw Error(errConflict, "Index '%s.%s' already exists with different settings", name_, indexDef.name_); } return false; } @@ -1225,7 +1295,7 @@ int NamespaceImpl::getIndexByName(std::string_view index) const { int NamespaceImpl::getIndexByNameOrJsonPath(std::string_view index) const { int idx; - if (getIndexByName(index, idx)) { + if (tryGetIndexByName(index, idx)) { return idx; } idx = payloadType_.FieldByJsonPath(index); @@ -1236,7 +1306,17 @@ int NamespaceImpl::getIndexByNameOrJsonPath(std::string_view index) const { } } -bool NamespaceImpl::getIndexByName(std::string_view name, int& index) const { +int NamespaceImpl::getScalarIndexByName(std::string_view index) const { + int idx; + if (tryGetIndexByName(index, idx)) { + if (idx < indexes_.firstCompositePos()) { + return idx; + } + } + throw Error(errParams, "Index '%s' not found in '%s'", index, name_); +} + +bool NamespaceImpl::tryGetIndexByName(std::string_view name, int& index) const { auto it = indexesNames_.find(name); if (it == indexesNames_.end()) return false; index = it->second; @@ -1244,7 +1324,7 @@ bool NamespaceImpl::getIndexByName(std::string_view name, int& index) const { } bool NamespaceImpl::getIndexByNameOrJsonPath(std::string_view name, int& index) const { - if (getIndexByName(name, index)) { + if (tryGetIndexByName(name, index)) { return true; } const auto idx = payloadType_.FieldByJsonPath(name); @@ -1255,6 +1335,17 @@ bool NamespaceImpl::getIndexByNameOrJsonPath(std::string_view name, int& index) return false; } +bool NamespaceImpl::getScalarIndexByName(std::string_view name, int& index) const { + int idx; + if (tryGetIndexByName(name, idx)) { + if (idx < indexes_.firstCompositePos()) { + index = idx; + return true; + } + } + return false; +} + bool NamespaceImpl::getSparseIndexByJsonPath(std::string_view jsonPath, int& index) const { // FIXME: Try to merge getIndexByNameOrJsonPath and getSparseIndexByJsonPath if it's possible for (int i = indexes_.firstSparsePos(), end = indexes_.firstSparsePos() + indexes_.sparseIndexesSize(); i < end; ++i) { @@ -1360,7 +1451,8 @@ void NamespaceImpl::doTruncate(UpdatesContainer& pendedRepl, const NsContext& ct itemsDataSize_ = 0; for (size_t i = 0; i < indexes_.size(); ++i) { const IndexOpts opts = indexes_[i]->Opts(); - std::unique_ptr newIdx{Index::New(getIndexDefinition(i), indexes_[i]->GetPayloadType(), indexes_[i]->Fields())}; + std::unique_ptr newIdx{Index::New(getIndexDefinition(i), PayloadType{indexes_[i]->GetPayloadType()}, + FieldsSet{indexes_[i]->Fields()}, config_.cacheConfig)}; newIdx->SetOpts(opts); std::swap(indexes_[i], newIdx); removeIndex(newIdx); @@ -1369,10 +1461,16 @@ void NamespaceImpl::doTruncate(UpdatesContainer& pendedRepl, const NsContext& ct WrSerializer ser; WALRecord wrec(WalUpdateQuery, (ser << "TRUNCATE " << name_).Slice()); - wal_.Add(wrec, ctx.GetOriginLSN()); + const auto lsn = wal_.Add(wrec, ctx.GetOriginLSN()); markUpdated(true); - pendedRepl.emplace_back(UpdateRecord::Type::Truncate, name_, wal_.LastLSN(), repl_.nsVersion, ctx.rdxContext.EmmiterServerId()); +#ifdef REINDEX_WITH_V3_FOLLOWERS + if (!repl_.temporary && !ctx.inSnapshot) { + observers_.OnWALUpdate(LSNPair(lsn, lsn), name_, wrec); + } +#endif // REINDEX_WITH_V3_FOLLOWERS + + pendedRepl.emplace_back(UpdateRecord::Type::Truncate, name_, lsn, repl_.nsVersion, ctx.rdxContext.EmmiterServerId()); } void NamespaceImpl::ModifyItem(Item& item, ItemModifyMode mode, const RdxContext& ctx) { @@ -1383,6 +1481,9 @@ void NamespaceImpl::ModifyItem(Item& item, ItemModifyMode mode, const RdxContext auto wlck = dataWLock(ctx); cg.Reset(); calc.LockHit(); + if (mode == ModeDelete && rx_unlikely(item.PkFields() != pkFields())) { + throw Error(errNotValid, "Item has outdated PK metadata (probably PK has been change during the Delete-call)"); + } modifyItem(item, mode, pendedRepl, NsContext(ctx)); replicate(std::move(pendedRepl), std::move(wlck), true, nullptr, ctx); @@ -1454,6 +1555,7 @@ void NamespaceImpl::CommitTransaction(LocalTransaction& tx, LocalQueryResults& r wlck = queryStatCalculator.CreateLock(*this, &NamespaceImpl::dataWLock, ctx.rdxContext, true); cg.Reset(); calc.LockHit(); + tx.ValidatePK(pkFields()); } checkClusterRole(ctx.rdxContext); // Check request source. Throw exception if false @@ -1468,6 +1570,11 @@ void NamespaceImpl::CommitTransaction(LocalTransaction& tx, LocalQueryResults& r auto lsn = wal_.Add(initWrec, tx.GetLSN()); if (!ctx.inSnapshot) { replicateAsync({UpdateRecord::Type::BeginTx, name_, lsn, repl_.nsVersion, ctx.rdxContext.EmmiterServerId()}, ctx.rdxContext); +#ifdef REINDEX_WITH_V3_FOLLOWERS + if (!repl_.temporary) { + observers_.OnWALUpdate(LSNPair(lsn, lsn), name_, initWrec); + } +#endif // REINDEX_WITH_V3_FOLLOWERS } } @@ -1629,7 +1736,7 @@ void NamespaceImpl::doUpsert(ItemImpl* ritem, IdType id, bool doUpdate) { } else { pl.Get(field, krefs, index.Opts().IsArray()); } - if (krefs == skrefs) continue; + if ((krefs.ArrayType().Is() && skrefs.ArrayType().Is()) || krefs == skrefs) continue; bool needClearCache{false}; index.Delete(krefs, id, *strHolder_, needClearCache); if (needClearCache && index.IsOrdered()) indexesCacheCleaner.Add(index.SortId()); @@ -1724,7 +1831,6 @@ void NamespaceImpl::deleteItem(Item& item, UpdatesContainer& pendedRepl, const N lsn_t itemLsn(item.GetLSN()); processWalRecord(std::move(wrec), ctx, itemLsn, &item); - pendedRepl.emplace_back(ctx.inTransaction ? UpdateRecord::Type::ItemDeleteTx : UpdateRecord::Type::ItemDelete, name_, wal_.LastLSN(), repl_.nsVersion, ctx.rdxContext.EmmiterServerId(), std::move(cjson)); } @@ -1781,6 +1887,13 @@ void NamespaceImpl::doModifyItem(Item& item, ItemModifyMode mode, UpdatesContain markUpdated(!exists); +#ifdef REINDEX_WITH_V3_FOLLOWERS + if (!repl_.temporary && !ctx.inSnapshot) { + observers_.OnWALUpdate(LSNPair(lsn, lsn), name_, + WALRecord(WalItemModify, cjson.Slice(), tagsMatcher_.version(), mode, ctx.inTransaction)); + } +#endif // REINDEX_WITH_V3_FOLLOWERS + switch (mode) { case ModeUpdate: pendedRepl.emplace_back(ctx.inTransaction ? UpdateRecord::Type::ItemUpdateTx : UpdateRecord::Type::ItemUpdate, name_, lsn, @@ -1806,34 +1919,61 @@ PayloadType NamespaceImpl::getPayloadType(const RdxContext& ctx) const { return payloadType_; } -// find id by PK. NOT THREAD SAFE! -std::pair NamespaceImpl::findByPK(ItemImpl* ritem, bool inTransaction, const RdxContext& ctx) { - auto pkIndexIt = indexesNames_.find(kPKIndexName); - - if (pkIndexIt == indexesNames_.end()) { - throw Error(errLogic, "Trying to modify namespace '%s', but it doesn't contain PK index", name_); - } - Index* pkIndex = indexes_[pkIndexIt->second].get(); - - Payload pl = ritem->GetPayload(); +RX_ALWAYS_INLINE VariantArray NamespaceImpl::getPkKeys(const ConstPayload& cpl, Index* pkIndex, int fieldNum) { // It is a faster alternative of "select ID from namespace where pk1 = 'item.pk1' and pk2 = 'item.pk2' " // Get pkey values from pk fields - VariantArray krefs; + VariantArray keys; if (IsComposite(pkIndex->Type())) { - krefs.push_back(Variant(*pl.Value())); + keys.emplace_back(*cpl.Value()); } else if (pkIndex->Opts().IsSparse()) { - auto f = pkIndex->Fields(); - pl.GetByJsonPath(f.getTagsPath(0), krefs, pkIndex->KeyType()); + cpl.GetByJsonPath(pkIndex->Fields().getTagsPath(0), keys, pkIndex->KeyType()); } else - pl.Get(pkIndexIt->second, krefs); - assertf(krefs.size() == 1, "Pkey field must contain 1 key, but there '%d' in '%s.%s'", krefs.size(), name_, pkIndex->Name()); + cpl.Get(fieldNum, keys); + return keys; +} + +RX_ALWAYS_INLINE SelectKeyResult NamespaceImpl::getPkDocs(const ConstPayload& cpl, bool inTransaction, const RdxContext& ctx) { + auto pkIndexIt = indexesNames_.find(kPKIndexName); + if (pkIndexIt == indexesNames_.end()) { + throw Error(errLogic, "Trying to modify namespace '%s', but it doesn't contain PK index", name_); + } + Index* pkIndex = indexes_[pkIndexIt->second].get(); + VariantArray keys = getPkKeys(cpl, pkIndex, pkIndexIt->second); + assertf(keys.size() == 1, "Pkey field must contain 1 key, but there '%d' in '%s.%s'", keys.size(), name_, pkIndex->Name()); Index::SelectOpts selectOpts; selectOpts.inTransaction = inTransaction; - SelectKeyResult res = pkIndex->SelectKey(krefs, CondEq, 0, selectOpts, nullptr, ctx)[0]; + return pkIndex->SelectKey(keys, CondEq, 0, selectOpts, nullptr, ctx)[0]; +} + +// find id by PK. NOT THREAD SAFE! +std::pair NamespaceImpl::findByPK(ItemImpl* ritem, bool inTransaction, const RdxContext& ctx) { + SelectKeyResult res = getPkDocs(ritem->GetConstPayload(), inTransaction, ctx); if (res.size() && res[0].ids_.size()) return {res[0].ids_[0], true}; return {-1, false}; } +void NamespaceImpl::checkUniquePK(const ConstPayload& cpl, bool inTransaction, const RdxContext& ctx) { + SelectKeyResult res = getPkDocs(cpl, inTransaction, ctx); + if (res.size() && res[0].ids_.size() > 1) { + auto pkIndexIt = indexesNames_.find(kPKIndexName); + Index* pkIndex = indexes_[pkIndexIt->second].get(); + VariantArray keys = getPkKeys(cpl, pkIndex, pkIndexIt->second); + + WrSerializer wrser; + wrser << "Duplicate Primary Key {" << pkIndex->Name() << ": "; + keys.Dump(wrser, CheckIsStringPrintable::No); + wrser << "} for rows ["; + for (size_t i = 0; i < res[0].ids_.size(); i++) { + if (i != 0) { + wrser << ", "; + } + wrser << res[0].ids_[i]; + } + wrser << "]!"; + throw Error(errLogic, wrser.Slice()); + } +} + void NamespaceImpl::optimizeIndexes(const NsContext& ctx) { static const auto kHardwareConcurrency = std::thread::hardware_concurrency(); // This is read lock only atomics based implementation of rebuild indexes @@ -1848,7 +1988,7 @@ void NamespaceImpl::optimizeIndexes(const NsContext& ctx) { rlck = rLock(ctx.rdxContext); } - if (isSystem()) { + if (isSystem() || repl_.temporary || !indexes_.size()) { return; } if (!lastUpdateTime || !config_.optimizationTimeout) { @@ -1858,10 +1998,6 @@ void NamespaceImpl::optimizeIndexes(const NsContext& ctx) { return; } - if (!indexes_.size()) { - return; - } - const auto optState{optimizationState_.load(std::memory_order_acquire)}; if (optState == OptimizationCompleted || cancelCommitCnt_.load(std::memory_order_relaxed)) return; const bool forceBuildAllIndexes = optState == NotOptimized; @@ -1933,7 +2069,7 @@ void NamespaceImpl::markUpdated(bool forceOptimizeAllIndexes) { int expected{OptimizationCompleted}; optimizationState_.compare_exchange_strong(expected, OptimizedPartially); } - queryTotalCountCache_->Clear(); + queryCountCache_->Clear(); joinCache_->Clear(); lastUpdateTime_.store( std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(), @@ -1947,6 +2083,10 @@ Item NamespaceImpl::newItem() { auto impl_ = pool_.get(0, payloadType_, tagsMatcher_, pkFields(), schema_); impl_->tagsMatcher() = tagsMatcher_; impl_->tagsMatcher().clearUpdated(); + impl_->schema() = schema_; +#ifdef RX_WITH_STDLIB_DEBUG + assertrx_dbg(impl_->PkFields() == pkFields()); +#endif // RX_WITH_STDLIB_DEBUG return Item(impl_.release()); } @@ -1960,6 +2100,7 @@ void NamespaceImpl::doUpdate(const Query& query, LocalQueryResults& result, Upda selCtx.inTransaction = ctx.inTransaction; selecter(result, selCtx, ctx.rdxContext); + ActiveQueryScope queryScope(query, QueryUpdate, optimizationState_, strHolder_.get()); const auto tmStart = high_resolution_clock::now(); bool updateWithJson = false; @@ -1992,8 +2133,14 @@ void NamespaceImpl::doUpdate(const Query& query, LocalQueryResults& result, Upda Payload pl(payloadType_, pv); uint64_t oldPlHash = pl.GetHash(); size_t oldItemCapacity = pv.GetCapacity(); - itemModifier.Modify(item.Id(), ctx.rdxContext, pendedRepl); - replicateItem(item.Id(), ctx, statementReplication, oldPlHash, oldItemCapacity, oldTmV, pendedRepl); + const bool isPKModified = itemModifier.Modify(item.Id(), ctx, pendedRepl); + std::optional modifyData; + if (isPKModified) { + // statementReplication = false; + modifyData.emplace(itemModifier.GetPayloadValueBackup(), lsn_t(item.Value().GetLSN())); + } + + replicateItem(item.Id(), ctx, statementReplication, oldPlHash, oldItemCapacity, oldTmV, std::move(modifyData), pendedRepl); item.Value() = items_[item.Id()]; } result.getTagsMatcher(0) = tagsMatcher_; @@ -2004,7 +2151,7 @@ void NamespaceImpl::doUpdate(const Query& query, LocalQueryResults& result, Upda // if (statementReplication) { // WrSerializer ser; // const_cast(query).type_ = QueryUpdate; - // WALRecord wrec(WalUpdateQuery, query.GetSQL(ser).Slice(), ctx.inTransaction); + // WALRecord wrec(WalUpdateQuery, query.GetSQL(ser, QueryUpdate).Slice(), ctx.inTransaction); // lsn = wal_.Add(wrec, ctx.GetOriginLSN()); // if (!ctx.rdxContext.fromReplication_) repl_.lastSelfLSN = lsn; // for (ItemRef &item : result.Items()) { @@ -2015,7 +2162,7 @@ void NamespaceImpl::doUpdate(const Query& query, LocalQueryResults& result, Upda // if (!ctx.rdxContext.fromReplication_) setReplLSNs(LSNPair(lsn_t(), lsn)); // } - if (query.debugLevel >= LogInfo) { + if (query.GetDebugLevel() >= LogInfo) { logPrintf(LogInfo, "Updated %d items in %d µs", result.Count(), duration_cast(high_resolution_clock::now() - tmStart).count()); } @@ -2026,27 +2173,48 @@ void NamespaceImpl::doUpdate(const Query& query, LocalQueryResults& result, Upda } void NamespaceImpl::replicateItem(IdType itemId, const NsContext& ctx, bool statementReplication, uint64_t oldPlHash, - size_t oldItemCapacity, int oldTmVersion, UpdatesContainer& pendedRepl) { + size_t oldItemCapacity, int oldTmVersion, std::optional&& modifyData, + UpdatesContainer& pendedRepl) { PayloadValue& pv(items_[itemId]); Payload pl(payloadType_, pv); if (!statementReplication) { replicateTmUpdateIfRequired(pendedRepl, oldTmVersion, ctx); - lsn_t lsn; - if (ctx.IsForceSyncItem()) { - lsn = ctx.GetOriginLSN(); - } else { - lsn = wal_.Add(WALRecord(WalItemUpdate, itemId, ctx.inTransaction), lsn_t(), lsn_t(items_[itemId].GetLSN())); - } - assertrx(!lsn.isEmpty()); + auto sendWalUpdate = [this, itemId, &ctx, &pv, &pendedRepl](UpdateRecord::Type mode) { + lsn_t lsn; + if (ctx.IsForceSyncItem()) { + lsn = ctx.GetOriginLSN(); + } else { + lsn = wal_.Add(WALRecord(WalItemUpdate, itemId, ctx.inTransaction), lsn_t(), lsn_t(items_[itemId].GetLSN())); + } + assertrx(!lsn.isEmpty()); - pv.SetLSN(lsn); - ItemImpl item(payloadType_, pv, tagsMatcher_); + pv.SetLSN(lsn); + ItemImpl item(payloadType_, pv, tagsMatcher_); - WrSerializer cjson; - item.GetCJSON(cjson, false); - pendedRepl.emplace_back(ctx.inTransaction ? UpdateRecord::Type::ItemUpdateTx : UpdateRecord::Type::ItemUpdate, name_, lsn, - repl_.nsVersion, ctx.rdxContext.EmmiterServerId(), std::move(cjson)); + WrSerializer cjson; + item.GetCJSON(cjson, false); +#ifdef REINDEX_WITH_V3_FOLLOWERS + if (!repl_.temporary && !ctx.inSnapshot) { + observers_.OnWALUpdate(LSNPair(lsn, lsn), name_, + WALRecord(WalItemModify, cjson.Slice(), tagsMatcher_.version(), ModeUpdate, ctx.inTransaction)); + } +#endif // REINDEX_WITH_V3_FOLLOWERS + pendedRepl.emplace_back(mode, name_, lsn, repl_.nsVersion, ctx.rdxContext.EmmiterServerId(), std::move(cjson)); + }; + + if (modifyData.has_value()) { + ItemImpl itemSave(payloadType_, modifyData->pv, tagsMatcher_); + WrSerializer cjson; + itemSave.GetCJSON(cjson, false); + processWalRecord(WALRecord(WalItemModify, cjson.Slice(), tagsMatcher_.version(), ModeDelete, ctx.inTransaction), ctx.rdxContext, + modifyData->lsn); + pendedRepl.emplace_back(ctx.inTransaction ? UpdateRecord::Type::ItemDeleteTx : UpdateRecord::Type::ItemDelete, name_, + wal_.LastLSN(), repl_.nsVersion, ctx.rdxContext.EmmiterServerId(), std::move(cjson)); + sendWalUpdate(ctx.inTransaction ? UpdateRecord::Type::ItemInsertTx : UpdateRecord::Type::ItemInsert); + } else { + sendWalUpdate(ctx.inTransaction ? UpdateRecord::Type::ItemUpdateTx : UpdateRecord::Type::ItemUpdate); + } } repl_.dataHash ^= oldPlHash; @@ -2056,11 +2224,17 @@ void NamespaceImpl::replicateItem(IdType itemId, const NsContext& ctx, bool stat saveTagsMatcherToStorage(true); if (storage_.IsValid()) { - WrSerializer pk; - WrSerializer data; - pk << "I"; + WrSerializer pk, data; + if (modifyData.has_value()) { + Payload plSave(payloadType_, modifyData->pv); + pk << kRxStorageItemPrefix; + plSave.SerializeFields(pk, pkFields()); + storage_.Remove(pk.Slice()); + pk.Reset(); + } + pk << kRxStorageItemPrefix; pl.SerializeFields(pk, pkFields()); - data.PutUInt64(int64_t(pv.GetLSN())); + data.PutUInt64(uint64_t(pv.GetLSN())); ItemImpl item(payloadType_, pv, tagsMatcher_); item.GetCJSON(data); storage_.Write(pk.Slice(), data.Slice()); @@ -2078,6 +2252,7 @@ void NamespaceImpl::doDelete(const Query& q, LocalQueryResults& result, UpdatesC selecter(result, selCtx, ctx.rdxContext); assertrx(result.IsNamespaceAdded(this)); + ActiveQueryScope queryScope(q, QueryDelete, optimizationState_, strHolder_.get()); const auto tmStart = high_resolution_clock::now(); const auto oldTmV = tagsMatcher_.version(); for (auto& r : result.Items()) { @@ -2087,7 +2262,7 @@ void NamespaceImpl::doDelete(const Query& q, LocalQueryResults& result, UpdatesC if (ctx.IsWalSyncItem() || (!q.HasLimit() && !q.HasOffset() && result.Count() >= kWALStatementItemsThreshold)) { WrSerializer ser; const_cast(q).type_ = QueryDelete; - processWalRecord(WALRecord(WalUpdateQuery, q.GetSQL(ser).Slice(), ctx.inTransaction), ctx); + processWalRecord(WALRecord(WalUpdateQuery, q.GetSQL(ser, QueryDelete).Slice(), ctx.inTransaction), ctx); pendedRepl.emplace_back(ctx.inTransaction ? UpdateRecord::Type::DeleteQueryTx : UpdateRecord::Type::DeleteQuery, name_, wal_.LastLSN(), repl_.nsVersion, ctx.rdxContext.EmmiterServerId(), std::string(ser.Slice())); } else { @@ -2100,7 +2275,7 @@ void NamespaceImpl::doDelete(const Query& q, LocalQueryResults& result, UpdatesC wal_.LastLSN(), repl_.nsVersion, ctx.rdxContext.EmmiterServerId(), std::move(cjson)); } } - if (q.debugLevel >= LogInfo) { + if (q.GetDebugLevel() >= LogInfo) { logPrintf(LogInfo, "Deleted %d items in %d µs", result.Count(), duration_cast(high_resolution_clock::now() - tmStart).count()); } @@ -2144,18 +2319,28 @@ void NamespaceImpl::replicateTmUpdateIfRequired(UpdatesContainer& pendedRepl, in if (oldTmVersion != tagsMatcher_.version()) { assertrx(ctx.GetOriginLSN().isEmpty()); const auto lsn = wal_.Add(WALRecord(WalEmpty, 0, ctx.inTransaction), lsn_t()); +#ifdef REINDEX_WITH_V3_FOLLOWERS + if (!repl_.temporary && !ctx.inSnapshot) { + WrSerializer ser; + ser.PutVarint(tagsMatcher_.version()); + ser.PutVarint(tagsMatcher_.stateToken()); + tagsMatcher_.serialize(ser); + WALRecord tmRec(WalTagsMatcher, ser.Slice(), ctx.inTransaction); + observers_.OnWALUpdate(LSNPair(lsn, lsn), name_, tmRec); + } +#endif // REINDEX_WITH_V3_FOLLOWERS pendedRepl.emplace_back(ctx.inTransaction ? UpdateRecord::Type::SetTagsMatcherTx : UpdateRecord::Type::SetTagsMatcher, name_, lsn, repl_.nsVersion, ctx.rdxContext.EmmiterServerId(), tagsMatcher_); } } void NamespaceImpl::Select(LocalQueryResults& result, SelectCtx& params, const RdxContext& ctx) { - if (params.query.IsWALQuery()) { - WALSelecter selecter(this, true); - selecter(result, params); - } else { + if (!params.query.IsWALQuery()) { NsSelecter selecter(this); selecter(result, params, ctx); + } else { + WALSelecter selecter(this, true); + selecter(result, params); } } @@ -2210,7 +2395,7 @@ NamespaceMemStat NamespaceImpl::GetMemStat(const RdxContext& ctx) { auto rlck = rLock(ctx); ret.name = name_; ret.joinCache = joinCache_->GetMemStat(); - ret.queryCache = queryTotalCountCache_->GetMemStat(); + ret.queryCache = queryCountCache_->GetMemStat(); ret.itemsCount = ItemsCount(); *(static_cast(&ret.replication)) = getReplState(); @@ -2356,6 +2541,8 @@ bool NamespaceImpl::loadIndexesFromStorage() { Serializer ser(def.data(), def.size()); tagsMatcher_.deserialize(ser); tagsMatcher_.clearUpdated(); + logPrintf(LogInfo, "[tm:%s]:%d: TagsMatcher was loaded from storage. tm: { state_token: 0x%08X, version: %d }", name_, + wal_.GetServer(), tagsMatcher_.stateToken(), tagsMatcher_.version()); logPrintf(LogTrace, "Loaded tags(version: %lld) of namespace %s:\n%s", sysRecordsVersions_.tagsVersion ? sysRecordsVersions_.tagsVersion - 1 : 0, name_, tagsMatcher_.dump()); } @@ -2604,6 +2791,16 @@ void NamespaceImpl::ApplySnapshotChunk(const SnapshotChunk& ch, bool isInitialLe handler.ApplyChunk(ch, isInitialLeaderSync, pendedRepl); if (ch.IsLastChunk()) { +#ifdef REINDEX_WITH_V3_FOLLOWERS + // Actually we are not supposing to use this mechanism + if (!repl_.temporary) { + WrSerializer ser; + auto nsDef = getDefinition(); + nsDef.GetJSON(ser); + ser.PutBool(true); + observers_.OnWALUpdate(LSNPair(), name_, WALRecord(WalWALSync, ser.Slice())); + } +#endif // REINDEX_WITH_V3_FOLLOWERS replicateAsync({isInitialLeaderSync ? UpdateRecord::Type::ResyncNamespaceLeaderInit : UpdateRecord::Type::ResyncNamespaceGeneric, name_, lsn_t(0, 0), lsn_t(0, 0), ctx.EmmiterServerId()}, ctx); @@ -2730,6 +2927,15 @@ void NamespaceImpl::setTagsMatcher(TagsMatcher&& tm, UpdatesContainer& pendedRep tagsMatcher_.setUpdated(); const auto lsn = wal_.Add(WALRecord(WalEmpty, 0, ctx.inTransaction), ctx.GetOriginLSN()); +#ifdef REINDEX_WITH_V3_FOLLOWERS + if (!repl_.temporary && !ctx.inSnapshot) { + WrSerializer ser; + ser.PutVarint(tagsMatcher_.version()); + ser.PutVarint(tagsMatcher_.stateToken()); + tagsMatcher_.serialize(ser); + observers_.OnWALUpdate(LSNPair(lsn, lsn), name_, WALRecord(WalTagsMatcher, ser.Slice(), ctx.inTransaction)); + } +#endif // REINDEX_WITH_V3_FOLLOWERS pendedRepl.emplace_back(ctx.inTransaction ? UpdateRecord::Type::SetTagsMatcherTx : UpdateRecord::Type::SetTagsMatcher, name_, lsn, repl_.nsVersion, ctx.rdxContext.EmmiterServerId(), std::move(tm)); @@ -2866,7 +3072,7 @@ std::vector NamespaceImpl::enumMeta() const { size_t prefixLen = strlen(kStorageMetaPrefix); for (dbIter->Seek(std::string_view(kStorageMetaPrefix)); - dbIter->Valid() && dbIter->GetComparator().Compare(dbIter->Key(), std::string_view(kStorageMetaPrefix "\xFF")) < 0; + dbIter->Valid() && dbIter->GetComparator().Compare(dbIter->Key(), std::string_view(kStorageMetaPrefix "\xFF\xFF\xFF\xFF")) < 0; dbIter->Next()) { std::string_view keySlice = dbIter->Key(); if (keySlice.size() > prefixLen) { @@ -3071,11 +3277,16 @@ void NamespaceImpl::processWalRecord(WALRecord&& wrec, const NsContext& ctx, lsn assertrx(!lsn.isEmpty()); item->setLSN(lsn); } +#ifdef REINDEX_WITH_V3_FOLLOWERS + if (!repl_.temporary && !ctx.inSnapshot) { + observers_.OnWALUpdate(LSNPair(lsn, lsn), name_, wrec); + } +#endif // REINDEX_WITH_V3_FOLLOWERS } void NamespaceImpl::replicateAsync(cluster::UpdateRecord&& rec, const RdxContext& ctx) { if (!repl_.temporary) { - auto err = clusterizator_->ReplicateAsync(std::move(rec), ctx); + auto err = clusterizator_.ReplicateAsync(std::move(rec), ctx); if (!err.ok()) { throw Error(errUpdateReplication, err.what()); } @@ -3084,7 +3295,7 @@ void NamespaceImpl::replicateAsync(cluster::UpdateRecord&& rec, const RdxContext void NamespaceImpl::replicateAsync(NamespaceImpl::UpdatesContainer&& recs, const RdxContext& ctx) { if (!repl_.temporary) { - auto err = clusterizator_->ReplicateAsync(std::move(recs), ctx); + auto err = clusterizator_.ReplicateAsync(std::move(recs), ctx); if (!err.ok()) { throw Error(errUpdateReplication, err.what()); } diff --git a/cpp_src/core/namespace/namespaceimpl.h b/cpp_src/core/namespace/namespaceimpl.h index 86f124c62..aaa3f105d 100644 --- a/cpp_src/core/namespace/namespaceimpl.h +++ b/cpp_src/core/namespace/namespaceimpl.h @@ -19,13 +19,13 @@ #include "core/querycache.h" #include "core/rollback.h" #include "core/schema.h" +#include "core/selectkeyresult.h" #include "core/storage/idatastorage.h" #include "core/storage/storagetype.h" #include "core/transaction/localtransaction.h" #include "estl/contexted_locks.h" #include "estl/fast_hash_map.h" #include "estl/shared_mutex.h" -#include "estl/smart_lock.h" #include "estl/syncpool.h" #include "stringsholder.h" #include "wal/waltracker.h" @@ -57,6 +57,10 @@ class SnapshotRecord; class Snapshot; struct SnapshotOpts; +#ifdef REINDEX_WITH_V3_FOLLOWERS +class UpdatesObservers; +#endif // REINDEX_WITH_V3_FOLLOWERS + namespace long_actions { template struct Logger; @@ -158,6 +162,7 @@ class NamespaceImpl : public intrusive_atomic_rc_base { // NOLINT(*performance. friend ProxiedSortExpression; friend SortExprFuncs::DistanceBetweenJoinedIndexesSameNs; friend class ReindexerImpl; + friend class RxSelector; friend LocalQueryResults; friend class SnapshotHandler; friend class FieldComparator; @@ -165,7 +170,7 @@ class NamespaceImpl : public intrusive_atomic_rc_base { // NOLINT(*performance. friend class ItemsLoader; friend class IndexInserters; - class NSUpdateSortedContext : public UpdateSortedContext { + class NSUpdateSortedContext final : public UpdateSortedContext { public: NSUpdateSortedContext(const NamespaceImpl &ns, SortType curSortId) : ns_(ns), sorted_indexes_(ns_.getSortedIdxCount()), curSortId_(curSortId) { @@ -234,7 +239,7 @@ class NamespaceImpl : public intrusive_atomic_rc_base { // NOLINT(*performance. using MutexType = Mutex; NsWLock() = default; - NsWLock(MutexType &mtx, const RdxContext &ctx, bool isCL) : impl_(mtx, &ctx), isClusterLck_(isCL) {} + NsWLock(MutexType &mtx, const RdxContext &ctx, bool isCL) : impl_(mtx, ctx), isClusterLck_(isCL) {} void lock() { impl_.lock(); } void unlock() { impl_.unlock(); } bool owns_lock() const { return impl_.owns_lock(); } @@ -247,30 +252,30 @@ class NamespaceImpl : public intrusive_atomic_rc_base { // NOLINT(*performance. typedef contexted_shared_lock RLockT; typedef NsWLock WLockT; - Locker(cluster::INsDataReplicator *clusterizator, NamespaceImpl &owner) : clusterizator_(clusterizator), owner_(owner) {} + Locker(cluster::INsDataReplicator &clusterizator, NamespaceImpl &owner) : clusterizator_(clusterizator), owner_(owner) {} - RLockT RLock(const RdxContext &ctx) const { return RLockT(mtx_, &ctx); } + RLockT RLock(const RdxContext &ctx) const { return RLockT(mtx_, ctx); } WLockT DataWLock(const RdxContext &ctx, bool skipClusterStatusCheck) const { using namespace std::string_view_literals; WLockT lck(mtx_, ctx, true); if (readonly_.load(std::memory_order_acquire)) { throw Error(errNamespaceInvalidated, "NS invalidated"sv); } - const bool requireSync = clusterizator_ && !ctx.NoWaitSync() && ctx.GetOriginLSN().isEmpty() && !owner_.isSystem(); + const bool requireSync = !ctx.NoWaitSync() && ctx.GetOriginLSN().isEmpty() && !owner_.isSystem(); const bool isFollowerNS = owner_.repl_.clusterStatus.role == ClusterizationStatus::Role::SimpleReplica || owner_.repl_.clusterStatus.role == ClusterizationStatus::Role::ClusterReplica; - bool synchronized = isFollowerNS || !requireSync || clusterizator_->IsInitialSyncDone(owner_.name_); + bool synchronized = isFollowerNS || !requireSync || clusterizator_.IsInitialSyncDone(owner_.name_); while (!synchronized) { // This is required in case of rename during sync wait auto name = owner_.name_; lck.unlock(); - clusterizator_->AwaitInitialSync(name, ctx); + clusterizator_.AwaitInitialSync(name, ctx); lck.lock(); if (readonly_.load(std::memory_order_acquire)) { throw Error(errNamespaceInvalidated, "NS invalidated"sv); } - synchronized = clusterizator_->IsInitialSyncDone(owner_.name_); + synchronized = clusterizator_.IsInitialSyncDone(owner_.name_); } if (!skipClusterStatusCheck) { @@ -302,11 +307,16 @@ class NamespaceImpl : public intrusive_atomic_rc_base { // NOLINT(*performance. mutable Mutex mtx_; mutable std::mutex storageMtx_; std::atomic readonly_ = {false}; - cluster::INsDataReplicator *clusterizator_; + cluster::INsDataReplicator &clusterizator_; NamespaceImpl &owner_; }; - NamespaceImpl(const std::string &_name, std::optional stateToken, cluster::INsDataReplicator *clusterizator); +#ifdef REINDEX_WITH_V3_FOLLOWERS + NamespaceImpl(const std::string &_name, std::optional stateToken, cluster::INsDataReplicator &clusterizator, + UpdatesObservers &); +#else + NamespaceImpl(const std::string &_name, std::optional stateToken, cluster::INsDataReplicator &clusterizator); +#endif // REINDEX_WITH_V3_FOLLOWERS NamespaceImpl &operator=(const NamespaceImpl &) = delete; ~NamespaceImpl(); @@ -366,8 +376,10 @@ class NamespaceImpl : public intrusive_atomic_rc_base { // NOLINT(*performance. int getIndexByName(std::string_view index) const; int getIndexByNameOrJsonPath(std::string_view name) const; - bool getIndexByName(std::string_view name, int &index) const; + int getScalarIndexByName(std::string_view name) const; + bool tryGetIndexByName(std::string_view name, int &index) const; bool getIndexByNameOrJsonPath(std::string_view name, int &index) const; + bool getScalarIndexByName(std::string_view name, int &index) const; bool getSparseIndexByJsonPath(std::string_view jsonPath, int &index) const; PayloadType getPayloadType(const RdxContext &ctx) const; @@ -387,6 +399,12 @@ class NamespaceImpl : public intrusive_atomic_rc_base { // NOLINT(*performance. void GetSnapshot(Snapshot &snapshot, const SnapshotOpts &opts, const RdxContext &ctx); void SetTagsMatcher(TagsMatcher &&tm, const RdxContext &ctx); void SetDestroyFlag() noexcept { dbDestroyed_ = true; } + Error FlushStorage(const RdxContext &ctx) { + const auto flushOpts = StorageFlushOpts().WithImmediateReopen(); + auto lck = rLock(ctx); + storage_.Flush(flushOpts); + return storage_.GetStatusCached().err; + } private: struct SysRecordsVersions { @@ -396,6 +414,12 @@ class NamespaceImpl : public intrusive_atomic_rc_base { // NOLINT(*performance. uint64_t schemaVersion{0}; }; + struct PKModifyRevertData { + PKModifyRevertData(PayloadValue &p, lsn_t l) : pv(p), lsn(l) {} + PayloadValue &pv; + lsn_t lsn; + }; + ReplicationState getReplState() const; std::string sysRecordName(std::string_view sysTag, uint64_t version); void writeSysRecToStorage(std::string_view data, std::string_view sysTag, uint64_t &version, bool direct); @@ -429,16 +453,16 @@ class NamespaceImpl : public intrusive_atomic_rc_base { // NOLINT(*performance. void addIndex(const IndexDef &indexDef, bool disableTmVersionInc, bool skipEqualityCheck = false); void doAddIndex(const IndexDef &indexDef, bool skipEqualityCheck, UpdatesContainer &pendedRepl, const NsContext &ctx); void addCompositeIndex(const IndexDef &indexDef); - bool checkIfSameIndexExists(const IndexDef &indexDef, bool discardConfig, bool *requireTtlUpdate = nullptr); + bool checkIfSameIndexExists(const IndexDef &indexDef, bool *requireTtlUpdate); template - void createFieldsSet(const std::string &idxName, IndexType type, const PathsT &paths, FieldsSet &fields); + void createCompositeFieldsSet(const std::string &idxName, const PathsT &paths, FieldsSet &fields); void verifyCompositeIndex(const IndexDef &indexDef) const; template void verifyAddIndex(const IndexDef &indexDef, GetNameF &&) const; void verifyUpdateIndex(const IndexDef &indexDef) const; void verifyUpdateCompositeIndex(const IndexDef &indexDef) const; - void updateIndex(const IndexDef &indexDef, bool disableTmVersionInc); - void doUpdateIndex(const IndexDef &indexDef, UpdatesContainer &pendedRepl, const NsContext &ctx); + bool updateIndex(const IndexDef &indexDef, bool disableTmVersionInc); + bool doUpdateIndex(const IndexDef &indexDef, UpdatesContainer &pendedRepl, const NsContext &ctx); void dropIndex(const IndexDef &index, bool disableTmVersionInc); void doDropIndex(const IndexDef &index, UpdatesContainer &pendedRepl, const NsContext &ctx); void addToWAL(const IndexDef &indexDef, WALRecType type, const NsContext &ctx); @@ -448,7 +472,7 @@ class NamespaceImpl : public intrusive_atomic_rc_base { // NOLINT(*performance. void setSchema(std::string_view schema, UpdatesContainer &pendedRepl, const NsContext &ctx); void setTagsMatcher(TagsMatcher &&tm, UpdatesContainer &pendedRepl, const NsContext &ctx); void replicateItem(IdType itemId, const NsContext &ctx, bool statementReplication, uint64_t oldPlHash, size_t oldItemCapacity, - int oldTmVersion, UpdatesContainer &pendedRepl); + int oldTmVersion, std::optional &&modifyData, UpdatesContainer &pendedRepl); template [[nodiscard]] RollBack_recreateCompositeIndexes recreateCompositeIndexes(size_t startIdx, size_t endIdx); @@ -459,6 +483,11 @@ class NamespaceImpl : public intrusive_atomic_rc_base { // NOLINT(*performance. std::string getMeta(const std::string &key) const; void putMeta(const std::string &key, std::string_view data, UpdatesContainer &pendedRepl, const NsContext &ctx); std::pair findByPK(ItemImpl *ritem, bool inTransaction, const RdxContext &); + + RX_ALWAYS_INLINE SelectKeyResult getPkDocs(const ConstPayload &cpl, bool inTransaction, const RdxContext &ctx); + RX_ALWAYS_INLINE VariantArray getPkKeys(const ConstPayload &cpl, Index *pkIndex, int fieldNum); + void checkUniquePK(const ConstPayload &cpl, bool inTransaction, const RdxContext &ctx); + int getSortedIdxCount() const noexcept; void updateSortedIdxCount(); void setFieldsBasedOnPrecepts(ItemImpl *ritem, UpdatesContainer &replUpdates, const NsContext &ctx); @@ -509,8 +538,6 @@ class NamespaceImpl : public intrusive_atomic_rc_base { // NOLINT(*performance. std::unordered_map meta_; - shared_ptr queryTotalCountCache_; - int sparseIndexesCount_ = 0; VariantArray krefs, skrefs; @@ -528,7 +555,7 @@ class NamespaceImpl : public intrusive_atomic_rc_base { // NOLINT(*performance. NamespaceImpl(const NamespaceImpl &src, AsyncStorage::FullLockT &storageLock); - bool isSystem() const noexcept { return !name_.empty() && name_[0] == '#'; } + bool isSystem() const noexcept { return isSystemNamespaceNameFast(name_); } IdType createItem(size_t realSize, IdType suggestedId); void processWalRecord(WALRecord &&wrec, const NsContext &ctx, lsn_t itemLsn = lsn_t(), Item *item = nullptr); @@ -539,7 +566,7 @@ class NamespaceImpl : public intrusive_atomic_rc_base { // NOLINT(*performance. QueryStatsCalculatorT &&statCalculator, const NsContext &ctx) { if (!repl_.temporary) { assertrx(!ctx.isCopiedNsRequest); - auto err = clusterizator_->Replicate( + auto err = clusterizator_.Replicate( std::move(recs), [&wlck]() { assertrx(wlck.isClusterLck()); @@ -575,12 +602,12 @@ class NamespaceImpl : public intrusive_atomic_rc_base { // NOLINT(*performance. } } - JoinCache::Ptr joinCache_; - PerfStatCounterMT updatePerfCounter_, selectPerfCounter_; std::atomic enablePerfCounters_; NamespaceConfigData config_; + std::unique_ptr queryCountCache_; + std::unique_ptr joinCache_; // Replication variables WALTracker wal_; ReplicationState repl_; @@ -603,7 +630,12 @@ class NamespaceImpl : public intrusive_atomic_rc_base { // NOLINT(*performance. std::deque strHoldersWaitingToBeDeleted_; std::chrono::seconds lastExpirationCheckTs_; mutable std::atomic nsUpdateSortedContextMemory_ = {0}; - cluster::INsDataReplicator *clusterizator_; + cluster::INsDataReplicator &clusterizator_; std::atomic dbDestroyed_{false}; + lsn_t incarnationTag_; // Determines unique namespace incarnation for the correct go cache invalidation + +#ifdef REINDEX_WITH_V3_FOLLOWERS + UpdatesObservers &observers_; +#endif // REINDEX_WITH_V3_FOLLOWERS }; } // namespace reindexer diff --git a/cpp_src/core/namespace/namespacestat.cc b/cpp_src/core/namespace/namespacestat.cc index 2ef987a44..2f12e4cff 100644 --- a/cpp_src/core/namespace/namespacestat.cc +++ b/cpp_src/core/namespace/namespacestat.cc @@ -183,6 +183,13 @@ void ReplicationState::FromJSON(span json) { if (!clStatusNode.empty()) { clusterStatus.FromJSON(clStatusNode); } + { + // v3 legacy + lsn_t lastUpstreamLSN; + wasV3ReplicatedNS = LoadLsn(lastUpstreamLSN, root["last_upstream_lsn"]) && !lastUpstreamLSN.isEmpty(); + wasV3ReplicatedNS = wasV3ReplicatedNS || root["slave_mode"].As(false); + } + } catch (const gason::Exception &ex) { throw Error(errParseJson, "ReplicationState: %s", ex.what()); } diff --git a/cpp_src/core/namespace/namespacestat.h b/cpp_src/core/namespace/namespacestat.h index 9fde2b3d6..1bc920ee7 100644 --- a/cpp_src/core/namespace/namespacestat.h +++ b/cpp_src/core/namespace/namespacestat.h @@ -22,14 +22,6 @@ struct LRUCacheMemStat { size_t itemsCount = 0; size_t emptyCount = 0; size_t hitCountLimit = 0; - - LRUCacheMemStat &operator+=(const LRUCacheMemStat &other) noexcept { - totalSize += other.totalSize; - itemsCount += other.itemsCount; - emptyCount += other.emptyCount; - hitCountLimit += other.hitCountLimit; - return *this; - } }; struct IndexMemStat { @@ -87,6 +79,9 @@ struct ReplicationState { lsn_t nsVersion; // Clusterization status ClusterizationStatus clusterStatus; + // Shows, that namespaces was replicated in v3. + // Required for the transition process only + bool wasV3ReplicatedNS = false; }; // TODO: Rename this diff --git a/cpp_src/core/namespace/snapshot/snapshothandler.cc b/cpp_src/core/namespace/snapshot/snapshothandler.cc index ddf1312aa..ca4815eb5 100644 --- a/cpp_src/core/namespace/snapshot/snapshothandler.cc +++ b/cpp_src/core/namespace/snapshot/snapshothandler.cc @@ -42,7 +42,7 @@ Snapshot SnapshotHandler::CreateSnapshot(const SnapshotOpts& opts) const { LocalQueryResults fullQr; { - Query q = Query(ns_.name_).Where("#lsn", CondAny, {}); + Query q = Query(ns_.name_).Where("#lsn", CondAny, VariantArray{}); SelectCtx selCtx(q, nullptr); SelectFunctionsHolder func; selCtx.functions = &func; @@ -171,8 +171,7 @@ Error SnapshotHandler::applyRealRecord(lsn_t lsn, const SnapshotRecord& snRec, c // Update query case WalUpdateQuery: { LocalQueryResults result; - Query q; - q.FromSQL(rec.data); + const Query q = Query::FromSQL(rec.data); switch (q.type_) { case QueryDelete: result.AddNamespace(&ns_, true); @@ -275,9 +274,7 @@ void SnapshotTxHandler::ApplyChunk(const SnapshotChunk& ch, bool isInitialLeader break; } case WalUpdateQuery: { - Query q; - q.FromSQL(wrec.data); - auto err = tx.Modify(std::move(q), lsn); + auto err = tx.Modify(Query::FromSQL(wrec.data), lsn); if (!err.ok()) throw err; break; } diff --git a/cpp_src/core/nsselecter/aggregator.cc b/cpp_src/core/nsselecter/aggregator.cc index 81194c7d3..837bebab0 100644 --- a/cpp_src/core/nsselecter/aggregator.cc +++ b/cpp_src/core/nsselecter/aggregator.cc @@ -26,8 +26,13 @@ static void copy(It begin, It end, std::vector &facets, const Field ConstPayload pl(payloadType, begin->first); VariantArray va; if (fields[i] == IndexValueType::SetByJsonPath) { - const TagsPath &tagsPath = fields.getTagsPath(tagPathIdx++); - pl.GetByJsonPath(tagsPath, va, KeyValueType::Undefined{}); + if (fields.isTagsPathIndexed(tagPathIdx)) { + const IndexedTagsPath &tagsPath = fields.getIndexedTagsPath(tagPathIdx++); + pl.GetByJsonPath(tagsPath, va, KeyValueType::Undefined{}); + } else { + const TagsPath &tagsPath = fields.getTagsPath(tagPathIdx++); + pl.GetByJsonPath(tagsPath, va, KeyValueType::Undefined{}); + } if (va.IsObjectValue()) { throw Error(errQueryExec, "Cannot aggregate object field"); } @@ -221,7 +226,7 @@ Aggregator::Aggregator(const PayloadType &payloadType, const FieldsSet &fields, } } else { if (sort.empty()) { - facets_ = std::make_unique(MultifieldUnorderedMap{payloadType_, fields_}); + facets_ = std::make_unique(MultifieldUnorderedMap{PayloadType{payloadType_}, FieldsSet{fields_}}); } else { facets_ = std::make_unique(MultifieldOrderedMap{MultifieldComparator{sort, fields_, payloadType_}}); } diff --git a/cpp_src/core/nsselecter/crashqueryreporter.cc b/cpp_src/core/nsselecter/crashqueryreporter.cc index dcfe7036b..7c5fa2ba0 100644 --- a/cpp_src/core/nsselecter/crashqueryreporter.cc +++ b/cpp_src/core/nsselecter/crashqueryreporter.cc @@ -9,36 +9,55 @@ namespace reindexer { struct QueryDebugContext { - SelectCtx *selectCtx = nullptr; + const Query *mainQuery = nullptr; + const Query *parentQuery = nullptr; std::atomic *nsOptimizationState = nullptr; ExplainCalc *explainCalc = nullptr; std::atomic_bool *nsLockerState = nullptr; StringsHolder *nsStrHolder = nullptr; + QueryType realQueryType = QuerySelect; }; thread_local QueryDebugContext g_queryDebugCtx; ActiveQueryScope::ActiveQueryScope(SelectCtx &ctx, std::atomic &nsOptimizationState, ExplainCalc &explainCalc, - std::atomic_bool &nsLockerState, StringsHolder *strHolder) + std::atomic_bool &nsLockerState, StringsHolder *strHolder) noexcept : isTrackedQuery_(ctx.requiresCrashTracking) { if (isTrackedQuery_) { - g_queryDebugCtx.selectCtx = &ctx; + g_queryDebugCtx.mainQuery = &ctx.query; + g_queryDebugCtx.parentQuery = ctx.parentQuery; g_queryDebugCtx.nsOptimizationState = &nsOptimizationState; g_queryDebugCtx.explainCalc = &explainCalc; g_queryDebugCtx.nsLockerState = &nsLockerState; g_queryDebugCtx.nsStrHolder = strHolder; + g_queryDebugCtx.realQueryType = ctx.crashReporterQueryType; } } + +ActiveQueryScope::ActiveQueryScope(const Query &q, QueryType realQueryType, std::atomic &nsOptimizationState, + StringsHolder *strHolder) noexcept + : isTrackedQuery_(true) { + g_queryDebugCtx.mainQuery = &q; + g_queryDebugCtx.parentQuery = nullptr; + g_queryDebugCtx.nsOptimizationState = &nsOptimizationState; + g_queryDebugCtx.explainCalc = nullptr; + g_queryDebugCtx.nsLockerState = nullptr; + g_queryDebugCtx.nsStrHolder = strHolder; + g_queryDebugCtx.realQueryType = realQueryType; +} + ActiveQueryScope::~ActiveQueryScope() { if (isTrackedQuery_) { - if (!g_queryDebugCtx.selectCtx) { - logPrintf(LogWarning, "~ActiveQueryScope: Empty context for tracked query"); + if (!g_queryDebugCtx.mainQuery) { + logPrintf(LogWarning, "~ActiveQueryScope: Empty query pointer in the ActiveQueryScope"); } - g_queryDebugCtx.selectCtx = nullptr; + g_queryDebugCtx.mainQuery = nullptr; + g_queryDebugCtx.parentQuery = nullptr; g_queryDebugCtx.nsOptimizationState = nullptr; g_queryDebugCtx.explainCalc = nullptr; g_queryDebugCtx.nsLockerState = nullptr; g_queryDebugCtx.nsStrHolder = nullptr; + g_queryDebugCtx.realQueryType = QuerySelect; } } @@ -57,41 +76,51 @@ static std::string_view nsOptimizationStateName(int state) { } void PrintCrashedQuery(std::ostream &out) { - if (!g_queryDebugCtx.selectCtx) { + if (!g_queryDebugCtx.mainQuery && !g_queryDebugCtx.parentQuery) { out << "*** No additional info from crash query tracker ***" << std::endl; return; } out << "*** Current query dump ***" << std::endl; - out << " Query: " << g_queryDebugCtx.selectCtx->query.GetSQL() << std::endl; - if (g_queryDebugCtx.selectCtx->parentQuery) { - out << " Parent Query: " << g_queryDebugCtx.selectCtx->parentQuery->GetSQL() << std::endl; + if (g_queryDebugCtx.mainQuery) { + out << " Query: " << g_queryDebugCtx.mainQuery->GetSQL(g_queryDebugCtx.realQueryType) << std::endl; + } + if (g_queryDebugCtx.parentQuery) { + out << " Parent Query: " << g_queryDebugCtx.parentQuery->GetSQL() << std::endl; } - out << " NS state: " << nsOptimizationStateName(g_queryDebugCtx.nsOptimizationState->load()) << std::endl; - out << " NS.locker state: "; - if (g_queryDebugCtx.nsLockerState->load()) { - out << " readonly"; - } else { - out << " regular"; + if (g_queryDebugCtx.nsOptimizationState) { + out << " NS state: " << nsOptimizationStateName(g_queryDebugCtx.nsOptimizationState->load()) << std::endl; } - out << std::endl; - out << " NS.strHolder state: [" << std::endl; - out << " memstat = " << g_queryDebugCtx.nsStrHolder->MemStat() << std::endl; - out << " holds indexes = " << std::boolalpha << g_queryDebugCtx.nsStrHolder->HoldsIndexes() << std::endl; - if (g_queryDebugCtx.nsStrHolder->HoldsIndexes()) { - const auto &indexes = g_queryDebugCtx.nsStrHolder->Indexes(); - out << " indexes.size = " << indexes.size() << std::endl; - out << " indexes = ["; - for (size_t i = 0; i < indexes.size(); ++i) { - if (i) out << " "; - out << indexes[i]->Name(); + if (g_queryDebugCtx.nsLockerState) { + out << " NS.locker state: "; + if (g_queryDebugCtx.nsLockerState->load()) { + out << " readonly"; + } else { + out << " regular"; + } + out << std::endl; + } + if (g_queryDebugCtx.nsStrHolder) { + out << " NS.strHolder state: [" << std::endl; + out << " memstat = " << g_queryDebugCtx.nsStrHolder->MemStat() << std::endl; + out << " holds indexes = " << std::boolalpha << g_queryDebugCtx.nsStrHolder->HoldsIndexes() << std::endl; + if (g_queryDebugCtx.nsStrHolder->HoldsIndexes()) { + const auto &indexes = g_queryDebugCtx.nsStrHolder->Indexes(); + out << " indexes.size = " << indexes.size() << std::endl; + out << " indexes = ["; + for (size_t i = 0; i < indexes.size(); ++i) { + if (i) out << " "; + out << indexes[i]->Name(); + } + out << "]" << std::endl; } out << "]" << std::endl; } - out << "]" << std::endl; - out << " Explain: " << g_queryDebugCtx.explainCalc->GetJSON() << std::endl; + if (g_queryDebugCtx.explainCalc) { + out << " Explain: " << g_queryDebugCtx.explainCalc->GetJSON() << std::endl; + } - g_queryDebugCtx.selectCtx = nullptr; + g_queryDebugCtx.mainQuery = g_queryDebugCtx.parentQuery = nullptr; } } // namespace reindexer diff --git a/cpp_src/core/nsselecter/crashqueryreporter.h b/cpp_src/core/nsselecter/crashqueryreporter.h index 5ed0aff96..b76ab67a2 100644 --- a/cpp_src/core/nsselecter/crashqueryreporter.h +++ b/cpp_src/core/nsselecter/crashqueryreporter.h @@ -2,17 +2,20 @@ #include #include +#include "core/type_consts.h" namespace reindexer { struct SelectCtx; class ExplainCalc; class StringsHolder; +class Query; class ActiveQueryScope { public: ActiveQueryScope(SelectCtx &ctx, std::atomic &nsOptimizationState, ExplainCalc &explainCalc, std::atomic_bool &nsLockerState, - StringsHolder *strHolder); + StringsHolder *strHolder) noexcept; + ActiveQueryScope(const Query &q, QueryType realQueryType, std::atomic &nsOptimizationState, StringsHolder *strHolder) noexcept; ~ActiveQueryScope(); public: diff --git a/cpp_src/core/nsselecter/explaincalc.cc b/cpp_src/core/nsselecter/explaincalc.cc index c7d9506de..642cade9c 100644 --- a/cpp_src/core/nsselecter/explaincalc.cc +++ b/cpp_src/core/nsselecter/explaincalc.cc @@ -29,7 +29,8 @@ void ExplainCalc::LogDump(int logLevel) { [this](const FieldsComparator &c) { logPrintf(LogInfo, "%s: cost %g, matched %d, %s", c.Name(), c.Cost(iters_), c.GetMatchedCount(), c.Dump()); }, - [](const AlwaysFalse &) { logPrintf(LogInfo, "AlwaysFalse"); }); + [](const AlwaysFalse &) { logPrintf(LogInfo, "AlwaysFalse"); }, + [](const AlwaysTrue &) { logPrintf(LogInfo, "AlwaysTrue"); }); } if (jselectors_) { @@ -45,7 +46,7 @@ void ExplainCalc::LogDump(int logLevel) { } } -constexpr inline const char *joinTypeName(JoinType type) noexcept { +constexpr static inline const char *joinTypeName(JoinType type) noexcept { switch (type) { case JoinType::InnerJoin: return "inner_join "; @@ -60,7 +61,7 @@ constexpr inline const char *joinTypeName(JoinType type) noexcept { } } -constexpr inline const char *opName(OpType op, bool first = true) { +constexpr static inline const char *opName(OpType op, bool first = true) { switch (op) { case OpAnd: return first ? "" : "and "; @@ -192,6 +193,7 @@ std::string ExplainCalc::GetJSON() { WrSerializer ser; { JsonBuilder json(ser); + json.EmitTrailingForFloat(false); if (enabled_) { json.Put("total_us"sv, To_us(total_)); json.Put("preselect_us"sv, To_us(preselect_)); @@ -290,6 +292,13 @@ std::string SelectIteratorContainer::explainJSON(const_iterator begin, const_ite jsonSkiped.Put("description"sv, "always "s + (it->operation == OpNot ? "true" : "false")); name << opName(it->operation == OpNot ? OpAnd : it->operation, it == begin) << "Always"sv << (it->operation == OpNot ? "True"sv : "False"sv); + }, + [&](const AlwaysTrue &) { + auto jsonSkiped = builder.Object(); + jsonSkiped.Put("type"sv, "Skipped"sv); + jsonSkiped.Put("description"sv, "always "s + (it->operation == OpNot ? "false" : "true")); + name << opName(it->operation == OpNot ? OpAnd : it->operation, it == begin) << "Always"sv + << (it->operation == OpNot ? "False"sv : "True"sv); }); } name << ')'; diff --git a/cpp_src/core/nsselecter/fieldscomparator.h b/cpp_src/core/nsselecter/fieldscomparator.h index c6e5908b5..62d6d4148 100644 --- a/cpp_src/core/nsselecter/fieldscomparator.h +++ b/cpp_src/core/nsselecter/fieldscomparator.h @@ -33,13 +33,13 @@ class FieldsComparator { const std::string& Name() const&& = delete; std::string Dump() const { return Name(); } int GetMatchedCount() const noexcept { return matchedCount_; } - void SetLeftField(const TagsPath& tpath) { - setField(tpath, ctx_[0].lCtx_); + void SetLeftField(const FieldsSet& fields) { + setField(fields, ctx_[0].lCtx_); leftFieldSet = true; } - void SetRightField(const TagsPath& tpath) { + void SetRightField(const FieldsSet& fields) { assertrx(leftFieldSet); - setField(tpath, ctx_[0].rCtx_); + setField(fields, ctx_[0].rCtx_); } void SetLeftField(const FieldsSet& fset, KeyValueType type, bool isArray) { if (type.Is()) { @@ -82,6 +82,11 @@ class FieldsComparator { }; void setField(const TagsPath& tpath, FieldContext& fctx) { fctx.fields_.push_back(tpath); } + void setField(const FieldsSet& fields, FieldContext& fctx) { + assertrx_throw(fields.size() == 1); + assertrx_throw(fields[0] == IndexValueType::SetByJsonPath); + setField(fields.getTagsPath(0), fctx); + } void setField(FieldContext& fctx, FieldsSet fset, KeyValueType type, bool isArray) { fctx.fields_ = std::move(fset); fctx.type_ = type; diff --git a/cpp_src/core/nsselecter/joinedselector.cc b/cpp_src/core/nsselecter/joinedselector.cc index bd5e92680..1ec77ee96 100644 --- a/cpp_src/core/nsselecter/joinedselector.cc +++ b/cpp_src/core/nsselecter/joinedselector.cc @@ -3,6 +3,7 @@ #include "core/namespace/namespaceimpl.h" #include "core/queryresults/joinresults.h" #include "nsselecter.h" +#include "vendor/sparse-map/sparse_set.h" constexpr size_t kMaxIterationsScaleForInnerJoinOptimization = 100; @@ -30,7 +31,7 @@ void JoinedSelector::selectFromRightNs(LocalQueryResults &joinItemR, const Query ctx.skipIndexesLookup = true; ctx.functions = &selectFunctions_; rightNs_->Select(joinItemR, ctx, rdxCtx_); - if (query.explain_) { + if (query.GetExplain()) { preResult_->explainOneSelect = joinItemR.explainResults; } @@ -54,8 +55,8 @@ void JoinedSelector::selectFromPreResultValues(LocalQueryResults &joinItemR, con for (const ItemRef &item : preResult_->values) { auto &v = item.Value(); assertrx(!v.IsFree()); - if (query.entries.CheckIfSatisfyConditions({preResult_->values.payloadType, v}, preResult_->values.tagsMatcher)) { - if (++matched > query.count) break; + if (query.Entries().CheckIfSatisfyConditions({preResult_->values.payloadType, v})) { + if (++matched > query.Limit()) break; found = true; joinItemR.Add(item); } @@ -73,34 +74,25 @@ bool JoinedSelector::Process(IdType rowId, int nsId, ConstPayload payload, bool const auto startTime = ExplainCalc::Clock::now(); // Put values to join conditions size_t i = 0; - if (itemQuery_.explain_ && !preResult_->explainOneSelect.empty()) itemQuery_.explain_ = false; + if (itemQuery_.GetExplain() && !preResult_->explainOneSelect.empty()) itemQuery_.Explain(false); std::unique_ptr itemQueryCopy; Query *itemQueryPtr = &itemQuery_; for (auto &je : joinQuery_.joinEntries_) { - const bool nonIndexedField = (je.idxNo == IndexValueType::SetByJsonPath); - if (nonIndexedField) { - VariantArray &values = itemQueryPtr->entries.Get(i).values; - const KeyValueType type{values.empty() ? KeyValueType::Undefined{} : values[0].Type()}; - payload.GetByJsonPath(je.index_, leftNs_->tagsMatcher_, values, type); - } else { - const auto &index = *leftNs_->indexes_[je.idxNo]; - const auto &fields = index.Fields(); - if (fields.getJsonPathsLength() == 0) { - payload.Get(fields[0], itemQueryPtr->entries.Get(i).values); - } else { - payload.GetByJsonPath(fields.getTagsPath(0), itemQueryPtr->entries.Get(i).values, index.KeyType()); - } + QueryEntry &qentry = itemQueryPtr->GetUpdatableEntry(i); + { + auto keyValues = qentry.UpdatableValues(QueryEntry::IgnoreEmptyValues{}); + payload.GetByFieldsSet(je.LeftFields(), keyValues, je.LeftFieldType(), je.LeftCompositeFieldsTypes()); } - if (itemQueryPtr->entries.Get(i).values.empty()) { + if (qentry.Values().empty()) { if (itemQueryPtr == &itemQuery_) { - itemQueryCopy = std::unique_ptr{new Query(itemQuery_)}; + itemQueryCopy = std::make_unique(itemQuery_); itemQueryPtr = itemQueryCopy.get(); } - itemQueryPtr->entries.SetValue(i, AlwaysFalse{}); + itemQueryPtr->SetEntry(i); } ++i; } - itemQueryPtr->Limit(match ? joinQuery_.count : 0); + itemQueryPtr->Limit(match ? joinQuery_.Limit() : 0); bool found = false; bool matchedAtLeastOnce = false; @@ -123,58 +115,70 @@ bool JoinedSelector::Process(IdType rowId, int nsId, ConstPayload payload, bool return matchedAtLeastOnce; } -template -void JoinedSelector::readValuesFromRightNs(VariantArray &values, const KeyValueType leftIndexType, [[maybe_unused]] int rightIdxNo, - [[maybe_unused]] std::string_view rightIndex) const { - std::unordered_set set; - VariantArray buffer; - for (IdType rowId : preResult_->ids) { - if (rightNs_->items_[rowId].IsFree()) continue; - buffer.clear(); - const ConstPayload pl{rightNs_->payloadType_, rightNs_->items_[rowId]}; - if constexpr (byJsonPath) { - pl.GetByJsonPath(rightIndex, rightNs_->tagsMatcher_, buffer, leftIndexType); - } else { - pl.Get(rightIdxNo, buffer); +template +VariantArray JoinedSelector::readValuesOfRightNsFrom(const Cont &data, const Fn &createPayload, const QueryJoinEntry &entry, + const PayloadType &pt) const { + const auto rightFieldType = entry.RightFieldType(); + const auto leftFieldType = entry.LeftFieldType(); + VariantArray res; + if (rightFieldType.Is()) { + unordered_payload_set set(data.size(), hash_composite(pt, entry.RightFields()), equal_composite(pt, entry.RightFields())); + for (const auto &v : data) { + const auto pl = createPayload(v); + if (pl) { + set.insert(*pl->Value()); + } } - if (!leftIndexType.Is() && !leftIndexType.Is()) { - for (Variant &v : buffer) set.insert(std::move(v.convert(leftIndexType))); - } else { - for (Variant &v : buffer) set.insert(std::move(v)); + res.reserve(set.size()); + for (auto &s : set) { + res.emplace_back(std::move(s)); } - } - values.reserve(set.size()); - std::move(set.begin(), set.end(), std::back_inserter(values)); -} - -template -void JoinedSelector::readValuesFromPreResult(VariantArray &values, const KeyValueType leftIndexType, int rightIdxNo, - std::string_view rightIndex) const { - std::unordered_set set; - VariantArray buffer; - for (const ItemRef &item : preResult_->values) { - buffer.clear(); - assertrx(!item.Value().IsFree()); - const ConstPayload pl{preResult_->values.payloadType, item.Value()}; - if constexpr (byJsonPath) { - pl.GetByJsonPath(rightIndex, preResult_->values.tagsMatcher, buffer, leftIndexType); - (void)rightIdxNo; - } else { - pl.Get(rightIdxNo, buffer); - (void)rightIndex; + } else { + tsl::sparse_set set(data.size()); + for (const auto &v : data) { + const auto pl = createPayload(v); + if (!pl) { + continue; + } + pl->GetByFieldsSet(entry.RightFields(), res, entry.RightFieldType(), entry.RightCompositeFieldsTypes()); + if (!leftFieldType.Is() && !leftFieldType.Is()) { + for (Variant &v : res) set.insert(std::move(v.convert(leftFieldType))); + } else { + for (Variant &v : res) set.insert(std::move(v)); + } } - if (!leftIndexType.Is() && !leftIndexType.Is()) { - for (Variant &v : buffer) set.insert(std::move(v.convert(leftIndexType))); - } else { - for (Variant &v : buffer) set.insert(std::move(v)); + res.clear(); + for (auto &s : set) { + res.emplace_back(std::move(s)); } } - values.reserve(set.size()); - std::move(set.begin(), set.end(), std::back_inserter(values)); + return res; +} + +VariantArray JoinedSelector::readValuesFromRightNs(const QueryJoinEntry &entry) const { + return readValuesOfRightNsFrom( + preResult_->ids, + [this](IdType rowId) -> std::optional { + const auto &item = rightNs_->items_[rowId]; + if (item.IsFree()) { + return std::nullopt; + } + return ConstPayload{rightNs_->payloadType_, item}; + }, + entry, rightNs_->payloadType_); } -template void JoinedSelector::readValuesFromPreResult(VariantArray &, KeyValueType, int, std::string_view) const; -template void JoinedSelector::readValuesFromPreResult(VariantArray &, KeyValueType, int, std::string_view) const; +VariantArray JoinedSelector::readValuesFromPreResult(const QueryJoinEntry &entry) const { + return readValuesOfRightNsFrom( + preResult_->values, + [this](const ItemRef &item) -> std::optional { + if (item.Value().IsFree()) { + return std::nullopt; + } + return ConstPayload{preResult_->values.payloadType, item.Value()}; + }, + entry, preResult_->values.payloadType); +} void JoinedSelector::AppendSelectIteratorOfJoinIndexData(SelectIteratorContainer &iterators, int *maxIterations, unsigned sortId, const SelectFunction::Ptr &selectFnc, const RdxContext &rdxCtx) { @@ -185,39 +189,25 @@ void JoinedSelector::AppendSelectIteratorOfJoinIndexData(SelectIteratorContainer return; } unsigned optimized = 0; - assertrx(preResult_->dataMode != JoinPreResult::ModeValues || itemQuery_.entries.Size() == joinQuery_.joinEntries_.size()); + assertrx_throw(preResult_->dataMode != JoinPreResult::ModeValues || itemQuery_.Entries().Size() == joinQuery_.joinEntries_.size()); for (size_t i = 0; i < joinQuery_.joinEntries_.size(); ++i) { const QueryJoinEntry &joinEntry = joinQuery_.joinEntries_[i]; - if (joinEntry.op_ != OpAnd || (joinEntry.condition_ != CondEq && joinEntry.condition_ != CondSet) || - (i + 1 < joinQuery_.joinEntries_.size() && joinQuery_.joinEntries_[i + 1].op_ == OpOr) || - joinEntry.idxNo == IndexValueType::SetByJsonPath) { + if (!joinEntry.IsLeftFieldIndexed() || joinEntry.Operation() != OpAnd || + (joinEntry.Condition() != CondEq && joinEntry.Condition() != CondSet) || + (i + 1 < joinQuery_.joinEntries_.size() && joinQuery_.joinEntries_[i + 1].Operation() == OpOr)) { continue; } - const auto &leftIndex = leftNs_->indexes_[joinEntry.idxNo]; + const auto &leftIndex = leftNs_->indexes_[joinEntry.LeftIdxNo()]; assertrx(!IsFullText(leftIndex->Type())); if (leftIndex->Opts().IsSparse()) continue; VariantArray values; if (preResult_->dataMode == JoinPreResult::ModeIdSet) { - int rightIdxNo = IndexValueType::NotSet; - if (rightNs_->getIndexByNameOrJsonPath(joinEntry.joinIndex_, rightIdxNo) && - !rightNs_->indexes_[rightIdxNo]->Opts().IsSparse()) { - readValuesFromRightNs(values, leftIndex->SelectKeyType(), rightIdxNo, joinEntry.joinIndex_); - } else { - readValuesFromRightNs(values, leftIndex->SelectKeyType(), rightIdxNo, joinEntry.joinIndex_); - } + values = readValuesFromRightNs(joinEntry); } else { - assertrx(itemQuery_.entries.HoldsOrReferTo(i)); - const QueryEntry &qe = itemQuery_.entries.Get(i); - assertrx(qe.index == joinEntry.joinIndex_); - const int rightIdxNo = qe.idxNo; - if (rightIdxNo == IndexValueType::SetByJsonPath) { - readValuesFromPreResult(values, leftIndex->SelectKeyType(), rightIdxNo, joinEntry.joinIndex_); - } else { - readValuesFromPreResult(values, leftIndex->SelectKeyType(), rightIdxNo, joinEntry.joinIndex_); - } + values = readValuesFromPreResult(joinEntry); } - auto ctx = selectFnc ? selectFnc->CreateCtx(joinEntry.idxNo) : BaseFunctionCtx::Ptr{}; + auto ctx = selectFnc ? selectFnc->CreateCtx(joinEntry.LeftIdxNo()) : BaseFunctionCtx::Ptr{}; assertrx(!ctx || ctx->type != BaseFunctionCtx::kFtCtx); if (leftIndex->Opts().GetCollateMode() == CollateUTF8) { @@ -231,9 +221,9 @@ void JoinedSelector::AppendSelectIteratorOfJoinIndexData(SelectIteratorContainer bool was = false; for (SelectKeyResult &res : leftIndex->SelectKey(values, CondSet, sortId, opts, ctx, rdxCtx)) { if (!res.comparators_.empty()) continue; - SelectIterator selIter{res, false, joinEntry.index_, - (joinEntry.idxNo < 0 ? IteratorFieldKind::NonIndexed : IteratorFieldKind::Indexed), false}; - selIter.Bind(leftNs_->payloadType_, joinEntry.idxNo); + SelectIterator selIter{res, false, joinEntry.LeftFieldName(), + (joinEntry.LeftIdxNo() < 0 ? IteratorFieldKind::NonIndexed : IteratorFieldKind::Indexed), false}; + selIter.Bind(leftNs_->payloadType_, joinEntry.LeftIdxNo()); const int curIterations = selIter.GetMaxIterations(); if (curIterations && curIterations < *maxIterations) *maxIterations = curIterations; iterators.Append(OpAnd, std::move(selIter)); diff --git a/cpp_src/core/nsselecter/joinedselector.h b/cpp_src/core/nsselecter/joinedselector.h index 355a51655..06bd5759b 100644 --- a/cpp_src/core/nsselecter/joinedselector.h +++ b/cpp_src/core/nsselecter/joinedselector.h @@ -88,7 +88,13 @@ class JoinedSelector { joinedSelectorsCount_(joinedSelectorsCount), rdxCtx_(rdxCtx), optimized_(false), - inTransaction_{inTransaction} {} + inTransaction_{inTransaction} { +#ifndef NDEBUG + for (const auto &jqe : joinQuery_.joinEntries_) { + assertrx_throw(jqe.FieldsHaveBeenSet()); + } +#endif + } JoinedSelector(JoinedSelector &&) = default; JoinedSelector &operator=(JoinedSelector &&) = delete; @@ -98,7 +104,7 @@ class JoinedSelector { bool Process(IdType, int nsId, ConstPayload, bool match); JoinType Type() const noexcept { return joinType_; } void SetType(JoinType type) noexcept { joinType_ = type; } - const std::string &RightNsName() const noexcept { return itemQuery_._namespace; } + const std::string &RightNsName() const noexcept { return itemQuery_.NsName(); } const JoinedQuery &JoinQuery() const noexcept { return joinQuery_; } int Called() const noexcept { return called_; } int Matched() const noexcept { return matched_; } @@ -109,10 +115,11 @@ class JoinedSelector { const NamespaceImpl::Ptr &RightNs() const noexcept { return rightNs_; } private: - template - void readValuesFromRightNs(VariantArray &values, KeyValueType leftIndexType, int rightIdxNo, std::string_view rightIndex) const; - template - void readValuesFromPreResult(VariantArray &values, KeyValueType leftIndexType, int rightIdxNo, std::string_view rightIndex) const; + [[nodiscard]] VariantArray readValuesFromRightNs(const QueryJoinEntry &) const; + [[nodiscard]] VariantArray readValuesFromPreResult(const QueryJoinEntry &) const; + template + [[nodiscard]] VariantArray readValuesOfRightNsFrom(const Cont &from, const Fn &createPayload, const QueryJoinEntry &, + const PayloadType &) const; void selectFromRightNs(LocalQueryResults &joinItemR, const Query &, bool &found, bool &matchedAtLeastOnce); void selectFromPreResultValues(LocalQueryResults &joinItemR, const Query &, bool &found, bool &matchedAtLeastOnce) const; @@ -134,7 +141,4 @@ class JoinedSelector { }; using JoinedSelectors = std::vector; -extern template void JoinedSelector::readValuesFromPreResult(VariantArray &, KeyValueType, int, std::string_view) const; -extern template void JoinedSelector::readValuesFromPreResult(VariantArray &, KeyValueType, int, std::string_view) const; - } // namespace reindexer diff --git a/cpp_src/core/nsselecter/joinedselectormock.h b/cpp_src/core/nsselecter/joinedselectormock.h index 5db3a49e6..e5cc58ea3 100644 --- a/cpp_src/core/nsselecter/joinedselectormock.h +++ b/cpp_src/core/nsselecter/joinedselectormock.h @@ -17,7 +17,7 @@ class JoinedSelectorMock { public: JoinedSelectorMock(JoinType jt, reindexer::JoinedQuery q) : query_{std::move(q)}, qr_{}, joinType_{jt} {} const reindexer::JoinedQuery& JoinQuery() const noexcept { return query_; } - const std::string& RightNsName() const noexcept { return query_._namespace; } + const std::string& RightNsName() const noexcept { return query_.NsName(); } reindexer::QueryResults& QueryResults() noexcept { return qr_; } const reindexer::QueryResults& QueryResults() const noexcept { return qr_; } JoinType Type() const noexcept { return joinType_; } diff --git a/cpp_src/core/nsselecter/nsselecter.cc b/cpp_src/core/nsselecter/nsselecter.cc index 30b4144a3..84004986b 100644 --- a/cpp_src/core/nsselecter/nsselecter.cc +++ b/cpp_src/core/nsselecter/nsselecter.cc @@ -1,6 +1,5 @@ #include "nsselecter.h" -#include "core/cjson/jsonbuilder.h" #include "core/namespace/namespaceimpl.h" #include "core/queryresults/joinresults.h" #include "core/sorting/sortexpression.h" @@ -24,12 +23,10 @@ void NsSelecter::operator()(LocalQueryResults &result, SelectCtx &ctx, const Rdx // std::cout << sql << std::endl; const size_t resultInitSize = result.Count(); ctx.sortingContext.enableSortOrders = ns_->SortOrdersBuilt(); - if (ns_->config_.logLevel > ctx.query.debugLevel) { - const_cast(&ctx.query)->debugLevel = ns_->config_.logLevel; - } + const LogLevel logLevel = std::max(ns_->config_.logLevel, LogLevel(ctx.query.GetDebugLevel())); auto &explain = ctx.explain; - explain = ExplainCalc(ctx.query.explain_ || ctx.query.debugLevel >= LogInfo); + explain = ExplainCalc(ctx.query.GetExplain() || logLevel >= LogInfo); ActiveQueryScope queryScope(ctx, ns_->optimizationState_, explain, ns_->locker_.IsReadOnly(), ns_->strHolder_.get()); explain.SetPreselectTime(ctx.preResultTimeTotal); @@ -48,13 +45,13 @@ void NsSelecter::operator()(LocalQueryResults &result, SelectCtx &ctx, const Rdx bool containAggCount = containSomeAggCount(AggCount); bool containAggCountCached = containAggCount ? false : containSomeAggCount(AggCountCached); - bool needCalcTotal = aggregationQueryRef.calcTotal == ModeAccurateTotal || containAggCount; + bool needCalcTotal = aggregationQueryRef.CalcTotal() == ModeAccurateTotal || containAggCount; QueryCacheKey ckey; - if (aggregationQueryRef.calcTotal == ModeCachedTotal || containAggCountCached) { + if (aggregationQueryRef.CalcTotal() == ModeCachedTotal || containAggCountCached) { ckey = QueryCacheKey{ctx.query}; - auto cached = ns_->queryTotalCountCache_->Get(ckey); + auto cached = ns_->queryCountCache_->Get(ckey); if (cached.valid && cached.val.total_count >= 0) { result.totalCount += cached.val.total_count; logPrintf(LogTrace, "[%s] using value from cache: %d", ns_->name_, result.totalCount); @@ -66,15 +63,12 @@ void NsSelecter::operator()(LocalQueryResults &result, SelectCtx &ctx, const Rdx } OnConditionInjections explainInjectedOnConditions; - QueryPreprocessor qPreproc((ctx.preResult && ctx.preResult->executionMode == JoinPreResult::ModeExecute) - ? const_cast(&ctx.query.entries)->MakeLazyCopy() - : QueryEntries{ctx.query.entries}, - ns_, ctx); + QueryPreprocessor qPreproc(QueryEntries{ctx.query.Entries()}, ns_, ctx); if (ctx.joinedSelectors) { - qPreproc.InjectConditionsFromJoins(*ctx.joinedSelectors, explainInjectedOnConditions, rdxCtx); + qPreproc.InjectConditionsFromJoins(*ctx.joinedSelectors, explainInjectedOnConditions, logLevel, rdxCtx); explain.PutOnConditionInjections(&explainInjectedOnConditions); } - auto aggregators = getAggregators(aggregationQueryRef.aggregations_, aggregationQueryRef.strictMode); + auto aggregators = getAggregators(aggregationQueryRef.aggregations_, aggregationQueryRef.GetStrictMode()); qPreproc.AddDistinctEntries(aggregators); const bool aggregationsOnly = aggregators.size() > 1 || (aggregators.size() == 1 && aggregators[0].Type() != AggDistinct); @@ -95,7 +89,7 @@ void NsSelecter::operator()(LocalQueryResults &result, SelectCtx &ctx, const Rdx } // Prepare data for select functions if (ctx.functions) { - fnc_ = ctx.functions->AddNamespace(ctx.query, *ns_, isFt); + fnc_ = ctx.functions->AddNamespace(ctx.query, *ns_, ctx.nsid, isFt); } if (!ctx.skipIndexesLookup) { @@ -110,7 +104,8 @@ void NsSelecter::operator()(LocalQueryResults &result, SelectCtx &ctx, const Rdx explain.AddPrepareTime(); if (ctx.contextCollectingMode) { - result.addNSContext(ns_->payloadType_, ns_->tagsMatcher_, FieldsSet(ns_->tagsMatcher_, ctx.query.selectFilter_), ns_->schema_); + result.addNSContext(ns_->payloadType_, ns_->tagsMatcher_, FieldsSet(ns_->tagsMatcher_, ctx.query.SelectFilters()), ns_->schema_, + ns_->incarnationTag_); } if (isFt) result.haveRank = true; @@ -123,8 +118,8 @@ void NsSelecter::operator()(LocalQueryResults &result, SelectCtx &ctx, const Rdx } SelectIteratorContainer qres(ns_->payloadType_, &ctx); - QresExplainHolder qresHolder(qres, (explain.IsEnabled() || ctx.query.debugLevel >= LogTrace) ? QresExplainHolder::ExplainEnabled::Yes - : QresExplainHolder::ExplainEnabled::No); + QresExplainHolder qresHolder(qres, (explain.IsEnabled() || logLevel >= LogTrace) ? QresExplainHolder::ExplainEnabled::Yes + : QresExplainHolder::ExplainEnabled::No); LoopCtx lctx(qres, ctx, qPreproc, aggregators, explain); if (!ctx.query.forcedSortOrder_.empty() && !qPreproc.MoreThanOneEvaluation()) { ctx.isForceAll = true; @@ -186,7 +181,7 @@ void NsSelecter::operator()(LocalQueryResults &result, SelectCtx &ctx, const Rdx qres.Append(OpAnd, SelectIterator(std::move(res), false, pr, IteratorFieldKind::None)); } break; case JoinPreResult::ModeIterators: - qres.LazyAppend(ctx.preResult->iterators.begin(), ctx.preResult->iterators.end()); + qres.Append(ctx.preResult->iterators.begin(), ctx.preResult->iterators.end()); break; case JoinPreResult::ModeValues: assertrx(0); @@ -215,7 +210,7 @@ void NsSelecter::operator()(LocalQueryResults &result, SelectCtx &ctx, const Rdx !ctx.sortingContext.sortIndex()) // 2. We have sorted query, by unordered index || ctx.preResult->btreeIndexOptimizationEnabled) { // 3. We have btree-index that is not committed yet ctx.preResult->iterators.Append(qres.cbegin(), qres.cend()); - if rx_unlikely (ctx.query.debugLevel >= LogInfo) { + if rx_unlikely (logLevel >= LogInfo) { logPrintf(LogInfo, "Built preResult (expected %d iterations) with %d iterators, q='%s'", maxIterations, qres.Size(), ctx.query.GetSQL()); } @@ -250,9 +245,9 @@ void NsSelecter::operator()(LocalQueryResults &result, SelectCtx &ctx, const Rdx bool hasComparators = false; qres.ExecuteAppropriateForEach( - Skip{}, - [&hasComparators](const FieldsComparator &) { hasComparators = true; }, - [&hasComparators](const SelectIterator &it) { + Skip{}, + [&hasComparators](const FieldsComparator &) noexcept { hasComparators = true; }, + [&hasComparators](const SelectIterator &it) noexcept { if (it.comparators_.size()) hasComparators = true; }); @@ -284,7 +279,7 @@ void NsSelecter::operator()(LocalQueryResults &result, SelectCtx &ctx, const Rdx qres.CheckFirstQuery(); // Rewind all results iterators - qres.ExecuteAppropriateForEach(Skip{}, + qres.ExecuteAppropriateForEach(Skip{}, [reverse, maxIterations](SelectIterator &it) { it.Start(reverse, maxIterations); }); // Let iterators choose most effecive algorith @@ -329,7 +324,7 @@ void NsSelecter::operator()(LocalQueryResults &result, SelectCtx &ctx, const Rdx // Get total count for simple query with 1 condition and 1 idset if (needCalcTotal && !lctx.calcTotal) { - if (!ctx.query.entries.Empty()) { + if (!ctx.query.Entries().Empty()) { result.totalCount += qres.Get(0).GetMaxIterations(); } else { result.totalCount += ns_->items_.size() - ns_->free_.size(); @@ -365,10 +360,10 @@ void NsSelecter::operator()(LocalQueryResults &result, SelectCtx &ctx, const Rdx } } // Put count/count_cached to aggretions - if (aggregationQueryRef.calcTotal != ModeNoTotal || containAggCount || containAggCountCached) { + if (aggregationQueryRef.HasCalcTotal() || containAggCount || containAggCountCached) { AggregationResult ret; ret.fields = {"*"}; - ret.type = (aggregationQueryRef.calcTotal == ModeAccurateTotal || containAggCount) ? AggCount : AggCountCached; + ret.type = (aggregationQueryRef.CalcTotal() == ModeAccurateTotal || containAggCount) ? AggCount : AggCountCached; if (ctx.isMergeQuerySubQuery()) { assertrx_throw(!result.aggregationResults.empty()); auto &agg = result.aggregationResults.back(); @@ -390,35 +385,35 @@ void NsSelecter::operator()(LocalQueryResults &result, SelectCtx &ctx, const Rdx explain.PutSelectors(&qresHolder.GetResultsRef()); explain.PutJoinedSelectors(ctx.joinedSelectors); - if rx_unlikely (ctx.query.debugLevel >= LogInfo) { + if rx_unlikely (logLevel >= LogInfo) { logPrintf(LogInfo, "%s", ctx.query.GetSQL()); - explain.LogDump(ctx.query.debugLevel); + explain.LogDump(logLevel); } - if (ctx.query.explain_) { + if (ctx.query.GetExplain()) { if (ctx.preResult && ctx.preResult->executionMode == JoinPreResult::ModeBuild) { ctx.preResult->explainPreSelect = explain.GetJSON(); } else { result.explainResults = explain.GetJSON(); } } - if rx_unlikely (ctx.query.debugLevel >= LogTrace) { + if rx_unlikely (logLevel >= LogTrace) { logPrintf(LogInfo, "Query returned: [%s]; total=%d", result.Dump(), result.totalCount); } if (needPutCachedTotal) { logPrintf(LogTrace, "[%s] put totalCount value into query cache: %d ", ns_->name_, result.totalCount); - ns_->queryTotalCountCache_->Put(ckey, {static_cast(result.totalCount - initTotalCount)}); + ns_->queryCountCache_->Put(ckey, {static_cast(result.totalCount - initTotalCount)}); } if (ctx.preResult && ctx.preResult->executionMode == JoinPreResult::ModeBuild) { switch (ctx.preResult->dataMode) { case JoinPreResult::ModeIdSet: - if rx_unlikely (ctx.query.debugLevel >= LogInfo) { + if rx_unlikely (logLevel >= LogInfo) { logPrintf(LogInfo, "Built idset preResult (expected %d iterations) with %d ids, q = '%s'", explain.Iterations(), ctx.preResult->ids.size(), ctx.query.GetSQL()); } break; case JoinPreResult::ModeValues: - if rx_unlikely (ctx.query.debugLevel >= LogInfo) { + if rx_unlikely (logLevel >= LogInfo) { logPrintf(LogInfo, "Built values preResult (expected %d iterations) with %d values, q = '%s'", explain.Iterations(), ctx.preResult->values.size(), ctx.query.GetSQL()); } @@ -491,7 +486,7 @@ class NsSelecter::JoinedNsValueGetter { template It NsSelecter::applyForcedSort(It begin, It end, const ItemComparator &compare, const SelectCtx &ctx, const joins::NamespaceResults *jr) { assertrx_throw(!ctx.sortingContext.entries.empty()); - if (ctx.query.mergeQueries_.size() > 1) throw Error(errLogic, "Force sort could not be applied to 'merged' queries."); + if (ctx.query.GetMergeQueries().size() > 1) throw Error(errLogic, "Force sort could not be applied to 'merged' queries."); return std::visit(overloaded{ [](const SortingContext::ExpressionEntry &) -> It { throw Error(errLogic, "Force sort could not be performed by expression."); @@ -631,65 +626,64 @@ class ForcedSortMap { static_cast(*this)); } const auto &operator*() const { - return std::visit(overloaded{[](MultiMap::Iterator it) -> const auto &{ return *it; - } - , [](SingleTypeMap::const_iterator it) -> const auto & { return *it; }}, + return std::visit(overloaded{[](MultiMap::Iterator it) -> const auto & { return *it; }, + [](SingleTypeMap::const_iterator it) -> const auto & { return *it; }}, static_cast(*this)); -} -}; // namespace reindexer + } + }; public: -ForcedSortMap(Variant k, mapped_type v, size_t size) - : data_{k.Type().Is() || k.Type().Is() || k.Type().IsNumeric() - ? DataType{MultiMap{size}} - : DataType{SingleTypeMap{{}, k.Type()}}} { - std::visit(overloaded{[&](MultiMap &m) { m.insert(std::move(k), v); }, [&](SingleTypeMap &m) { m.emplace(std::move(k), v); }}, data_); -} -std::pair emplace(Variant k, mapped_type v) & { - return std::visit(overloaded{[&](MultiMap &m) { - const auto [iter, success] = m.insert(std::move(k), v); - return std::make_pair(Iterator{iter}, success); - }, - [&](SingleTypeMap &m) { - if (!m.type_.IsSame(k.Type())) { - throw Error{errQueryExec, "Items of different types in forced sort list"}; - } - const auto [iter, success] = m.emplace(std::move(k), v); - return std::make_pair(Iterator{iter}, success); - }}, - data_); -} -bool contain(const Variant &k) const { - return std::visit(overloaded{[&k](const MultiMap &m) { return m.find(k) != m.cend(); }, - [&k](const SingleTypeMap &m) { - if (!m.type_.IsSame(k.Type())) { - throw Error{errQueryExec, "Items of different types in forced sort list"}; - } - return m.find(k) != m.end(); - }}, - data_); -} -mapped_type get(const Variant &k) const { - return std::visit(overloaded{[&k](const MultiMap &m) { - const auto it = m.find(k); - assertrx_throw(it != m.cend()); - return it->second; - }, - [&k](const SingleTypeMap &m) { - if (!m.type_.IsSame(k.Type())) { - throw Error{errQueryExec, "Items of different types in forced sort list"}; - } - const auto it = m.find(k); - assertrx_throw(it != m.end()); - return it->second; - }}, - data_); -} + ForcedSortMap(Variant k, mapped_type v, size_t size) + : data_{k.Type().Is() || k.Type().Is() || k.Type().IsNumeric() + ? DataType{MultiMap{size}} + : DataType{SingleTypeMap{{}, k.Type()}}} { + std::visit(overloaded{[&](MultiMap &m) { m.insert(std::move(k), v); }, [&](SingleTypeMap &m) { m.emplace(std::move(k), v); }}, + data_); + } + std::pair emplace(Variant k, mapped_type v) & { + return std::visit(overloaded{[&](MultiMap &m) { + const auto [iter, success] = m.insert(std::move(k), v); + return std::make_pair(Iterator{iter}, success); + }, + [&](SingleTypeMap &m) { + if (!m.type_.IsSame(k.Type())) { + throw Error{errQueryExec, "Items of different types in forced sort list"}; + } + const auto [iter, success] = m.emplace(std::move(k), v); + return std::make_pair(Iterator{iter}, success); + }}, + data_); + } + bool contain(const Variant &k) const { + return std::visit(overloaded{[&k](const MultiMap &m) { return m.find(k) != m.cend(); }, + [&k](const SingleTypeMap &m) { + if (!m.type_.IsSame(k.Type())) { + throw Error{errQueryExec, "Items of different types in forced sort list"}; + } + return m.find(k) != m.end(); + }}, + data_); + } + mapped_type get(const Variant &k) const { + return std::visit(overloaded{[&k](const MultiMap &m) { + const auto it = m.find(k); + assertrx_throw(it != m.cend()); + return it->second; + }, + [&k](const SingleTypeMap &m) { + if (!m.type_.IsSame(k.Type())) { + throw Error{errQueryExec, "Items of different types in forced sort list"}; + } + const auto it = m.find(k); + assertrx_throw(it != m.end()); + return it->second; + }}, + data_); + } private: -DataType data_; -} -; + DataType data_; +}; template class ForcedMapInserter { @@ -786,7 +780,7 @@ It NsSelecter::applyForcedSortImpl(NamespaceImpl &ns, It begin, It end, const It // implementation for composite indexes const auto &payloadType = ns.payloadType_; const FieldsSet &fields = ns.indexes_[idx]->Fields(); - unordered_payload_map sortMap(0, payloadType, fields); + unordered_payload_map sortMap(0, PayloadType{payloadType}, FieldsSet{fields}); ForcedMapInserter inserter{sortMap}; for (auto value : forcedSortOrder) { value.convert(fieldType, &payloadType, &fields); @@ -890,7 +884,7 @@ It NsSelecter::applyForcedSortImpl(NamespaceImpl &ns, It begin, It end, const It template void NsSelecter::applyGeneralSort(It itFirst, It itLast, It itEnd, const ItemComparator &comparator, const SelectCtx &ctx) { - if (ctx.query.mergeQueries_.size() > 1) { + if (ctx.query.GetMergeQueries().size() > 1) { throw Error(errLogic, "Sorting cannot be applied to merged queries."); } @@ -1044,7 +1038,7 @@ void NsSelecter::selectLoop(LoopCtx &ctx, ResultsT &result, const RdxContext &rd // Exclude last sets of id from each query result, so duplicated keys will // be removed for (auto &it : qres) { - if (it.HoldsOrReferTo() && it.Value().distinct) { + if (it.Is() && it.Value().distinct) { it.Value().ExcludeLastSet(pv, rowId, properRowId); } } @@ -1219,7 +1213,7 @@ void NsSelecter::addSelectResult(uint8_t proc, IdType rowId, IdType properRowId, void NsSelecter::checkStrictModeAgg(StrictMode strictMode, const std::string &name, const std::string &nsName, const TagsMatcher &tagsMatcher) const { - if (int index = IndexValueType::SetByJsonPath; ns_->getIndexByName(name, index)) return; + if (int index = IndexValueType::SetByJsonPath; ns_->tryGetIndexByName(name, index)) return; if (strictMode == StrictModeIndexes) { throw Error(errParams, @@ -1321,7 +1315,7 @@ void NsSelecter::prepareSortJoinedIndex(size_t nsIdx, std::string_view column, i .FieldByName(column, index); if (index == IndexValueType::SetByJsonPath) { skipSortingEntry |= !validateField( - strictMode, column, js.joinQuery_._namespace, + strictMode, column, js.joinQuery_.NsName(), js.preResult_->dataMode == JoinPreResult::ModeValues ? js.preResult_->values.tagsMatcher : js.rightNs_->tagsMatcher_); } } @@ -1350,8 +1344,9 @@ static void removeQuotesFromExpression(std::string &expression) { void NsSelecter::prepareSortingContext(SortingEntries &sortBy, SelectCtx &ctx, bool isFt, bool availableSelectBySortIndex) { using namespace SortExprFuncs; - const auto strictMode = - ctx.inTransaction ? StrictModeNone : ((ctx.query.strictMode == StrictModeNotSet) ? ns_->config_.strictMode : ctx.query.strictMode); + const auto strictMode = ctx.inTransaction + ? StrictModeNone + : ((ctx.query.GetStrictMode() == StrictModeNotSet) ? ns_->config_.strictMode : ctx.query.GetStrictMode()); static const JoinedSelectors emptyJoinedSelectors; const auto &joinedSelectors = ctx.joinedSelectors ? *ctx.joinedSelectors : emptyJoinedSelectors; ctx.sortingContext.entries.clear(); @@ -1401,7 +1396,7 @@ void NsSelecter::prepareSortingContext(SortingEntries &sortBy, SelectCtx &ctx, b ctx.isForceAll = true; } } else { - if (!ctx.query.mergeQueries_.empty()) { + if (!ctx.query.GetMergeQueries().empty()) { throw Error(errLogic, "Sorting by expression cannot be applied to merged queries."); } struct { @@ -1560,15 +1555,16 @@ size_t NsSelecter::calculateNormalCost(const QueryEntries &qentries, SelectCtx & next = qentries.Next(i); const bool calculateEntry = costCalculator.OnNewEntry(qentries, i, next); qentries.InvokeAppropriate( - i, Skip{}, [&costCalculator](const QueryEntriesBracket &) { costCalculator.MarkInapposite(); }, - [&costCalculator](const JoinQueryEntry &) { costCalculator.MarkInapposite(); }, - [&costCalculator](const BetweenFieldsQueryEntry &) { costCalculator.MarkInapposite(); }, + i, [](const SubQueryEntry &) { assertrx_throw(0); }, [](const SubQueryFieldEntry &) { assertrx_throw(0); }, + Skip{}, [&costCalculator](const QueryEntriesBracket &) noexcept { costCalculator.MarkInapposite(); }, + [&costCalculator](const JoinQueryEntry &) noexcept { costCalculator.MarkInapposite(); }, + [&costCalculator](const BetweenFieldsQueryEntry &) noexcept { costCalculator.MarkInapposite(); }, [&](const QueryEntry &qe) { - if (qe.idxNo < 0) { + if (!qe.IsFieldIndexed()) { costCalculator.MarkInapposite(); return; } - if (qe.idxNo == ctx.sortingContext.uncommitedIndex) { + if (qe.IndexNo() == ctx.sortingContext.uncommitedIndex) { if (sortIndexSearchState == SortIndexNotFound) { const bool isExpectingIdSet = qentries.GetOperation(i) == OpAnd && (next == sz || qentries.GetOperation(next) != OpOr); @@ -1590,7 +1586,7 @@ size_t NsSelecter::calculateNormalCost(const QueryEntries &qentries, SelectCtx & return; } - auto &index = ns_->indexes_[qe.idxNo]; + auto &index = ns_->indexes_[qe.IndexNo()]; if (IsFullText(index->Type())) { costCalculator.MarkInapposite(); return; @@ -1603,8 +1599,8 @@ size_t NsSelecter::calculateNormalCost(const QueryEntries &qentries, SelectCtx & opts.inTransaction = ctx.inTransaction; try { - SelectKeyResults reslts = index->SelectKey(qe.values, qe.condition, 0, opts, nullptr, rdxCtx); - costCalculator.Add(reslts, qe.idxNo == ctx.sortingContext.uncommitedIndex); + SelectKeyResults reslts = index->SelectKey(qe.Values(), qe.Condition(), 0, opts, nullptr, rdxCtx); + costCalculator.Add(reslts, qe.IndexNo() == ctx.sortingContext.uncommitedIndex); } catch (const Error &) { costCalculator.MarkInapposite(); } @@ -1627,11 +1623,13 @@ size_t NsSelecter::calculateOptimizedCost(size_t costNormal, const QueryEntries continue; } qentries.InvokeAppropriate( - i, Skip{}, [&costCalculator](const QueryEntriesBracket &) { costCalculator.MarkInapposite(); }, - [&costCalculator](const JoinQueryEntry &) { costCalculator.MarkInapposite(); }, - [&costCalculator](const BetweenFieldsQueryEntry &) { costCalculator.MarkInapposite(); }, + i, Skip{}, [](const SubQueryEntry &) { assertrx_throw(0); }, + [](const SubQueryFieldEntry &) { assertrx_throw(0); }, + [&costCalculator](const QueryEntriesBracket &) noexcept { costCalculator.MarkInapposite(); }, + [&costCalculator](const JoinQueryEntry &) noexcept { costCalculator.MarkInapposite(); }, + [&costCalculator](const BetweenFieldsQueryEntry &) noexcept { costCalculator.MarkInapposite(); }, [&](const QueryEntry &qe) { - if (qe.idxNo < 0 || qe.idxNo != ctx.sortingContext.uncommitedIndex) { + if (!qe.IsFieldIndexed() || qe.IndexNo() != ctx.sortingContext.uncommitedIndex) { costCalculator.MarkInapposite(); return; } @@ -1644,7 +1642,7 @@ size_t NsSelecter::calculateOptimizedCost(size_t costNormal, const QueryEntries opts.inTransaction = ctx.inTransaction; try { - SelectKeyResults reslts = ns_->indexes_[qe.idxNo]->SelectKey(qe.values, qe.condition, 0, opts, nullptr, rdxCtx); + SelectKeyResults reslts = ns_->indexes_[qe.IndexNo()]->SelectKey(qe.Values(), qe.Condition(), 0, opts, nullptr, rdxCtx); costCalculator.Add(reslts); } catch (const Error &) { costCalculator.MarkInapposite(); @@ -1659,9 +1657,9 @@ bool NsSelecter::isSortOptimizatonEffective(const QueryEntries &qentries, Select if (qentries.Size() == 0) { return true; } - if (qentries.Size() == 1 && qentries.HoldsOrReferTo(0)) { + if (qentries.Size() == 1 && qentries.Is(0)) { const auto &qe = qentries.Get(0); - if (qe.idxNo == ctx.sortingContext.uncommitedIndex) { + if (qe.IndexNo() == ctx.sortingContext.uncommitedIndex) { return SelectIteratorContainer::IsExpectingOrderedResults(qe); } } @@ -1695,8 +1693,8 @@ bool NsSelecter::isSortOptimizatonEffective(const QueryEntries &qentries, Select // TODO: It's possible to evaluate this multiplier, based on the query conditions, but the only way to avoid corner cases is to // allow user to hint this optimization. const size_t limitMultiplier = std::max(size_t(20), size_t(totalItemsCount / expectedMaxIterationsNormal) * 4); - const auto offset = ctx.query.HasOffset() ? ctx.query.start : 1; - costOptimized = limitMultiplier * (ctx.query.count + offset); + const auto offset = ctx.query.HasOffset() ? ctx.query.Offset() : 1; + costOptimized = limitMultiplier * (ctx.query.Limit() + offset); } return costOptimized <= costNormal; } diff --git a/cpp_src/core/nsselecter/nsselecter.h b/cpp_src/core/nsselecter/nsselecter.h index 667f8640d..50fe4c77e 100644 --- a/cpp_src/core/nsselecter/nsselecter.h +++ b/cpp_src/core/nsselecter/nsselecter.h @@ -11,7 +11,7 @@ enum class IsMergeQuery : bool { Yes = true, No = false }; enum class IsFTQuery { Yes, No, NotSet }; struct SelectCtx { - explicit SelectCtx(const Query &query_, const Query *parentQuery_) : query(query_), parentQuery(parentQuery_) {} + explicit SelectCtx(const Query &query_, const Query *parentQuery_) noexcept : query(query_), parentQuery(parentQuery_) {} const Query &query; JoinedSelectors *joinedSelectors = nullptr; SelectFunctionsHolder *functions = nullptr; @@ -28,6 +28,7 @@ struct SelectCtx { bool inTransaction = false; IsMergeQuery isMergeQuery = IsMergeQuery::No; IsFTQuery isFtQuery = IsFTQuery::NotSet; + QueryType crashReporterQueryType = QuerySelect; const Query *parentQuery = nullptr; ExplainCalc explain; diff --git a/cpp_src/core/nsselecter/querypreprocessor.cc b/cpp_src/core/nsselecter/querypreprocessor.cc index a7bffb666..2401bb467 100644 --- a/cpp_src/core/nsselecter/querypreprocessor.cc +++ b/cpp_src/core/nsselecter/querypreprocessor.cc @@ -1,7 +1,6 @@ #include "querypreprocessor.h" #include "core/index/index.h" -#include "core/index/indextext/indextext.h" #include "core/namespace/namespaceimpl.h" #include "core/nsselecter/joinedselector.h" #include "core/nsselecter/selectiteratorcontainer.h" @@ -21,26 +20,24 @@ QueryPreprocessor::QueryPreprocessor(QueryEntries &&queries, NamespaceImpl *ns, ns_(*ns), query_{ctx.query}, strictMode_(ctx.inTransaction ? StrictModeNone - : ((query_.strictMode == StrictModeNotSet) ? ns_.config_.strictMode : query_.strictMode)), - start_(query_.start), - count_(query_.count), + : ((query_.GetStrictMode() == StrictModeNotSet) ? ns_.config_.strictMode : query_.GetStrictMode())), + start_(query_.Offset()), + count_(query_.Limit()), forcedSortOrder_(!query_.forcedSortOrder_.empty()), reqMatchedOnce_(ctx.reqMatchedOnceFlag) { if (forcedSortOrder_ && (start_ > QueryEntry::kDefaultOffset || count_ < QueryEntry::kDefaultLimit)) { - assertrx(!query_.sortingEntries_.empty()); + assertrx_throw(!query_.sortingEntries_.empty()); static const std::vector emptyJoinedSelectors; const auto &sEntry = query_.sortingEntries_[0]; if (SortExpression::Parse(sEntry.expression, emptyJoinedSelectors).ByField()) { - QueryEntry qe; - qe.values.reserve(query_.forcedSortOrder_.size()); - for (const auto &v : query_.forcedSortOrder_) qe.values.push_back(v); - qe.condition = query_.forcedSortOrder_.size() == 1 ? CondEq : CondSet; - qe.index = sEntry.expression; - if (!ns_.getIndexByNameOrJsonPath(qe.index, qe.idxNo)) { - qe.idxNo = IndexValueType::SetByJsonPath; - } + VariantArray values; + values.reserve(query_.forcedSortOrder_.size()); + for (const auto &v : query_.forcedSortOrder_) values.push_back(v); desc_ = sEntry.desc; - Append(desc_ ? OpNot : OpAnd, std::move(qe)); + QueryField fld{sEntry.expression}; + SetQueryField(fld, ns_); + Append(desc_ ? OpNot : OpAnd, std::move(fld), query_.forcedSortOrder_.size() == 1 ? CondEq : CondSet, + std::move(values)); queryEntryAddedByForcedSortOptimization_ = true; } } @@ -58,9 +55,8 @@ void QueryPreprocessor::ExcludeFtQuery(const RdxContext &rdxCtx) { if (queryEntryAddedByForcedSortOptimization_ || Size() <= 1) return; for (auto it = begin(), next = it, endIt = end(); it != endIt; it = next) { ++next; - if (it->HoldsOrReferTo() && it->Value().idxNo != IndexValueType::SetByJsonPath) { - const auto indexNo = it->Value().idxNo; - auto &index = ns_.indexes_[indexNo]; + if (it->Is() && it->Value().IsFieldIndexed()) { + auto &index = ns_.indexes_[it->Value().IndexNo()]; if (!IsFastFullText(index->Type())) continue; if (it->operation != OpAnd || (next != endIt && next->operation == OpOr) || !index->EnablePreselectBeforeFt()) break; ftPreselect_ = index->FtPreselect(rdxCtx); @@ -80,16 +76,16 @@ bool QueryPreprocessor::NeedNextEvaluation(unsigned start, unsigned count, bool if (evaluationsCount_++) return false; if (queryEntryAddedByForcedSortOptimization_) { container_.back().operation = desc_ ? OpAnd : OpNot; - assertrx(start <= start_); + assertrx_throw(start <= start_); start_ = start; - assertrx(count <= count_); + assertrx_throw(count <= count_); count_ = count; return count_ || (reqMatchedOnce_ && !matchedAtLeastOnce); } else if (ftEntry_) { if (!matchedAtLeastOnce) return false; qresHolder.BackupContainer(); - start_ = query_.start; - count_ = query_.count; + start_ = query_.Offset(); + count_ = query_.Limit(); forcedSortOrder_ = !query_.forcedSortOrder_.empty(); clear(); Append(OpAnd, std::move(*ftEntry_)); @@ -101,19 +97,19 @@ bool QueryPreprocessor::NeedNextEvaluation(unsigned start, unsigned count, bool return false; } -void QueryPreprocessor::checkStrictMode(const std::string &index, int idxNo) const { - if (idxNo != IndexValueType::SetByJsonPath) return; +void QueryPreprocessor::checkStrictMode(const QueryField &field) const { + if (field.IsFieldIndexed()) return; switch (strictMode_) { case StrictModeIndexes: throw Error(errStrictMode, "Current query strict mode allows filtering by indexes only. There are no indexes with name '%s' in namespace '%s'", - index, ns_.name_); + field.FieldName(), ns_.name_); case StrictModeNames: - if (ns_.tagsMatcher_.path2tag(index).empty()) { + if (field.HaveEmptyField()) { throw Error(errStrictMode, "Current query strict mode allows filtering by existing fields only. There are no fields with name '%s' in " "namespace '%s'", - index, ns_.name_); + field.FieldName(), ns_.name_); } case StrictModeNotSet: case StrictModeNone: @@ -124,9 +120,9 @@ void QueryPreprocessor::checkStrictMode(const std::string &index, int idxNo) con class JoinOnExplainEnabled; class JoinOnExplainDisabled; -void QueryPreprocessor::InjectConditionsFromJoins(JoinedSelectors &js, OnConditionInjections &expalainOnInjections, +void QueryPreprocessor::InjectConditionsFromJoins(JoinedSelectors &js, OnConditionInjections &expalainOnInjections, LogLevel logLevel, const RdxContext &rdxCtx) { - bool needExplain = query_.explain_ || query_.debugLevel >= LogInfo; + const bool needExplain = query_.GetExplain() || logLevel >= LogInfo; if (needExplain) { injectConditionsFromJoins(0, container_.size(), js, expalainOnInjections, rdxCtx); } else { @@ -151,7 +147,7 @@ bool QueryPreprocessor::removeBrackets() { bool QueryPreprocessor::canRemoveBracket(size_t i) const { if (Size(i) < 2) { - throw Error{errParams, "Bracket cannot be empty"}; + throw Error{errQueryExec, "Bracket cannot be empty"}; } const size_t next = Next(i); const OpType op = GetOperation(i); @@ -162,7 +158,7 @@ bool QueryPreprocessor::canRemoveBracket(size_t i) const { size_t QueryPreprocessor::removeBrackets(size_t begin, size_t end) { if (begin != end && GetOperation(begin) == OpOr) { - throw Error{errParams, "First condition cannot be with operation OR"}; + throw Error{errQueryExec, "OR operator in first condition or after left join"}; } size_t deleted = 0; for (size_t i = begin; i < end - deleted; i = Next(i)) { @@ -181,38 +177,39 @@ size_t QueryPreprocessor::removeBrackets(size_t begin, size_t end) { void QueryPreprocessor::InitIndexNumbers() { ExecuteAppropriateForEach( - Skip{}, + Skip{}, [](const SubQueryEntry &) { assertrx(0); }, + [](const SubQueryFieldEntry &) { assertrx(0); }, [this](QueryEntry &entry) { - if (entry.idxNo == IndexValueType::NotSet) { - if (!ns_.getIndexByNameOrJsonPath(entry.index, entry.idxNo)) { - entry.idxNo = IndexValueType::SetByJsonPath; - } + if (!entry.FieldsHaveBeenSet()) { + SetQueryField(entry.FieldData(), ns_); } - checkStrictMode(entry.index, entry.idxNo); + checkStrictMode(entry.FieldData()); }, [this](BetweenFieldsQueryEntry &entry) { - if (entry.firstIdxNo == IndexValueType::NotSet) { - if (!ns_.getIndexByNameOrJsonPath(entry.firstIndex, entry.firstIdxNo)) { - entry.firstIdxNo = IndexValueType::SetByJsonPath; - } - } - checkStrictMode(entry.firstIndex, entry.firstIdxNo); - if (entry.secondIdxNo == IndexValueType::NotSet) { - if (!ns_.getIndexByNameOrJsonPath(entry.secondIndex, entry.secondIdxNo)) { - entry.secondIdxNo = IndexValueType::SetByJsonPath; - } + if (!entry.FieldsHaveBeenSet()) { + SetQueryField(entry.LeftFieldData(), ns_); + SetQueryField(entry.RightFieldData(), ns_); } - checkStrictMode(entry.secondIndex, entry.secondIdxNo); + checkStrictMode(entry.LeftFieldData()); + checkStrictMode(entry.RightFieldData()); }); } size_t QueryPreprocessor::lookupQueryIndexes(uint16_t dst, uint16_t srcBegin, uint16_t srcEnd) { - assertrx(dst <= srcBegin); + assertrx_throw(dst <= srcBegin); h_vector iidx(kMaxIndexes, uint16_t(0)); size_t merged = 0; for (size_t src = srcBegin, nextSrc; src < srcEnd; src = nextSrc) { nextSrc = Next(src); const bool changeDst = container_[src].InvokeAppropriate( + [](const SubQueryEntry &) -> bool { + assertrx_throw(0); + abort(); + }, + [](const SubQueryFieldEntry &) -> bool { + assertrx_throw(0); + abort(); + }, [&](const QueryEntriesBracket &) { if (dst != src) container_[dst] = std::move(container_[src]); const size_t mergedInBracket = lookupQueryIndexes(dst + 1, src + 1, nextSrc); @@ -221,17 +218,16 @@ size_t QueryPreprocessor::lookupQueryIndexes(uint16_t dst, uint16_t srcBegin, ui return true; }, [&](QueryEntry &entry) { - const bool isIndexField = (entry.idxNo >= 0); - if (isIndexField) { + if (entry.IsFieldIndexed()) { // try merge entries with AND opetator if ((GetOperation(src) == OpAnd) && (nextSrc >= srcEnd || GetOperation(nextSrc) != OpOr)) { - if (size_t(entry.idxNo) >= iidx.size()) { + if (size_t(entry.IndexNo()) >= iidx.size()) { const auto oldSize = iidx.size(); - iidx.resize(size_t(entry.idxNo) + 1); + iidx.resize(entry.IndexNo() + 1); std::fill(iidx.begin() + oldSize, iidx.begin() + iidx.size(), 0); } - auto &iidxRef = iidx[entry.idxNo]; - if (iidxRef > 0 && !ns_.indexes_[entry.idxNo]->Opts().IsArray()) { + auto &iidxRef = iidx[entry.IndexNo()]; + if (iidxRef > 0 && !ns_.indexes_[entry.IndexNo()]->Opts().IsArray()) { if (mergeQueryEntries(iidxRef - 1, src)) { ++merged; return false; @@ -256,6 +252,10 @@ size_t QueryPreprocessor::lookupQueryIndexes(uint16_t dst, uint16_t srcBegin, ui [dst, src, this](AlwaysFalse &) { if (dst != src) container_[dst] = std::move(container_[src]); return true; + }, + [dst, src, this](AlwaysTrue &) { + if (dst != src) container_[dst] = std::move(container_[src]); + return true; }); if (changeDst) dst = Next(dst); } @@ -264,21 +264,24 @@ size_t QueryPreprocessor::lookupQueryIndexes(uint16_t dst, uint16_t srcBegin, ui void QueryPreprocessor::CheckUniqueFtQuery() const { bool found = false; - ExecuteAppropriateForEach(Skip{}, [&](const QueryEntry &qe) { - if (qe.idxNo != IndexValueType::SetByJsonPath && IsFullText(ns_.indexes_[qe.idxNo]->Type())) { - if (found) { - throw Error{errParams, "Query cannot contain more than one full text condition"}; - } else { - found = true; + ExecuteAppropriateForEach( + Skip{}, + [](const SubQueryEntry &) { assertrx_throw(0); }, [](const SubQueryFieldEntry &) { assertrx_throw(0); }, + [&](const QueryEntry &qe) { + if (qe.IsFieldIndexed() && IsFullText(ns_.indexes_[qe.IndexNo()]->Type())) { + if (found) { + throw Error{errQueryExec, "Query cannot contain more than one full text condition"}; + } else { + found = true; + } } - } - }); + }); } bool QueryPreprocessor::ContainsFullTextIndexes() const { for (auto it = cbegin().PlainIterator(), end = cend().PlainIterator(); it != end; ++it) { - if (it->HoldsOrReferTo() && it->Value().idxNo != IndexValueType::SetByJsonPath && - IsFullText(ns_.indexes_[it->Value().idxNo]->Type())) { + if (it->Is() && it->Value().IsFieldIndexed() && + IsFullText(ns_.indexes_[it->Value().IndexNo()]->Type())) { return true; } } @@ -321,11 +324,14 @@ static void createCompositeKeyValues(const h_vector } } -static void createCompositeKeyValues(const h_vector, 4> &values, const PayloadType &plType, - VariantArray &ret) { +static VariantArray createCompositeKeyValues(const h_vector, 4> &values, const PayloadType &plType, + uint32_t resultSetSize) { PayloadValue d(plType.TotalSize()); Payload pl(plType, d); + VariantArray ret; + ret.reserve(resultSetSize); createCompositeKeyValues(values, plType, pl, ret, 0); + return ret; } size_t QueryPreprocessor::substituteCompositeIndexes(const size_t from, const size_t to) { @@ -342,7 +348,7 @@ size_t QueryPreprocessor::substituteCompositeIndexes(const size_t from, const si deleted += substituteCompositeIndexes(cur + 1, cur + bracketSize); continue; } - if (!HoldsOrReferTo(cur) || GetOperation(cur) != OpAnd) { + if (!Is(cur) || GetOperation(cur) != OpAnd) { continue; } const auto next = Next(cur); @@ -350,15 +356,16 @@ size_t QueryPreprocessor::substituteCompositeIndexes(const size_t from, const si continue; } auto &qe = Get(cur); - if ((qe.condition != CondEq && qe.condition != CondSet) || qe.idxNo >= ns_.payloadType_.NumFields() || qe.idxNo < 0) { + if ((qe.Condition() != CondEq && qe.Condition() != CondSet) || !qe.IsFieldIndexed() || + qe.IndexNo() >= ns_.payloadType_.NumFields()) { continue; } - const std::vector *found = getCompositeIndex(qe.idxNo); + const std::vector *found = getCompositeIndex(qe.IndexNo()); if (!found || found->empty()) { continue; } - searcher.Add(qe.idxNo, *found, cur); + searcher.Add(qe.IndexNo(), *found, cur); } EntriesRanges deleteRanges; @@ -371,14 +378,13 @@ size_t QueryPreprocessor::substituteCompositeIndexes(const size_t from, const si uint32_t maxSetSize = 0; for (auto i : res.entries) { auto &qe = Get(i); - if rx_unlikely (!res.fields.contains(qe.idxNo)) { + if rx_unlikely (!res.fields.contains(qe.IndexNo())) { throw Error(errLogic, "Error during composite index's fields substitution (this should not happen)"); } - - maxSetSize = std::max(maxSetSize, qe.values.size()); - resultSetSize = (resultSetSize == 0) ? qe.values.size() : (resultSetSize * qe.values.size()); + maxSetSize = std::max(maxSetSize, qe.Values().size()); + resultSetSize = (resultSetSize == 0) ? qe.Values().size() : (resultSetSize * qe.Values().size()); } - static const CompositeValuesCountLimits kCompositeSetLimits; + constexpr static CompositeValuesCountLimits kCompositeSetLimits; if (resultSetSize != maxSetSize) { // Do not perform substitution if result set size becoms larger than initial indexes set size // and this size is greater than limit @@ -390,22 +396,17 @@ size_t QueryPreprocessor::substituteCompositeIndexes(const size_t from, const si } for (auto i : res.entries) { auto &qe = Get(i); - const auto idxKeyType = ns_.indexes_[qe.idxNo]->KeyType(); - for (auto &v : qe.values) { - v.convert(idxKeyType); - } - values.emplace_back(qe.idxNo, std::move(qe.values)); + qe.ConvertValuesToFieldType(); + const int idxNo = qe.IndexNo(); + values.emplace_back(idxNo, std::move(qe).Values()); } { - QueryEntry ce(CondSet, ns_.indexes_[res.idx]->Name(), res.idx); - ce.values.reserve(resultSetSize); - createCompositeKeyValues(values, ns_.payloadType_, ce.values); - if (ce.values.size() == 1) { - ce.condition = CondEq; - } + VariantArray qValues = createCompositeKeyValues(values, ns_.payloadType_, resultSetSize); const auto first = res.entries.front(); SetOperation(OpAnd, first); - container_[first].SetValue(std::move(ce)); + QueryField fld{ns_.indexes_[res.idx]->Name()}; + setQueryIndex(fld, res.idx, ns_); + container_[first].Emplace(std::move(fld), qValues.size() == 1 ? CondEq : CondSet, std::move(qValues)); } deleteRanges.Add(span(res.entries.data() + 1, res.entries.size() - 1)); resIdx = searcher.RemoveUsedAndGetNext(resIdx); @@ -417,29 +418,15 @@ size_t QueryPreprocessor::substituteCompositeIndexes(const size_t from, const si return deleted; } -void QueryPreprocessor::convertWhereValues(QueryEntry *qe) const { - const FieldsSet *fields = nullptr; - KeyValueType keyType{KeyValueType::Undefined{}}; - const bool isIndexField = (qe->idxNo != IndexValueType::SetByJsonPath); - if (isIndexField) { - keyType = ns_.indexes_[qe->idxNo]->SelectKeyType(); - fields = &ns_.indexes_[qe->idxNo]->Fields(); - } - if (!keyType.Is()) { - if (qe->condition != CondDWithin) { - for (auto &key : qe->values) { - key.convert(keyType, &ns_.payloadType_, fields); - } - } - } -} +void QueryPreprocessor::convertWhereValues(QueryEntry &qe) const { qe.ConvertValuesToFieldType(ns_.payloadType_); } void QueryPreprocessor::convertWhereValues(QueryEntries::iterator begin, QueryEntries::iterator end) const { for (auto it = begin; it != end; ++it) { it->InvokeAppropriate( - Skip{}, + Skip{}, [](const SubQueryEntry &) { assertrx_throw(0); }, + [](const SubQueryFieldEntry &) { assertrx_throw(0); }, [this, &it](const QueryEntriesBracket &) { convertWhereValues(it.begin(), it.end()); }, - [this](QueryEntry &qe) { convertWhereValues(&qe); }); + [this](QueryEntry &qe) { convertWhereValues(qe); }); } } @@ -476,25 +463,34 @@ void QueryPreprocessor::findMaxIndex(QueryEntries::const_iterator begin, QueryEn h_vector &foundIndexes) const { for (auto it = begin; it != end; ++it) { const FoundIndexInfo foundIdx = it->InvokeAppropriate( + [](const SubQueryEntry &) -> FoundIndexInfo { + assertrx_throw(0); + abort(); + }, + [](const SubQueryFieldEntry &) -> FoundIndexInfo { + assertrx_throw(0); + abort(); + }, [this, &it, &foundIndexes](const QueryEntriesBracket &) { findMaxIndex(it.cbegin(), it.cend(), foundIndexes); return FoundIndexInfo(); }, [this](const QueryEntry &entry) -> FoundIndexInfo { - if (entry.idxNo != IndexValueType::SetByJsonPath && !entry.distinct) { - const auto idxPtr = ns_.indexes_[entry.idxNo].get(); + if (entry.IsFieldIndexed() && !entry.Distinct()) { + const auto idxPtr = ns_.indexes_[entry.IndexNo()].get(); if (idxPtr->IsOrdered() && !idxPtr->Opts().IsArray()) { - if (IsOrderedCondition(entry.condition)) { + if (IsOrderedCondition(entry.Condition())) { return FoundIndexInfo{idxPtr, FoundIndexInfo::ConditionType::Compatible}; - } else if (entry.condition == CondAny || entry.values.size() > 1) { + } else if (entry.Condition() == CondAny || entry.Values().size() > 1) { return FoundIndexInfo{idxPtr, FoundIndexInfo::ConditionType::Incompatible}; } } } return FoundIndexInfo(); }, - [](const JoinQueryEntry &) { return FoundIndexInfo(); }, [](const BetweenFieldsQueryEntry &) { return FoundIndexInfo(); }, - [](const AlwaysFalse &) { return FoundIndexInfo(); }); + [](const JoinQueryEntry &) noexcept { return FoundIndexInfo(); }, + [](const BetweenFieldsQueryEntry &) noexcept { return FoundIndexInfo(); }, + [](const AlwaysFalse &) noexcept { return FoundIndexInfo(); }, [](const AlwaysTrue &) noexcept { return FoundIndexInfo(); }); if (foundIdx.index) { auto found = std::find_if(foundIndexes.begin(), foundIndexes.end(), [foundIdx](const FoundIndexInfo &i) { return i.index == foundIdx.index; }); @@ -510,42 +506,38 @@ void QueryPreprocessor::findMaxIndex(QueryEntries::const_iterator begin, QueryEn bool QueryPreprocessor::mergeQueryEntries(size_t lhs, size_t rhs) { QueryEntry *lqe = &Get(lhs); QueryEntry &rqe = Get(rhs); - if ((lqe->condition == CondEq || lqe->condition == CondSet) && (rqe.condition == CondEq || rqe.condition == CondSet)) { + if ((lqe->Condition() == CondEq || lqe->Condition() == CondSet) && (rqe.Condition() == CondEq || rqe.Condition() == CondSet)) { // intersect 2 queryentries on the same index - if rx_unlikely (lqe->values.empty()) { + if rx_unlikely (lqe->Values().empty()) { return true; } - if (container_[lhs].IsRef()) { - container_[lhs].SetValue(const_cast(*lqe)); - lqe = &Get(lhs); - } + const bool distinct = lqe->Distinct() || rqe.Distinct(); VariantArray setValues; - if (rx_likely(!rqe.values.empty())) { - convertWhereValues(lqe); - convertWhereValues(&rqe); - VariantArray *first = &lqe->values; - VariantArray *second = &rqe.values; - if (lqe->values.size() > rqe.values.size()) { - std::swap(first, second); - } - setValues.reserve(first->size()); + if (rx_likely(!rqe.Values().empty())) { + convertWhereValues(*lqe); + convertWhereValues(rqe); + auto &&[first, second] = lqe->Values().size() < rqe.Values().size() + ? std::make_pair(std::move(*lqe).Values(), std::move(rqe).Values()) + : std::make_pair(std::move(rqe).Values(), std::move(*lqe).Values()); + + setValues.reserve(first.size()); constexpr size_t kMinArraySizeToUseHashSet = 250; - if (second->size() < kMinArraySizeToUseHashSet) { + if (second.size() < kMinArraySizeToUseHashSet) { // Intersect via binary search + sort for small vectors - std::sort(first->begin(), first->end()); - for (auto &&v : *second) { - if (std::binary_search(first->begin(), first->end(), v)) { + boost::sort::pdqsort(first.begin(), first.end()); + for (auto &&v : second) { + if (std::binary_search(first.begin(), first.end(), v)) { setValues.emplace_back(std::move(v)); } } } else { // Intersect via hash_set for large vectors reindexer::fast_hash_set set; - set.reserve(first->size() * 2); - for (auto &&v : *first) { + set.reserve(first.size() * 2); + for (auto &&v : first) { set.emplace(std::move(v)); } - for (auto &&v : *second) { + for (auto &&v : second) { if (set.erase(v)) { setValues.emplace_back(std::move(v)); } @@ -553,27 +545,18 @@ bool QueryPreprocessor::mergeQueryEntries(size_t lhs, size_t rhs) { } } - lqe->values = std::move(setValues); - lqe->condition = (lqe->values.size() == 1) ? CondEq : CondSet; - lqe->distinct |= rqe.distinct; + lqe->SetCondAndValues(CondSet, std::move(setValues)); + lqe->Distinct(distinct); return true; - } else if (rqe.condition == CondAny) { - if (!lqe->distinct && rqe.distinct) { - if (container_[lhs].IsRef()) { - container_[lhs].SetValue(const_cast(*lqe)); - lqe = &Get(lhs); - } - lqe->distinct = true; + } else if (rqe.Condition() == CondAny) { + if (!lqe->Distinct() && rqe.Distinct()) { + lqe->Distinct(true); } return true; - } else if (lqe->condition == CondAny) { - const bool distinct = lqe->distinct || rqe.distinct; - if (container_[rhs].IsRef()) { - container_[lhs].SetValue(const_cast(rqe)); - } else { - container_[lhs].SetValue(std::move(rqe)); - } - Get(lhs).distinct = distinct; + } else if (lqe->Condition() == CondAny) { + const bool distinct = lqe->Distinct() || rqe.Distinct(); + container_[lhs].SetValue(std::move(rqe)); + Get(lhs).Distinct(distinct); return true; } @@ -584,19 +567,16 @@ void QueryPreprocessor::AddDistinctEntries(const h_vector &aggreg bool wasAdded = false; for (auto &ag : aggregators) { if (ag.Type() != AggDistinct) continue; - QueryEntry qe; - assertrx(ag.Names().size() == 1); - qe.index = ag.Names()[0]; - qe.condition = CondAny; - qe.distinct = true; - Append(wasAdded ? OpOr : OpAnd, std::move(qe)); + assertrx_throw(ag.Names().size() == 1); + Append(wasAdded ? OpOr : OpAnd, ag.Names()[0], QueryEntry::DistinctTag{}); wasAdded = true; } } -void QueryPreprocessor::fillQueryEntryFromOnCondition(QueryEntry &queryEntry, std::string &explainStr, AggType &oAggType, - NamespaceImpl &rightNs, Query joinQuery, std::string joinIndex, CondType condition, - KeyValueType valuesType, const RdxContext &rdxCtx) { +std::pair QueryPreprocessor::queryValuesFromOnCondition(std::string &explainStr, AggType &oAggType, + NamespaceImpl &rightNs, Query joinQuery, + const QueryJoinEntry &joinEntry, CondType condition, + const RdxContext &rdxCtx) { size_t limit; const auto &rNsCfg = rightNs.Config(); if (rNsCfg.maxPreselectSize == 0) { @@ -607,26 +587,26 @@ void QueryPreprocessor::fillQueryEntryFromOnCondition(QueryEntry &queryEntry, st limit = std::min(std::max(rNsCfg.minPreselectSize, rightNs.ItemsCount() * rNsCfg.maxPreselectPart), rNsCfg.maxPreselectSize); } - joinQuery.explain_ = query_.explain_; - joinQuery.count = limit + 2; - joinQuery.start = 0; + joinQuery.Explain(query_.GetExplain()); + joinQuery.Limit(limit + 2); + joinQuery.Offset(QueryEntry::kDefaultOffset); joinQuery.sortingEntries_.clear(); joinQuery.forcedSortOrder_.clear(); joinQuery.aggregations_.clear(); switch (condition) { case CondEq: case CondSet: - joinQuery.Distinct(std::move(joinIndex)); + joinQuery.Distinct(joinEntry.RightFieldName()); oAggType = AggType::AggDistinct; break; case CondLt: case CondLe: - joinQuery.Aggregate(AggMax, {std::move(joinIndex)}); + joinQuery.Aggregate(AggMax, {joinEntry.RightFieldName()}); oAggType = AggType::AggMax; break; case CondGt: case CondGe: - joinQuery.Aggregate(AggMin, {std::move(joinIndex)}); + joinQuery.Aggregate(AggMin, {joinEntry.RightFieldName()}); oAggType = AggType::AggMin; break; case CondAny: @@ -635,104 +615,87 @@ void QueryPreprocessor::fillQueryEntryFromOnCondition(QueryEntry &queryEntry, st case CondEmpty: case CondLike: case CondDWithin: - throw Error(errParams, "Unsupported condition in ON statment: %s", CondTypeToStr(condition)); + throw Error(errQueryExec, "Unsupported condition in ON statment: %s", CondTypeToStr(condition)); } SelectCtx ctx{joinQuery, nullptr}; LocalQueryResults qr; rightNs.Select(qr, ctx, rdxCtx); - if (qr.Count() > limit) return; - assertrx(qr.aggregationResults.size() == 1); + if (qr.Count() > limit) return {CondAny, {}}; + assertrx_throw(qr.aggregationResults.size() == 1); + auto &aggRes = qr.aggregationResults[0]; explainStr = qr.explainResults; switch (condition) { case CondEq: case CondSet: { - assertrx(qr.aggregationResults[0].type == AggDistinct); - queryEntry.values.reserve(qr.aggregationResults[0].distincts.size()); - assertrx(qr.aggregationResults[0].distinctsFields.size() == 1); - const auto field = qr.aggregationResults[0].distinctsFields[0]; - for (Variant &distValue : qr.aggregationResults[0].distincts) { + assertrx_throw(aggRes.type == AggDistinct); + VariantArray values; + values.reserve(aggRes.distincts.size()); + for (Variant &distValue : aggRes.distincts) { if (distValue.Type().Is()) { - ConstPayload pl(qr.aggregationResults[0].payloadType, distValue.operator const PayloadValue &()); - VariantArray v; - if (field == IndexValueType::SetByJsonPath) { - assertrx(qr.aggregationResults[0].distinctsFields.getTagsPathsLength() == 1); - pl.GetByJsonPath(qr.aggregationResults[0].distinctsFields.getTagsPath(0), v, valuesType); - } else { - pl.Get(field, v); - } - assertrx(v.size() == 1); - queryEntry.values.emplace_back(std::move(v[0])); + ConstPayload pl(aggRes.payloadType, distValue.operator const PayloadValue &()); + values.emplace_back(pl.GetComposite(aggRes.distinctsFields, joinEntry.RightCompositeFieldsTypes())); } else { - queryEntry.values.emplace_back(std::move(distValue)); + values.emplace_back(std::move(distValue)); } } - queryEntry.condition = (queryEntry.values.size() == 1) ? CondEq : CondSet; - break; + return {CondSet, std::move(values)}; } case CondLt: case CondLe: case CondGt: case CondGe: - if (auto value = qr.aggregationResults[0].GetValue()) { - queryEntry.condition = condition; - queryEntry.values.emplace_back(*value); + if (auto value = aggRes.GetValue()) { + return {condition, {Variant{*value}}}; + } else { + return {CondAny, {}}; } - break; case CondAny: case CondRange: case CondAllSet: case CondEmpty: case CondLike: case CondDWithin: - throw Error(errParams, "Unsupported condition in ON statment: %s", CondTypeToStr(condition)); + default: + throw Error(errQueryExec, "Unsupported condition in ON statment: %s", CondTypeToStr(condition)); } } -template -void QueryPreprocessor::fillQueryEntryFromOnCondition(QueryEntry &queryEntry, std::string_view joinIndex, CondType condition, - const JoinedSelector &joinedSelector, KeyValueType valuesType, const int rightIdxNo, - const CollateOpts &collate) { - JoinPreResult::Values &values = joinedSelector.preResult_->values; +std::pair QueryPreprocessor::queryValuesFromOnCondition(CondType condition, const QueryJoinEntry &joinEntry, + const JoinedSelector &joinedSelector, + const CollateOpts &collate) { switch (condition) { case CondEq: - case CondSet: { - joinedSelector.readValuesFromPreResult(queryEntry.values, valuesType, rightIdxNo, joinIndex); - queryEntry.condition = (queryEntry.values.size() == 1) ? CondEq : CondSet; - return; - } + case CondSet: + return {CondSet, joinedSelector.readValuesFromPreResult(joinEntry)}; case CondLt: case CondLe: case CondGt: case CondGe: { - queryEntry.condition = condition; - VariantArray buffer; + const JoinPreResult::Values &values = joinedSelector.preResult_->values; + VariantArray buffer, keyValues; for (const ItemRef &item : values) { - buffer.clear(); - assertrx(!item.Value().IsFree()); + assertrx_throw(!item.Value().IsFree()); const ConstPayload pl{values.payloadType, item.Value()}; - if constexpr (byJsonPath) { - pl.GetByJsonPath(joinIndex, values.tagsMatcher, buffer, valuesType); - } else { - pl.Get(rightIdxNo, buffer); - } + pl.GetByFieldsSet(joinEntry.RightFields(), buffer, joinEntry.RightFieldType(), joinEntry.RightCompositeFieldsTypes()); for (Variant &v : buffer) { - if (queryEntry.values.empty()) { - queryEntry.values.emplace_back(std::move(v)); + if (keyValues.empty()) { + keyValues.emplace_back(std::move(v)); } else { - const auto cmp = queryEntry.values[0].Compare(v, collate); + const auto cmp = keyValues[0].Compare(v, collate); if (condition == CondLt || condition == CondLe) { if (cmp < 0) { - queryEntry.values[0] = std::move(v); + keyValues[0] = std::move(v); } } else { if (cmp > 0) { - queryEntry.values[0] = std::move(v); + keyValues[0] = std::move(v); } } } } } + return {condition, std::move(keyValues)}; } break; case CondAny: case CondRange: @@ -740,7 +703,8 @@ void QueryPreprocessor::fillQueryEntryFromOnCondition(QueryEntry &queryEntry, st case CondEmpty: case CondLike: case CondDWithin: - throw Error(errParams, "Unsupported condition in ON statment: %s", CondTypeToStr(condition)); + default: + throw Error(errQueryExec, "Unsupported condition in ON statment: %s", CondTypeToStr(condition)); } } @@ -752,6 +716,7 @@ void QueryPreprocessor::briefDump(size_t from, size_t to, const std::vector ser << container_[it].operation << ' '; } container_[it].InvokeAppropriate( + [](const SubQueryEntry &) { assertrx_throw(0); }, [](const SubQueryFieldEntry &) { assertrx_throw(0); }, [&](const QueryEntriesBracket &b) { ser << "("; briefDump(it + 1, Next(it), joinedSelectors, ser); @@ -761,7 +726,7 @@ void QueryPreprocessor::briefDump(size_t from, size_t to, const std::vector [&ser](const QueryEntry &qe) { ser << qe.DumpBrief() << ' '; }, [&joinedSelectors, &ser](const JoinQueryEntry &jqe) { ser << jqe.Dump(joinedSelectors) << ' '; }, [&ser](const BetweenFieldsQueryEntry &qe) { ser << qe.Dump() << ' '; }, - [&ser](const AlwaysFalse &) { ser << "AlwaysFalse" << ' '; }); + [&ser](const AlwaysFalse &) { ser << "AlwaysFalse" << ' '; }, [&ser](const AlwaysTrue &) { ser << "AlwaysTrue" << ' '; }); } } } @@ -774,7 +739,8 @@ size_t QueryPreprocessor::injectConditionsFromJoins(size_t from, size_t to, Join size_t injectedCount = 0; for (size_t cur = from; cur < to; cur = Next(cur)) { container_[cur].InvokeAppropriate( - Skip{}, + [](const SubQueryEntry &) { assertrx_throw(0); }, [](const SubQueryFieldEntry &) { assertrx_throw(0); }, + Skip{}, [&](const QueryEntriesBracket &) { size_t injCount = injectConditionsFromJoins(cur + 1, Next(cur), js, explainOnInjections, rdxCtx); to += injCount; @@ -782,8 +748,7 @@ size_t QueryPreprocessor::injectConditionsFromJoins(size_t from, size_t to, Join assertrx_throw(to <= container_.size()); }, [&](const JoinQueryEntry &jqe) { - assertrx(js.size() > jqe.joinIndex); - + assertrx_throw(js.size() > jqe.joinIndex); JoinedSelector &joinedSelector = js[jqe.joinIndex]; const bool byValues = joinedSelector.PreResult() && joinedSelector.PreResult()->dataMode == JoinPreResult::ModeValues; @@ -803,14 +768,13 @@ size_t QueryPreprocessor::injectConditionsFromJoins(size_t from, size_t to, Join return; } } - const auto &joinEntries = joinedSelector.joinQuery_.joinEntries_; // LeftJoin-s shall not be in QueryEntries container_ by construction - assertrx(joinedSelector.Type() == InnerJoin || joinedSelector.Type() == OrInnerJoin); + assertrx_throw(joinedSelector.Type() == InnerJoin || joinedSelector.Type() == OrInnerJoin); // Checking if we have anything to inject into main Where clause bool foundANDOrOR = false; for (const auto &je : joinEntries) { - if (je.op_ != OpNot) { + if (je.Operation() != OpNot) { foundANDOrOR = true; break; } @@ -822,7 +786,7 @@ size_t QueryPreprocessor::injectConditionsFromJoins(size_t from, size_t to, Join OpType op = GetOperation(cur); if (joinedSelector.Type() == OrInnerJoin) { - if (op == OpNot) throw Error(errParams, "OR INNER JOIN with operation NOT"); + if (op == OpNot) throw Error(errQueryExec, "OR INNER JOIN with operation NOT"); op = OpOr; joinedSelector.SetType(InnerJoin); } @@ -832,6 +796,8 @@ size_t QueryPreprocessor::injectConditionsFromJoins(size_t from, size_t to, Join // !!!Warning jqe reference will be invalidated after EncloseInBracket EncloseInBracket(cur, cur + 1, op); ++cur; + ++to; + ++injectedCount; explainJoinOn.ReserveOnEntries(joinEntries.size()); @@ -840,12 +806,10 @@ size_t QueryPreprocessor::injectConditionsFromJoins(size_t from, size_t to, Join size_t orChainLength = 0; for (size_t i = 0, s = joinEntries.size(); i < s; ++i) { const QueryJoinEntry &joinEntry = joinEntries[i]; - auto explainEntry = explainJoinOn.AppendOnEntryExplain(); explainEntry.InitialCondition(joinEntry, joinedSelector); - - CondType condition = joinEntry.condition_; - OpType operation = joinEntry.op_; + CondType condition = joinEntry.Condition(); + OpType operation = joinEntry.Operation(); switch (operation) { case OpNot: orChainLength = 0; @@ -873,7 +837,7 @@ size_t QueryPreprocessor::injectConditionsFromJoins(size_t from, size_t to, Join case CondEmpty: case CondLike: case CondDWithin: - throw Error(errParams, "Unsupported condition in ON statment: %s", CondTypeToStr(condition)); + throw Error(errQueryExec, "Unsupported condition in ON statment: %s", CondTypeToStr(condition)); } operation = OpAnd; break; @@ -888,67 +852,67 @@ size_t QueryPreprocessor::injectConditionsFromJoins(size_t from, size_t to, Join orChainLength = 0; break; } - - QueryEntry newEntry; - newEntry.index = joinEntry.index_; - newEntry.idxNo = IndexValueType::SetByJsonPath; - KeyValueType valuesType = KeyValueType::Undefined{}; - CollateOpts collate; - if (ns_.getIndexByNameOrJsonPath(newEntry.index, newEntry.idxNo)) { - const Index &index = *ns_.indexes_[newEntry.idxNo]; - valuesType = index.SelectKeyType(); - collate = index.Opts().collateOpts_; - } - + CondType queryCondition{CondAny}; + VariantArray values; if (byValues) { - assertrx(joinedSelector.itemQuery_.entries.HoldsOrReferTo(i)); - const QueryEntry &qe = joinedSelector.itemQuery_.entries.Get(i); - assertrx(qe.index == joinEntry.joinIndex_); - const int rightIdxNo = qe.idxNo; - if (rightIdxNo == IndexValueType::SetByJsonPath) { - fillQueryEntryFromOnCondition(newEntry, joinEntry.joinIndex_, condition, joinedSelector, valuesType, - rightIdxNo, collate); - } else { - fillQueryEntryFromOnCondition(newEntry, joinEntry.joinIndex_, condition, joinedSelector, valuesType, - rightIdxNo, collate); + assertrx_throw(joinedSelector.itemQuery_.Entries().Is(i)); + assertrx_throw(joinedSelector.itemQuery_.Entries().Get(i).FieldName() == joinEntry.RightFieldName()); + CollateOpts collate; + if (joinEntry.IsLeftFieldIndexed()) { + collate = ns_.indexes_[joinEntry.LeftIdxNo()]->Opts().collateOpts_; } + std::tie(queryCondition, values) = queryValuesFromOnCondition(condition, joinEntry, joinedSelector, collate); } else { bool skip = false; switch (condition) { + case CondAny: + case CondEmpty: + case CondLike: + case CondDWithin: + explainEntry.Skipped("Skipped due to unsupperted on condition"sv); + skip = true; + break; + case CondRange: case CondLt: case CondLe: case CondGt: - case CondGe: { - const QueryEntry &qe = joinedSelector.itemQuery_.entries.Get(i); - skip = qe.idxNo != IndexValueType::SetByJsonPath && joinedSelector.RightNs()->indexes_[qe.idxNo]->IsUuid(); - if (skip) { - explainEntry.Skipped("Skipped due to condition Lt|Le|Gt|Ge with UUID index field."sv); - } + case CondGe: + joinedSelector.itemQuery_.Entries().Get(i).FieldType().EvaluateOneOf( + [&skip, + &explainEntry](OneOf) noexcept { + skip = true; + explainEntry.Skipped( + "Skipped due to condition Lt|Le|Gt|Ge|Range with not indexed or not numeric field."sv); + }, + [](OneOf) noexcept { + }); break; - } case CondEq: case CondSet: case CondAllSet: - case CondAny: - case CondEmpty: - case CondRange: - case CondLike: - case CondDWithin: + joinedSelector.itemQuery_.Entries().Get(i).FieldType().EvaluateOneOf( + [&skip, &explainEntry](OneOf) noexcept { + skip = true; + explainEntry.Skipped("Skipped due to condition Eq|Set|AllSet with composite index."sv); + }, + [](OneOf) noexcept {}); break; } if (!skip) { std::string explainSelect; AggType selectAggType; - fillQueryEntryFromOnCondition(newEntry, explainSelect, selectAggType, *joinedSelector.RightNs(), - joinedSelector.JoinQuery(), joinEntry.joinIndex_, condition, valuesType, rdxCtx); + std::tie(queryCondition, values) = + queryValuesFromOnCondition(explainSelect, selectAggType, *joinedSelector.RightNs(), + joinedSelector.JoinQuery(), joinEntry, condition, rdxCtx); explainEntry.ExplainSelect(std::move(explainSelect), selectAggType); } } - - if (!newEntry.values.empty()) { - explainEntry.Succeed(newEntry); - - Insert(cur, operation, std::move(newEntry)); + if (!values.empty()) { + Insert(cur, operation, QueryEntry{QueryField(joinEntry.LeftFieldData()), queryCondition, std::move(values)}); + explainEntry.Succeed(Get(cur)); ++cur; ++count; prevIsSkipped = false; @@ -956,6 +920,7 @@ size_t QueryPreprocessor::injectConditionsFromJoins(size_t from, size_t to, Join explainEntry.Skipped("Skipped as cannot obtain values from right namespace."sv); if (operation == OpOr) { Erase(cur - orChainLength, cur); + cur -= orChainLength; count -= orChainLength; // Marking On-injections as fail for removed entries. explainJoinOn.FailOnEntriesAsOrChain(orChainLength); @@ -971,8 +936,10 @@ size_t QueryPreprocessor::injectConditionsFromJoins(size_t from, size_t to, Join [this, cur, count, &js](WrSerializer &ser) { briefDump(cur - count, Next(cur - count), js, ser); }); ++cur; - injectedCount += count + 2; - to += count + 2; + injectedCount += count + 1; + to += count + 1; + } else { + explainJoinOn.Skipped("Skipped as there are no injected conditions"); } }); } @@ -1020,7 +987,7 @@ class JoinOnExplainEnabled { explainEntry_.succeed = true; explainEntry_.reason = ""; explainEntry_.newCond = newEntry.DumpBrief(); - explainEntry_.valuesCount = newEntry.values.size(); + explainEntry_.valuesCount = newEntry.Values().size(); } void Skipped(std::string_view reason) noexcept { @@ -1090,4 +1057,34 @@ class JoinOnExplainEnabled { time_point_t startTime_; }; +void QueryPreprocessor::setQueryIndex(QueryField &qField, int idxNo, const NamespaceImpl &ns) { + const auto &idx = *ns.indexes_[idxNo]; + std::vector compositeFieldsTypes; + if (idxNo >= ns.indexes_.firstCompositePos()) { +#ifndef NDEBUG + const bool ftIdx = IsFullText(idx.Type()); +#endif + for (const auto f : ns.indexes_[idxNo]->Fields()) { + if (f == IndexValueType::SetByJsonPath) { + // not indexed fields allowed only in ft composite indexes + assertrx_throw(ftIdx); + compositeFieldsTypes.push_back(KeyValueType::String{}); + } else { + assertrx_throw(f <= ns.indexes_.firstCompositePos()); + compositeFieldsTypes.push_back(ns.indexes_[f]->SelectKeyType()); + } + } + } + qField.SetIndexData(idxNo, FieldsSet(idx.Fields()), idx.KeyType(), idx.SelectKeyType(), std::move(compositeFieldsTypes)); +} + +void QueryPreprocessor::SetQueryField(QueryField &qField, const NamespaceImpl &ns) { + int idxNo = IndexValueType::SetByJsonPath; + if (ns.getIndexByNameOrJsonPath(qField.FieldName(), idxNo)) { + setQueryIndex(qField, idxNo, ns); + } else { + qField.SetField({ns.tagsMatcher_.path2tag(qField.FieldName())}); + } +} + } // namespace reindexer diff --git a/cpp_src/core/nsselecter/querypreprocessor.h b/cpp_src/core/nsselecter/querypreprocessor.h index 907636251..7248bdd80 100644 --- a/cpp_src/core/nsselecter/querypreprocessor.h +++ b/cpp_src/core/nsselecter/querypreprocessor.h @@ -1,7 +1,6 @@ #pragma once #include "aggregator.h" -#include "core/ft/ftsetcashe.h" #include "core/index/ft_preselect.h" #include "core/query/queryentry.h" #include "estl/h_vector.h" @@ -47,7 +46,7 @@ class QueryPreprocessor : private QueryEntries { unsigned Count() const noexcept { return count_; } bool MoreThanOneEvaluation() const noexcept { return queryEntryAddedByForcedSortOptimization_; } bool AvailableSelectBySortIndex() const noexcept { return !queryEntryAddedByForcedSortOptimization_ || !forcedStage(); } - void InjectConditionsFromJoins(JoinedSelectors &js, OnConditionInjections &expalainOnInjections, const RdxContext &rdxCtx); + void InjectConditionsFromJoins(JoinedSelectors &js, OnConditionInjections &expalainOnInjections, LogLevel, const RdxContext &rdxCtx); void Reduce(bool isFt); void InitIndexNumbers(); using QueryEntries::Size; @@ -65,6 +64,7 @@ class QueryPreprocessor : private QueryEntries { return std::move(*ftPreselect_); } bool IsFtPreselected() const noexcept { return ftPreselect_ && !ftEntry_; } + static void SetQueryField(QueryField &, const NamespaceImpl &); private: struct FoundIndexInfo { @@ -78,6 +78,7 @@ class QueryPreprocessor : private QueryEntries { uint64_t isFitForSortOptimization : 1; }; + static void setQueryIndex(QueryField &, int idxNo, const NamespaceImpl &); [[nodiscard]] SortingEntries detectOptimalSortOrder() const; bool forcedStage() const noexcept { return evaluationsCount_ == (desc_ ? 1 : 0); } size_t lookupQueryIndexes(uint16_t dst, uint16_t srcBegin, uint16_t srcEnd); @@ -85,7 +86,7 @@ class QueryPreprocessor : private QueryEntries { bool mergeQueryEntries(size_t lhs, size_t rhs); const std::vector *getCompositeIndex(int field) const; void convertWhereValues(QueryEntries::iterator begin, QueryEntries::iterator end) const; - void convertWhereValues(QueryEntry *) const; + void convertWhereValues(QueryEntry &) const; [[nodiscard]] const Index *findMaxIndex(QueryEntries::const_iterator begin, QueryEntries::const_iterator end) const; void findMaxIndex(QueryEntries::const_iterator begin, QueryEntries::const_iterator end, h_vector &foundIndexes) const; @@ -94,12 +95,13 @@ class QueryPreprocessor : private QueryEntries { */ template size_t injectConditionsFromJoins(size_t from, size_t to, JoinedSelectors &, OnConditionInjections &, const RdxContext &); - void fillQueryEntryFromOnCondition(QueryEntry &, std::string &outExplainStr, AggType &, NamespaceImpl &rightNs, Query joinQuery, - std::string joinIndex, CondType condition, KeyValueType, const RdxContext &); - template - void fillQueryEntryFromOnCondition(QueryEntry &, std::string_view joinIndex, CondType condition, const JoinedSelector &, KeyValueType, - int rightIdxNo, const CollateOpts &); - void checkStrictMode(const std::string &index, int idxNo) const; + [[nodiscard]] std::pair queryValuesFromOnCondition(std::string &outExplainStr, AggType &, + NamespaceImpl &rightNs, Query joinQuery, + const QueryJoinEntry &, CondType condition, + const RdxContext &); + [[nodiscard]] std::pair queryValuesFromOnCondition(CondType condition, const QueryJoinEntry &, + const JoinedSelector &, const CollateOpts &); + void checkStrictMode(const QueryField &) const; bool removeBrackets(); size_t removeBrackets(size_t begin, size_t end); bool canRemoveBracket(size_t i) const; diff --git a/cpp_src/core/nsselecter/selectiteratorcontainer.cc b/cpp_src/core/nsselecter/selectiteratorcontainer.cc index f30cdfabe..8466c28d3 100644 --- a/cpp_src/core/nsselecter/selectiteratorcontainer.cc +++ b/cpp_src/core/nsselecter/selectiteratorcontainer.cc @@ -70,9 +70,10 @@ bool SelectIteratorContainer::markBracketsHavingJoins(iterator begin, iterator e bool result = false; for (iterator it = begin; it != end; ++it) { result = it->InvokeAppropriate( - [it](SelectIteratorsBracket &b) { return (b.haveJoins = markBracketsHavingJoins(it.begin(), it.end())); }, - [](SelectIterator &) { return false; }, [](JoinSelectIterator &) { return true; }, - [](FieldsComparator &) { return false; }, [](AlwaysFalse &) { return false; }) || + [it](SelectIteratorsBracket &b) noexcept { return (b.haveJoins = markBracketsHavingJoins(it.begin(), it.end())); }, + [](SelectIterator &) noexcept { return false; }, [](JoinSelectIterator &) noexcept { return true; }, + [](FieldsComparator &) noexcept { return false; }, [](AlwaysFalse &) noexcept { return false; }, + [](AlwaysTrue &) noexcept { return false; }) || result; } return result; @@ -80,9 +81,9 @@ bool SelectIteratorContainer::markBracketsHavingJoins(iterator begin, iterator e bool SelectIteratorContainer::haveJoins(size_t i) const noexcept { return container_[i].InvokeAppropriate( - [](const SelectIteratorsBracket &b) { return b.haveJoins; }, [](const SelectIterator &) { return false; }, - [](const FieldsComparator &) { return false; }, [](const JoinSelectIterator &) { return true; }, - [](const AlwaysFalse &) { return false; }); + [](const SelectIteratorsBracket &b) noexcept { return b.haveJoins; }, [](const SelectIterator &) noexcept { return false; }, + [](const FieldsComparator &) noexcept { return false; }, [](const JoinSelectIterator &) noexcept { return true; }, + [](const AlwaysFalse &) noexcept { return false; }, [](const AlwaysTrue &) noexcept { return true; }); } void SelectIteratorContainer::moveJoinsToTheBeginingOfORs(span indexes, unsigned from, unsigned to) { @@ -103,11 +104,15 @@ void SelectIteratorContainer::moveJoinsToTheBeginingOfORs(span indexes SetOperation(OpOr, indexes[firstNotJoin]); if (IsJoinIterator(indexes[cur])) { unsigned tmp = indexes[cur]; - for (unsigned i = cur; i > firstNotJoin; --i) indexes[i] = indexes[i - 1]; + for (unsigned i = cur; i > firstNotJoin; --i) { + indexes[i] = indexes[i - 1]; + } indexes[firstNotJoin] = tmp; } else { memcpy(&buffer[0], &indexes[cur], sizeof(unsigned) * curSize); - for (unsigned i = cur - 1; i >= firstNotJoin; --i) indexes[i + curSize] = indexes[i]; + for (unsigned i = cur + curSize - 1, e = firstNotJoin + curSize; i >= e; --i) { + indexes[i] = indexes[i - curSize]; + } memcpy(&indexes[firstNotJoin], &buffer[0], sizeof(unsigned) * curSize); } } @@ -119,9 +124,10 @@ void SelectIteratorContainer::moveJoinsToTheBeginingOfORs(span indexes double SelectIteratorContainer::cost(span indexes, unsigned cur, int expectedIterations) const { return container_[indexes[cur]].InvokeAppropriate( [&](const SelectIteratorsBracket &) { return cost(indexes, cur + 1, cur + Size(indexes[cur]), expectedIterations); }, - [expectedIterations](const SelectIterator &sit) { return sit.Cost(expectedIterations); }, - [](const JoinSelectIterator &jit) { return jit.Cost(); }, - [expectedIterations](const FieldsComparator &c) { return c.Cost(expectedIterations); }, [](const AlwaysFalse &) { return 1; }); + [expectedIterations](const SelectIterator &sit) noexcept { return sit.Cost(expectedIterations); }, + [](const JoinSelectIterator &jit) noexcept { return jit.Cost(); }, + [expectedIterations](const FieldsComparator &c) noexcept { return c.Cost(expectedIterations); }, + [](const AlwaysFalse &) noexcept { return 1.0; }, [expectedIterations](const AlwaysTrue &) noexcept { return expectedIterations; }); } double SelectIteratorContainer::cost(span indexes, unsigned from, unsigned to, int expectedIterations) const { @@ -144,7 +150,7 @@ double SelectIteratorContainer::fullCost(span indexes, unsigned cur, u } bool SelectIteratorContainer::isIdset(const_iterator it, const_iterator end) { - return it->operation == OpAnd && it->HoldsOrReferTo() && + return it->operation == OpAnd && it->Is() && it->Value().comparators_.empty() && // !it->Value().empty() && (++it == end || it->operation != OpOr); } @@ -176,10 +182,9 @@ void SelectIteratorContainer::CheckFirstQuery() { // Let iterators choose most effective algorithm void SelectIteratorContainer::SetExpectMaxIterations(int expectedIterations) { assertrx(!Empty()); - assertrx(HoldsOrReferTo(0)); + assertrx(Is(0)); for (Container::iterator it = container_.begin() + 1; it != container_.end(); ++it) { - if (it->HoldsOrReferTo()) { - if (it->IsRef()) it->SetValue(it->Value()); + if (it->Is()) { it->Value().SetExpectMaxIterations(expectedIterations); } } @@ -188,46 +193,15 @@ void SelectIteratorContainer::SetExpectMaxIterations(int expectedIterations) { SelectKeyResults SelectIteratorContainer::processQueryEntry(const QueryEntry &qe, const NamespaceImpl &ns, StrictMode strictMode) { SelectKeyResults selectResults; - FieldsSet fields; - TagsPath tagsPath = ns.tagsMatcher_.path2tag(qe.index); - - // TODO: it may be necessary to remove or change this switch after QueryEntry refactoring - switch (qe.condition) { - case CondAny: - case CondEmpty: - case CondAllSet: - case CondEq: - case CondSet: - break; - case CondRange: - case CondDWithin: - if (qe.values.size() != 2) { - throw Error(errParams, "For condition %s required exactly 2 arguments, but provided %d", CondTypeToStr(qe.condition), - qe.values.size()); - } - break; - case CondLt: - case CondLe: - case CondGt: - case CondGe: - case CondLike: - if (qe.values.size() != 1) { - throw Error(errParams, "For condition %s required exactly 1 argument, but provided %d", CondTypeToStr(qe.condition), - qe.values.size()); - } - break; - } - - if (!tagsPath.empty()) { + if (!qe.HaveEmptyField()) { SelectKeyResult comparisonResult; - fields.push_back(tagsPath); - comparisonResult.comparators_.emplace_back(qe.condition, KeyValueType::Null{}, qe.values, false, qe.distinct, ns.payloadType_, - fields, nullptr, CollateOpts()); + comparisonResult.comparators_.emplace_back(qe.Condition(), KeyValueType::Null{}, qe.Values(), false, qe.Distinct(), ns.payloadType_, + qe.Fields(), nullptr, CollateOpts()); selectResults.emplace_back(std::move(comparisonResult)); } else if (strictMode == StrictModeNone) { SelectKeyResult res; // Ignore non-index/non-existing fields - if (qe.condition == CondEmpty) { + if (qe.Condition() == CondEmpty) { res.emplace_back(SingleSelectKeyResult(IdType(0), IdType(ns.items_.size()))); } else { res.emplace_back(SingleSelectKeyResult(IdType(0), IdType(0))); @@ -237,33 +211,30 @@ SelectKeyResults SelectIteratorContainer::processQueryEntry(const QueryEntry &qe throw Error( errStrictMode, "Current query strict mode allows filtering by existing fields only. There are no fields with name '%s' in namespace '%s'", - qe.index, ns.name_); + qe.FieldName(), ns.name_); } return selectResults; } template -void SelectIteratorContainer::processField(FieldsComparator &fc, std::string_view field, int idxNo, const NamespaceImpl &ns) const { - const bool nonIndexField = (idxNo == IndexValueType::SetByJsonPath); - if (nonIndexField) { - TagsPath tagsPath = ns.tagsMatcher_.path2tag(field); - if (tagsPath.empty()) { - throw Error{errQueryExec, "Only existing fields can be compared. There are no fields with name '%s' in namespace '%s'", field, - ns.name_}; - } +void SelectIteratorContainer::processField(FieldsComparator &fc, const QueryField &field, const NamespaceImpl &ns) const { + if (field.IsFieldIndexed()) { + auto &index = ns.indexes_[field.IndexNo()]; if constexpr (left) { - fc.SetLeftField(tagsPath); + fc.SetCollateOpts(index->Opts().collateOpts_); + fc.SetLeftField(field.Fields(), field.FieldType(), index->Opts().IsArray()); } else { - fc.SetRightField(tagsPath); + fc.SetRightField(field.Fields(), field.FieldType(), index->Opts().IsArray()); } + } else if (field.HaveEmptyField()) { + throw Error{errQueryExec, "Only existing fields can be compared. There are no fields with name '%s' in namespace '%s'", + field.FieldName(), ns.name_}; } else { - auto &index = ns.indexes_[idxNo]; if constexpr (left) { - fc.SetCollateOpts(index->Opts().collateOpts_); - fc.SetLeftField(index->Fields(), index->KeyType(), index->Opts().IsArray()); + fc.SetLeftField(field.Fields()); } else { - fc.SetRightField(index->Fields(), index->KeyType(), index->Opts().IsArray()); + fc.SetRightField(field.Fields()); } } } @@ -272,7 +243,7 @@ SelectKeyResults SelectIteratorContainer::processQueryEntry(const QueryEntry &qe unsigned sortId, bool isQueryFt, SelectFunction::Ptr &selectFnc, bool &isIndexFt, bool &isIndexSparse, FtCtx::Ptr &ftCtx, QueryPreprocessor &qPreproc, const RdxContext &rdxCtx) { - auto &index = ns.indexes_[qe.idxNo]; + auto &index = ns.indexes_[qe.IndexNo()]; isIndexFt = IsFullText(index->Type()); isIndexSparse = index->Opts().IsSparse(); @@ -289,31 +260,31 @@ SelectKeyResults SelectIteratorContainer::processQueryEntry(const QueryEntry &qe opts.forceComparator = 1; } } - if (qe.distinct) { + if (qe.Distinct()) { opts.distinct = 1; } opts.maxIterations = GetMaxIterations(); opts.indexesNotOptimized = !ctx_->sortingContext.enableSortOrders; opts.inTransaction = ctx_->inTransaction; - auto ctx = selectFnc ? selectFnc->CreateCtx(qe.idxNo) : BaseFunctionCtx::Ptr{}; + auto ctx = selectFnc ? selectFnc->CreateCtx(qe.IndexNo()) : BaseFunctionCtx::Ptr{}; if (ctx && ctx->type == BaseFunctionCtx::kFtCtx) ftCtx = reindexer::reinterpret_pointer_cast(ctx); if (index->Opts().GetCollateMode() == CollateUTF8 || isIndexFt) { - for (auto &key : qe.values) key.EnsureUTF8(); + for (auto &key : qe.Values()) key.EnsureUTF8(); } PerfStatCalculatorMT calc(index->GetSelectPerfCounter(), ns.enablePerfCounters_); if (qPreproc.IsFtPreselected()) { - return index->SelectKey(qe.values, qe.condition, opts, ctx, qPreproc.MoveFtPreselect(), rdxCtx); + return index->SelectKey(qe.Values(), qe.Condition(), opts, ctx, qPreproc.MoveFtPreselect(), rdxCtx); } else { - return index->SelectKey(qe.values, qe.condition, sortId, opts, ctx, rdxCtx); + return index->SelectKey(qe.Values(), qe.Condition(), sortId, opts, ctx, rdxCtx); } } void SelectIteratorContainer::processJoinEntry(const JoinQueryEntry &jqe, OpType op) { auto &js = (*ctx_->joinedSelectors)[jqe.joinIndex]; if (js.JoinQuery().joinEntries_.empty()) throw Error(errQueryExec, "Join without ON conditions"); - if (js.JoinQuery().joinEntries_[0].op_ == OpOr) throw Error(errQueryExec, "The first ON condition cannot have OR operation"); + if (js.JoinQuery().joinEntries_[0].Operation() == OpOr) throw Error(errQueryExec, "The first ON condition cannot have OR operation"); if (js.Type() != InnerJoin && js.Type() != OrInnerJoin) throw Error(errLogic, "Not INNER JOIN in QueryEntry"); if (js.Type() == OrInnerJoin) { if (op == OpNot) throw Error(errQueryExec, "NOT operator with or_inner_join"); @@ -327,7 +298,7 @@ void SelectIteratorContainer::processJoinEntry(const JoinQueryEntry &jqe, OpType } void SelectIteratorContainer::processQueryEntryResults(SelectKeyResults &selectResults, OpType op, const NamespaceImpl &ns, - const QueryEntry &qe, bool isIndexFt, bool isIndexSparse, bool nonIndexField, + const QueryEntry &qe, bool isIndexFt, bool isIndexSparse, std::optional nextOp) { if (selectResults.empty()) { if (op == OpAnd) { @@ -340,19 +311,16 @@ void SelectIteratorContainer::processQueryEntryResults(SelectKeyResults &selectR case OpOr: { const iterator last = lastAppendedOrClosed(); if (last == this->end()) { - throw Error(errQueryExec, "OR operator in first condition or after left join "); + throw Error(errQueryExec, "OR operator in first condition or after left join"); } - if (last->HoldsOrReferTo() && !last->Value().distinct && last->operation != OpNot) { - if (last->IsRef()) { - last->SetValue(last->Value()); - } + if (last->Is() && !last->Value().distinct && last->operation != OpNot) { SelectIterator &it = last->Value(); - if (nonIndexField || isIndexSparse) { + if (!qe.IsFieldIndexed() || isIndexSparse) { it.Append(res); } else { - it.AppendAndBind(res, ns.payloadType_, qe.idxNo); + it.AppendAndBind(res, ns.payloadType_, qe.IndexNo()); } - it.name += " or " + qe.index; + it.name += " or " + qe.FieldName(); break; } } @@ -360,16 +328,13 @@ void SelectIteratorContainer::processQueryEntryResults(SelectKeyResults &selectR case OpNot: case OpAnd: // Iterator Field Kind: Query entry results. Field known. - Append(op, SelectIterator(res, qe.distinct, qe.index, - qe.idxNo < 0 ? IteratorFieldKind::NonIndexed : IteratorFieldKind::Indexed, isIndexFt)); - if (!nonIndexField && !isIndexSparse) { + Append(op, res, qe.Distinct(), qe.FieldName(), + qe.IndexNo() < 0 ? IteratorFieldKind::NonIndexed : IteratorFieldKind::Indexed, isIndexFt); + if (qe.IsFieldIndexed() && !isIndexSparse) { // last appended is always a SelectIterator const auto lastAppendedIt = lastAppendedOrClosed(); - if (lastAppendedIt->IsRef()) { - lastAppendedIt->SetValue(lastAppendedIt->Value()); - } SelectIterator &lastAppended = lastAppendedIt->Value(); - lastAppended.Bind(ns.payloadType_, qe.idxNo); + lastAppended.Bind(ns.payloadType_, qe.IndexNo()); lastAppended.SetNotOperationFlag(op == OpNot); const auto maxIterations = lastAppended.GetMaxIterations(); const int cur = op == OpNot ? ns.items_.size() - maxIterations : maxIterations; @@ -393,25 +358,23 @@ void SelectIteratorContainer::processEqualPositions(const std::vector(eqPos.queryEntriesPositions[0])}; - if (firstQe.condition == CondEmpty || (firstQe.condition == CondSet && firstQe.values.empty())) { + if (firstQe.Condition() == CondEmpty || (firstQe.Condition() == CondSet && firstQe.Values().empty())) { throw Error(errLogic, "Condition IN(with empty parameter list), IS NULL, IS EMPTY not allowed for equal position!"); } - const KeyValueType type = firstQe.values.size() ? firstQe.values[0].Type() : KeyValueType::Null{}; - Comparator cmp(firstQe.condition, type, firstQe.values, true, firstQe.distinct, ns.payloadType_, FieldsSet({firstQe.idxNo})); + const KeyValueType type = firstQe.Values().size() ? firstQe.Values()[0].Type() : KeyValueType::Null{}; + Comparator cmp{firstQe.Condition(), type, firstQe.Values(), true, firstQe.Distinct(), ns.payloadType_, firstQe.Fields()}; for (size_t i = 0; i < eqPos.queryEntriesPositions.size(); ++i) { const QueryEntry &qe = queries.Get(eqPos.queryEntriesPositions[i]); - if (qe.condition == CondEmpty || (qe.condition == CondSet && qe.values.empty())) { + if (qe.Condition() == CondEmpty || (qe.Condition() == CondSet && qe.Values().empty())) { throw Error(errLogic, "Condition IN(with empty parameter list), IS NULL, IS EMPTY not allowed for equal position!"); } - if (qe.idxNo == IndexValueType::SetByJsonPath) { - cmp.BindEqualPosition(ns.tagsMatcher_.path2tag(qe.index), qe.values, qe.condition); - } else if (ns.indexes_[qe.idxNo]->Opts().IsSparse()) { - const TagsPath &tp = ns.indexes_[qe.idxNo]->Fields().getTagsPath(0); - cmp.BindEqualPosition(tp, qe.values, qe.condition); + assertrx_throw(qe.Fields().size() == 1); + if (qe.Fields()[0] == IndexValueType::SetByJsonPath) { + cmp.BindEqualPosition(qe.Fields().getFieldsPath(0), qe.Values(), qe.Condition()); } else { - cmp.BindEqualPosition(qe.idxNo, qe.values, qe.condition); + cmp.BindEqualPosition(qe.Fields()[0], qe.Values(), qe.Condition()); } } @@ -448,35 +411,36 @@ std::vector SelectIteratorContainer::pr for (size_t j = begin, next; j < end; j = next) { next = queries.Next(j); queries.InvokeAppropriate( - j, Skip{}, + j, Skip{}, [](const SubQueryEntry &) { assertrx_throw(0); }, + [](const SubQueryFieldEntry &) { assertrx_throw(0); }, [&](const QueryEntry &eq) { - if (foundFields.find(eq.index) != foundFields.end()) { + if (foundFields.find(eq.FieldName()) != foundFields.end()) { throw Error(errParams, "Equal position field '%s' found twice in enclosing bracket; equal position fields: [%s]", - eq.index, getEpFieldsStr()); + eq.FieldName(), getEpFieldsStr()); } - const auto it = epFields.find(eq.index); + const auto it = epFields.find(eq.FieldName()); if (it == epFields.end()) return; if (queries.GetOperation(j) != OpAnd || (next < end && queries.GetOperation(next) == OpOr)) { throw Error(errParams, "Only AND operation allowed for equal position; equal position field with not AND operation: '%s'; " "equal position fields: [%s]", - eq.index, getEpFieldsStr()); + eq.FieldName(), getEpFieldsStr()); } result[i].queryEntriesPositions.push_back(j); foundFields.insert(epFields.extract(it)); }, [&](const BetweenFieldsQueryEntry &eq) { // TODO equal positions for BetweenFieldsQueryEntry #1092 - if (epFields.find(eq.firstIndex) != epFields.end()) { + if (epFields.find(eq.LeftFieldName()) != epFields.end()) { throw Error( errParams, "Equal positions for conditions between fields are not supported; field: '%s'; equal position fields: [%s]", - eq.firstIndex, getEpFieldsStr()); + eq.LeftFieldName(), getEpFieldsStr()); } - if (epFields.find(eq.secondIndex) != epFields.end()) { + if (epFields.find(eq.RightFieldName()) != epFields.end()) { throw Error( errParams, "Equal positions for conditions between fields are not supported; field: '%s'; equal position fields: [%s]", - eq.secondIndex, getEpFieldsStr()); + eq.RightFieldName(), getEpFieldsStr()); } }); } @@ -506,6 +470,14 @@ bool SelectIteratorContainer::prepareIteratorsForSelectLoop(QueryPreprocessor &q const OpType op = queries.GetOperation(i); containFT = queries.InvokeAppropriate( i, + [](const SubQueryEntry &) -> bool { + assertrx_throw(0); + abort(); + }, + [](const SubQueryFieldEntry &) -> bool { + assertrx_throw(0); + abort(); + }, [&](const QueryEntriesBracket &) { OpenBracket(op); const bool contFT = @@ -517,28 +489,16 @@ bool SelectIteratorContainer::prepareIteratorsForSelectLoop(QueryPreprocessor &q return contFT; }, [&](const QueryEntry &qe) { - const bool isFT = qe.idxNo != IndexValueType::SetByJsonPath && IsFullText(ns.indexes_[qe.idxNo]->Type()); + const bool isFT = qe.IsFieldIndexed() && IsFullText(ns.indexes_[qe.IndexNo()]->Type()); if (isFT && (op == OpOr || (next < end && queries.GetOperation(next) == OpOr))) { throw Error(errLogic, "OR operation is not allowed with fulltext index"); } SelectKeyResults selectResults; bool isIndexFt = false, isIndexSparse = false; - const bool nonIndexField = (qe.idxNo == IndexValueType::SetByJsonPath); - - if (nonIndexField) { - auto strictMode = ns.config_.strictMode; - if (ctx_) { - if (ctx_->inTransaction) { - strictMode = StrictModeNone; - } else if (ctx_->query.strictMode != StrictModeNotSet) { - strictMode = ctx_->query.strictMode; - } - } - selectResults = processQueryEntry(qe, ns, strictMode); - } else { - bool enableSortIndexOptimize = (ctx_->sortingContext.uncommitedIndex == qe.idxNo) && !sortIndexFound && - (op == OpAnd) && !qe.distinct && (begin == 0) && + if (qe.IsFieldIndexed()) { + bool enableSortIndexOptimize = (ctx_->sortingContext.uncommitedIndex == qe.IndexNo()) && !sortIndexFound && + (op == OpAnd) && !qe.Distinct() && (begin == 0) && (next == end || queries.GetOperation(next) != OpOr); if (enableSortIndexOptimize) { if (!IsExpectingOrderedResults(qe)) { @@ -549,12 +509,22 @@ bool SelectIteratorContainer::prepareIteratorsForSelectLoop(QueryPreprocessor &q } selectResults = processQueryEntry(qe, enableSortIndexOptimize, ns, sortId, isQueryFt, selectFnc, isIndexFt, isIndexSparse, ftCtx, qPreproc, rdxCtx); + } else { + auto strictMode = ns.config_.strictMode; + if (ctx_) { + if (ctx_->inTransaction) { + strictMode = StrictModeNone; + } else if (ctx_->query.GetStrictMode() != StrictModeNotSet) { + strictMode = ctx_->query.GetStrictMode(); + } + } + selectResults = processQueryEntry(qe, ns, strictMode); } std::optional nextOp; if (next != end) { nextOp = queries.GetOperation(next); } - processQueryEntryResults(selectResults, op, ns, qe, isIndexFt, isIndexSparse, nonIndexField, nextOp); + processQueryEntryResults(selectResults, op, ns, qe, isIndexFt, isIndexSparse, nextOp); if (op != OpOr) { for (auto &ep : equalPositions) { const auto lastPosition = ep.queryEntriesPositions.back(); @@ -574,15 +544,19 @@ bool SelectIteratorContainer::prepareIteratorsForSelectLoop(QueryPreprocessor &q return false; }, [&](const BetweenFieldsQueryEntry &qe) { - FieldsComparator fc{qe.firstIndex, qe.Condition(), qe.secondIndex, ns.payloadType_}; - processField(fc, qe.firstIndex, qe.firstIdxNo, ns); - processField(fc, qe.secondIndex, qe.secondIdxNo, ns); + FieldsComparator fc{qe.LeftFieldName(), qe.Condition(), qe.RightFieldName(), ns.payloadType_}; + processField(fc, qe.LeftFieldData(), ns); + processField(fc, qe.RightFieldData(), ns); Append(op, std::move(fc)); return false; }, [this, op](const AlwaysFalse &) { Append(op, AlwaysFalse{}); return false; + }, + [this, op](const AlwaysTrue &) { + Append(op, AlwaysTrue{}); + return false; }) || containFT; } @@ -624,10 +598,11 @@ bool SelectIteratorContainer::checkIfSatisfyAllConditions(iterator begin, iterat // suggest that all JOINs in chain of OR ... OR ... OR ... OR will be before all not JOINs (see SortByCost) if (result) { // check what it does not holds join - if (it->InvokeAppropriate([](const SelectIteratorsBracket &b) { return !b.haveJoins; }, - [](const SelectIterator &) { return true; }, - [](const JoinSelectIterator &) { return false; }, - [](const FieldsComparator &) { return true; }, [](const AlwaysFalse &) { return true; })) + if (it->InvokeAppropriate( + [](const SelectIteratorsBracket &b) noexcept { return !b.haveJoins; }, + [](const SelectIterator &) noexcept { return true; }, [](const JoinSelectIterator &) noexcept { return false; }, + [](const FieldsComparator &) noexcept { return true; }, [](const AlwaysFalse &) noexcept { return true; }, + [](const AlwaysTrue &) noexcept { return true; })) continue; } } else { @@ -641,7 +616,8 @@ bool SelectIteratorContainer::checkIfSatisfyAllConditions(iterator begin, iterat }, [&](SelectIterator &sit) { return checkIfSatisfyCondition(sit, pv, &lastFinish, rowId, properRowId); }, [&](JoinSelectIterator &jit) { return checkIfSatisfyCondition(jit, pv, properRowId, match); }, - [&pv](FieldsComparator &c) { return c.Compare(pv); }, [](AlwaysFalse &) { return false; }); + [&pv](FieldsComparator &c) { return c.Compare(pv); }, [](AlwaysFalse &) noexcept { return false; }, + [](AlwaysTrue &) noexcept { return true; }); if (it->operation == OpOr) { result |= lastResult; currentFinish &= (!result && lastFinish); @@ -672,8 +648,9 @@ IdType SelectIteratorContainer::next(const_iterator it, IdType from) { } return from; }, - [from](const JoinSelectIterator &) { return from; }, [from](const FieldsComparator &) { return from; }, - [](const AlwaysFalse &) { + [from](const JoinSelectIterator &) noexcept { return from; }, [from](const FieldsComparator &) noexcept { return from; }, + [from](const AlwaysTrue &) noexcept { return from; }, + [](const AlwaysFalse &) noexcept { if constexpr (reverse) { return std::numeric_limits::lowest(); } else { @@ -747,7 +724,8 @@ void SelectIteratorContainer::dump(size_t level, const_iterator begin, const_ite }, [&ser](const SelectIterator &sit) { ser << sit.Dump(); }, [&ser, &joinedSelectors](const JoinSelectIterator &jit) { jit.Dump(ser, joinedSelectors); }, - [&ser](const FieldsComparator &c) { ser << c.Dump(); }, [&ser](const AlwaysFalse &) { ser << "Always False"; }); + [&ser](const FieldsComparator &c) { ser << c.Dump(); }, [&ser](const AlwaysFalse &) { ser << "Always False"; }, + [&ser](const AlwaysTrue &) { ser << "Always True"; }); ser << '\n'; } } @@ -759,11 +737,11 @@ void JoinSelectIterator::Dump(WrSerializer &ser, const std::vector { - using Base = ExpressionTree; +class SelectIteratorContainer : public ExpressionTree { + using Base = + ExpressionTree; public: SelectIteratorContainer(PayloadType pt = PayloadType(), SelectCtx *ctx = nullptr) @@ -49,11 +50,11 @@ class SelectIteratorContainer bool IsSelectIterator(size_t i) const noexcept { assertrx(i < Size()); - return container_[i].HoldsOrReferTo(); + return container_[i].Is(); } bool IsJoinIterator(size_t i) const noexcept { assertrx(i < container_.size()); - return container_[i].HoldsOrReferTo(); + return container_[i].Is(); } void ExplainJSON(int iters, JsonBuilder &builder, const std::vector *js) const { explainJSON(cbegin(), cend(), iters, builder, js); @@ -67,7 +68,7 @@ class SelectIteratorContainer int GetMaxIterations(bool withZero = false) { return (withZero && wasZeroIterations_) ? 0 : maxIterations_; } std::string Dump() const; static bool IsExpectingOrderedResults(const QueryEntry &qe) noexcept { - return IsOrderedCondition(qe.condition) || (qe.condition != CondAny && qe.values.size() <= 1); + return IsOrderedCondition(qe.Condition()) || (qe.Condition() != CondAny && qe.Values().size() <= 1); } private: @@ -101,10 +102,10 @@ class SelectIteratorContainer bool isQueryFt, SelectFunction::Ptr &selectFnc, bool &isIndexFt, bool &isIndexSparse, FtCtx::Ptr &, QueryPreprocessor &qPreproc, const RdxContext &); template - void processField(FieldsComparator &, std::string_view field, int idxNo, const NamespaceImpl &ns) const; + void processField(FieldsComparator &, const QueryField &, const NamespaceImpl &) const; void processJoinEntry(const JoinQueryEntry &, OpType); void processQueryEntryResults(SelectKeyResults &selectResults, OpType, const NamespaceImpl &ns, const QueryEntry &qe, bool isIndexFt, - bool isIndexSparse, bool nonIndexField, std::optional nextOp); + bool isIndexSparse, std::optional nextOp); struct EqualPositions { h_vector queryEntriesPositions; size_t positionToInsertIterator = 0; diff --git a/cpp_src/core/parallelexecutor.cc b/cpp_src/core/parallelexecutor.cc index 37d44db93..33debc72a 100644 --- a/cpp_src/core/parallelexecutor.cc +++ b/cpp_src/core/parallelexecutor.cc @@ -5,7 +5,7 @@ namespace reindexer { Error ParallelExecutor::createIntegralError(h_vector, 8> &errors, size_t clientCount) { if (errors.empty()) { - return errOK; + return {}; } std::string descr; bool eqErr = true; @@ -16,12 +16,12 @@ Error ParallelExecutor::createIntegralError(h_vector, 8> & eqErr = false; } if (e.first.code() == errStrictMode) { - errStrictModeCounter++; + ++errStrictModeCounter; } descr += fmt::format("[ shard:{} err:{} ]", e.second, e.first.what()); } if (errStrictModeCounter == errors.size() && errors.size() < clientCount) { - return errOK; + return {}; } if (eqErr) { return errors[0].first; @@ -46,26 +46,23 @@ Error ParallelExecutor::ExecSelect(const Query &query, QueryResults &result, con auto ward = ctx.BeforeShardingProxy(); size_t clientCount = countClientConnection(connections); - for (auto itr = connections.rbegin(); itr != connections.rend(); ++itr) { - auto &connection = *itr; - int shardId = connection.ShardId(); - if (connection) { - clientResults.emplace_back(connection.ShardId()); - clientResults.back().results = client::QueryResults{result.Flags()}; - clientResults.back().connection = + if (auto &connection = *itr; connection) { + const int shardId = connection.ShardId(); + auto &clientData = clientResults.emplace_back(connection.ShardId()); + clientData.results = client::QueryResults{result.Flags()}; + clientData.connection = connection->WithShardingParallelExecution(connections.size() > 1) .WithCompletion([clientCount, &clientCompl, &clientErrors, shardId, &mtx, &cv, this](const Error &err) { completionFunction(clientCount, clientCompl, clientErrors, shardId, mtx, cv, err); }) .WithContext(ctx.GetCancelCtx()); - Error err = clientResults.back().connection.Select(query, clientResults.back().results); + Error err = clientData.connection.Select(query, clientData.results); if (!err.ok()) { std::lock_guard lck(mtx); clientErrors.emplace_back(std::move(err), shardId); } - } else { const auto shCtx = ctx.WithShardId(localShardId_, true); LocalQueryResults lqr; @@ -75,7 +72,7 @@ Error ParallelExecutor::ExecSelect(const Query &query, QueryResults &result, con result.AddQr(std::move(lqr), localShardId_, false); } else { std::lock_guard lck(mtx); - clientErrors.emplace_back(std::move(status), shardId); + clientErrors.emplace_back(std::move(status), localShardId_); } } } @@ -86,8 +83,24 @@ Error ParallelExecutor::ExecSelect(const Query &query, QueryResults &result, con if (!status.ok()) { return status; } + const auto shardingVersion = result.GetShardingConfigVersion(); for (size_t i = 0; i < clientResults.size(); ++i) { - result.AddQr(std::move(clientResults[i].results), clientResults[i].shardId, (i + 1) == clientResults.size()); + bool hasError = false; + auto &clientData = clientResults[i]; + for (auto &ep : clientErrors) { + if (ep.second == clientData.shardId) { + hasError = true; + break; + } + } + if rx_likely (!hasError) { + if rx_unlikely (clientData.results.GetShardingConfigVersion() != shardingVersion) { + return Error(errLogic, + "Distributed parallel query: local and remote sharding versions (source IDs) are different: %d vs %d", + shardingVersion, clientData.results.GetShardingConfigVersion()); + } + result.AddQr(std::move(clientData.results), clientData.shardId, (i + 1) == clientResults.size()); + } } return status; } @@ -99,7 +112,7 @@ void ParallelExecutor::completionFunction(size_t clientCount, size_t &clientComp std::lock_guard lck(mtx); clientCompl++; if (!err.ok()) { - clientErrors.push_back(std::make_pair(err, shardId)); + clientErrors.emplace_back(err, shardId); } if (clientCompl == clientCount) { cv.notify_one(); diff --git a/cpp_src/core/parallelexecutor.h b/cpp_src/core/parallelexecutor.h index 7fda21a15..50f577ec0 100644 --- a/cpp_src/core/parallelexecutor.h +++ b/cpp_src/core/parallelexecutor.h @@ -29,35 +29,32 @@ class ParallelExecutor { std::mutex mtx; size_t clientCompl = 0; - h_vector, 8> clientErrors; - int isLocalCall = 0; h_vector results; auto ward = rdxCtx.BeforeShardingProxy(); size_t clientCount = countClientConnection(*connections.get()); - for (auto itr = connections->rbegin(); itr != connections->rend(); ++itr) { - auto connection = *itr; - int shardId = itr->ShardId(); - if (connection) { - results.emplace_back(shardId); - results.back().connection = + if (auto connection = *itr; connection) { + const int shardId = itr->ShardId(); + auto &clientData = results.emplace_back(shardId); + clientData.connection = connection->WithCompletion([clientCount, &clientCompl, &clientErrors, shardId, &mtx, &cv, this](const Error &err) { completionFunction(clientCount, clientCompl, clientErrors, shardId, mtx, cv, err); }); - auto &conn = results.back().connection; + auto &conn = clientData.connection; auto invokeWrap = [&f, &conn](auto &&...args) { return std::invoke(f, conn, std::forward(args)...); }; Error err; // check whether it is necessary to pass the ShardId to the function - if constexpr (std::is_invocable_v) + if constexpr (std::is_invocable_v) { err = invokeWrap(std::forward(args)..., shardId); - else + } else { err = invokeWrap(std::forward(args)...); + } if (!err.ok()) { std::lock_guard lck(mtx); @@ -68,7 +65,7 @@ class ParallelExecutor { isLocalCall = 1; if (!err.ok()) { std::lock_guard lck(mtx); - clientErrors.emplace_back(std::move(err), shardId); + clientErrors.emplace_back(std::move(err), localShardId_); } } } @@ -83,39 +80,33 @@ class ParallelExecutor { std::vector &result, const Predicate &predicated, std::string_view nsName, Args &&...args) { std::condition_variable cv; std::mutex mtx; - h_vector, 8> clientErrors; size_t clientCompl = 0; - std::string errString; - std::deque>> results; int isLocalCall = 0; - auto ward = rdxCtx.BeforeShardingProxy(); size_t clientCount = countClientConnection(*connections.get()); for (auto itr = connections->rbegin(); itr != connections->rend(); ++itr) { - auto connection = *itr; - int shardId = itr->ShardId(); - - if (connection) { - results.push_back(ConnectionData>{}); - results.back().connection = + if (auto connection = *itr; connection) { + const int shardId = itr->ShardId(); + auto &clientData = results.emplace_back(); + clientData.connection = connection->WithCompletion([clientCount, &clientCompl, &clientErrors, shardId, &mtx, &cv, this](const Error &err) { completionFunction(clientCount, clientCompl, clientErrors, shardId, mtx, cv, err); }); - Error err = std::invoke(clientF, results.back().connection, nsName, std::forward(args)..., results.back().results); + Error err = std::invoke(clientF, clientData.connection, nsName, std::forward(args)..., clientData.results); if (!err.ok()) { std::lock_guard lck(mtx); clientErrors.emplace_back(std::move(err), shardId); } } else { - results.push_back(ConnectionData>{}); - Error err = local(nsName, std::forward(args)..., results.back().results, localShardId_); + auto &localData = results.emplace_back(); + Error err = local(nsName, std::forward(args)..., localData.results, localShardId_); isLocalCall = 1; if (!err.ok()) { std::lock_guard lck(mtx); - clientErrors.emplace_back(std::move(err), shardId); + clientErrors.emplace_back(std::move(err), localShardId_); } } } diff --git a/cpp_src/core/payload/fieldsset.h b/cpp_src/core/payload/fieldsset.h index 3e6f49cef..be0d7c542 100644 --- a/cpp_src/core/payload/fieldsset.h +++ b/cpp_src/core/payload/fieldsset.h @@ -75,18 +75,8 @@ class FieldsSet : protected base_fields_set { } } - void push_back(const TagsPath &tagsPath) { - if (!contains(tagsPath)) { - base_fields_set::push_back(IndexValueType::SetByJsonPath); - tagsPaths_.emplace_back(tagsPath); - } - } - void push_back(TagsPath &&tagsPath) { - if (!contains(tagsPath)) { - base_fields_set::push_back(IndexValueType::SetByJsonPath); - tagsPaths_.emplace_back(std::move(tagsPath)); - } - } + void push_back(const TagsPath &tagsPath) { pushBack(tagsPath); } + void push_back(TagsPath &&tagsPath) { pushBack(std::move(tagsPath)); } void push_front(TagsPath &&tagsPath) { if (!contains(tagsPath)) { base_fields_set::insert(begin(), IndexValueType::SetByJsonPath); @@ -100,19 +90,10 @@ class FieldsSet : protected base_fields_set { tagsPaths_.emplace(tagsPaths_.begin(), tagsPath); } } - - void push_back(const IndexedTagsPath &tagsPath) { - if (!contains(tagsPath)) { - base_fields_set::push_back(IndexValueType::SetByJsonPath); - tagsPaths_.emplace_back(tagsPath); - } - } - void push_back(IndexedTagsPath &&tagsPath) { - if (!contains(tagsPath)) { - base_fields_set::push_back(IndexValueType::SetByJsonPath); - tagsPaths_.emplace_back(std::move(tagsPath)); - } - } + void push_back(const IndexedTagsPath &tagsPath) { pushBack(tagsPath); } + void push_back(IndexedTagsPath &&tagsPath) { pushBack(std::move(tagsPath)); } + void push_back(const FieldsPath &fieldPath) { pushBack(fieldPath); } + void push_back(FieldsPath &&fieldPath) { pushBack(std::move(fieldPath)); } void push_back(int f) { if (f < 0) return; @@ -153,31 +134,34 @@ class FieldsSet : protected base_fields_set { bool contains(const IndexesFieldsSet &f) const noexcept { return (mask_ & f.mask()) == f.mask(); } bool contains(const TagsPath &tagsPath) const noexcept { for (const FieldsPath &path : tagsPaths_) { - if (path.index() == 0) { - if (std::get(path) == tagsPath) return true; - } else { - if (std::get(path).Compare(tagsPath)) return true; + if (std::visit(overloaded{[&tagsPath](const TagsPath &path) { return path == tagsPath; }, + [&tagsPath](const IndexedTagsPath &path) { return path.Compare(tagsPath); }}, + path)) { + return true; } } return false; } bool contains(const IndexedTagsPath &tagsPath) const noexcept { for (const FieldsPath &path : tagsPaths_) { - if (path.index() == 1) { - if (std::get(path) == tagsPath) return true; - } else { - if (tagsPath.Compare(std::get(path))) return true; + if (std::visit(overloaded{[&tagsPath](const TagsPath &path) { return tagsPath.Compare(path); }, + [&tagsPath](const IndexedTagsPath &path) { return path == tagsPath; }}, + path)) { + return true; } } return false; } + bool contains(const FieldsPath &fieldsPath) const noexcept { + return std::visit([&](const auto &fp) { return contains(fp); }, fieldsPath); + } bool match(const TagsPath &tagsPath) const noexcept { if (tagsPaths_.empty()) return true; - for (auto &flt : tagsPaths_) { - if (flt.index() == 0) { - if (comparePaths(tagsPath, std::get(flt))) return true; - } else { - if (comparePaths(std::get(flt), tagsPath)) return true; + for (auto &path : tagsPaths_) { + if (std::visit(overloaded{[&tagsPath, this](const TagsPath &path) { return comparePaths(tagsPath, path); }, + [&tagsPath, this](const IndexedTagsPath &path) { return comparePaths(path, tagsPath); }}, + path)) { + return true; } } return false; @@ -185,11 +169,11 @@ class FieldsSet : protected base_fields_set { template bool match(const IndexedTagsPathImpl &tagsPath) const noexcept { if (tagsPaths_.empty()) return true; - for (auto &flt : tagsPaths_) { - if (flt.index() == 1) { - if (comparePaths(tagsPath, std::get(flt))) return true; - } else { - if (comparePaths(tagsPath, std::get(flt))) return true; + for (auto &path : tagsPaths_) { + if (std::visit(overloaded{[&tagsPath, this](const TagsPath &path) { return comparePaths(tagsPath, path); }, + [&tagsPath, this](const IndexedTagsPath &path) { return comparePaths(tagsPath, path); }}, + path)) { + return true; } } return false; @@ -206,12 +190,15 @@ class FieldsSet : protected base_fields_set { const h_vector &getJsonPaths() const noexcept { return jsonPaths_; } bool isTagsPathIndexed(size_t idx) const noexcept { assertrx(idx < tagsPaths_.size()); - return (tagsPaths_[idx].index() == 1); + return std::visit(overloaded{[](const TagsPath &) { return false; }, [](const IndexedTagsPath &) { return true; }}, + tagsPaths_[idx]); } const TagsPath &getTagsPath(size_t idx) const & { return std::get(tagsPaths_[idx]); } const TagsPath &getTagsPath(size_t idx) const && = delete; const IndexedTagsPath &getIndexedTagsPath(size_t idx) const & { return std::get(tagsPaths_[idx]); } const IndexedTagsPath &getIndexedTagsPath(size_t idx) const && = delete; + const FieldsPath &getFieldsPath(size_t idx) const & { return tagsPaths_[idx]; } + const FieldsPath &getFieldsPath(size_t idx) const && = delete; const std::string &getJsonPath(size_t idx) const &noexcept { return jsonPaths_[idx]; } const std::string &getJsonPath(size_t idx) const && = delete; @@ -242,7 +229,14 @@ class FieldsSet : protected base_fields_set { os << "]}"; } -protected: +private: + template + void pushBack(F &&fieldPath) { + if (!contains(fieldPath)) { + base_fields_set::push_back(IndexValueType::SetByJsonPath); + tagsPaths_.emplace_back(std::forward(fieldPath)); + } + } template bool comparePaths(const TPath1 &lhs, const TPath2 &rhs) const noexcept { unsigned i = 0, count = std::min(lhs.size(), rhs.size()); diff --git a/cpp_src/core/payload/payloadfieldvalue.cc b/cpp_src/core/payload/payloadfieldvalue.cc index e9a0d6a06..2b2defba1 100644 --- a/cpp_src/core/payload/payloadfieldvalue.cc +++ b/cpp_src/core/payload/payloadfieldvalue.cc @@ -3,7 +3,7 @@ namespace reindexer { void PayloadFieldValue::throwSetTypeMissmatch(const Variant& kv) { - throw Error(errLogic, "PayloadFieldValue::Set field '%s' type mismatch. passed '%s', expected '%s'\n", t_.Name(), kv.Type().Name(), + throw Error(errLogic, "PayloadFieldValue::Set field '%s' type mismatch. Passed '%s', expected '%s'", t_.Name(), kv.Type().Name(), t_.Type().Name()); } diff --git a/cpp_src/core/payload/payloadiface.cc b/cpp_src/core/payload/payloadiface.cc index b70d716b7..b7becd91d 100644 --- a/cpp_src/core/payload/payloadiface.cc +++ b/cpp_src/core/payload/payloadiface.cc @@ -5,7 +5,6 @@ #include "core/keyvalue/p_string.h" #include "core/keyvalue/variant.h" #include "core/namespace/stringsholder.h" -#include "itoa/itoa.h" #include "payloadiface.h" #include "payloadvalue.h" @@ -77,29 +76,77 @@ void PayloadIface::GetByJsonPath(std::string_view jsonPath, TagsMatcher &tags } template -void PayloadIface::GetByJsonPath(const TagsPath &jsonPath, VariantArray &krefs, KeyValueType expectedType) const { - ConstPayload pl(t_, *v_); - FieldsSet filter({jsonPath}); - BaseEncoder encoder(nullptr, &filter); +template +void PayloadIface::getByJsonPath(const P &path, VariantArray &krefs, KeyValueType expectedType) const { krefs.clear(); - if (!jsonPath.empty()) { - FieldsExtractor extractor(&krefs, expectedType, jsonPath.size()); - encoder.Encode(pl, extractor); + if (path.empty()) { + return; } + const FieldsSet filter{{path}}; + ConstPayload pl(t_, *v_); + BaseEncoder encoder(nullptr, &filter); + FieldsExtractor extractor(&krefs, expectedType, path.size(), &filter); + encoder.Encode(pl, extractor); +} + +template +void PayloadIface::GetByJsonPath(const TagsPath &tagsPath, VariantArray &krefs, KeyValueType expectedType) const { + getByJsonPath(tagsPath, krefs, expectedType); } template void PayloadIface::GetByJsonPath(const IndexedTagsPath &tagsPath, VariantArray &krefs, KeyValueType expectedType) const { - ConstPayload pl(t_, *v_); - FieldsSet filter({tagsPath}); - BaseEncoder encoder(nullptr, &filter); - krefs.Clear(); - if (!tagsPath.empty()) { - FieldsExtractor extractor(&krefs, expectedType, tagsPath.size(), &filter); - encoder.Encode(pl, extractor); + getByJsonPath(tagsPath, krefs, expectedType); +} + +template +void PayloadIface::GetByFieldsSet(const FieldsSet &fields, VariantArray &kvs, KeyValueType expectedType, + const std::vector &expectedCompositeTypes) const { + if (expectedType.Is()) { + kvs.Clear(); + kvs.emplace_back(GetComposite(fields, expectedCompositeTypes)); + } else { + assertrx_throw(fields.size() == 1); + if (fields[0] == IndexValueType::SetByJsonPath) { + assertrx_throw(fields.getTagsPathsLength() == 1); + if (fields.isTagsPathIndexed(0)) { + getByJsonPath(fields.getIndexedTagsPath(0), kvs, expectedType); + } else { + getByJsonPath(fields.getTagsPath(0), kvs, expectedType); + } + } else { + Get(fields[0], kvs); + } } } +template +Variant PayloadIface::GetComposite(const FieldsSet &fields, const std::vector &expectedTypes) const { + thread_local VariantArray buffer; + buffer.clear(); + assertrx_throw(fields.size() == expectedTypes.size()); + size_t jsonFieldIdx{0}; + [[maybe_unused]] const size_t maxJsonFieldIdx{fields.getTagsPathsLength()}; + VariantArray buf; + for (size_t i = 0, s = fields.size(); i < s; ++i) { + buf.clear(); + if (fields[i] == IndexValueType::SetByJsonPath) { + assertrx_throw(jsonFieldIdx < maxJsonFieldIdx); + if (fields.isTagsPathIndexed(jsonFieldIdx)) { + getByJsonPath(fields.getIndexedTagsPath(jsonFieldIdx), buf, expectedTypes[i]); + } else { + getByJsonPath(fields.getTagsPath(jsonFieldIdx), buf, expectedTypes[i]); + } + ++jsonFieldIdx; + } else { + Get(fields[i], buf); + } + assertrx_throw(buf.size() == 1); + buffer.emplace_back(std::move(buf[0])); + } + return Variant{buffer}; +} + template VariantArray PayloadIface::GetIndexedArrayData(const IndexedTagsPath &tagsPath, int field, int &offset, int &size) const { if (tagsPath.empty()) { @@ -143,9 +190,8 @@ void PayloadIface::Set(int field, int idx, const Variant &v) { pv.Set(v); } -template -// template ::value>::type *> -int PayloadIface::ResizeArray(int field, int count, bool append) { +template <> +int PayloadIface::ResizeArray(int field, int count, bool append) { assertrx(t_.Field(field).IsArray()); size_t realSize = RealSize(); @@ -162,7 +208,7 @@ int PayloadIface::ResizeArray(int field, int count, bool append) { assertrx(insert <= realSize); - const_cast(v_)->Resize(realSize, realSize + grow - strip); + v_->Resize(realSize, realSize + grow - strip); memmove(v_->Ptr() + insert + grow - strip, v_->Ptr() + insert, realSize - insert); arr = reinterpret_cast(Field(field).p_); @@ -199,8 +245,13 @@ void PayloadIface::SerializeFields(WrSerializer &ser, const FieldsSet &fields for (int field : fields) { if (field == IndexValueType::SetByJsonPath) { assertrx(tagPathIdx < fields.getTagsPathsLength()); - const TagsPath &tagsPath = fields.getTagsPath(tagPathIdx); - GetByJsonPath(tagsPath, varr, KeyValueType::Undefined{}); + if (fields.isTagsPathIndexed(tagPathIdx)) { + const IndexedTagsPath &tagsPath = fields.getIndexedTagsPath(tagPathIdx); + GetByJsonPath(tagsPath, varr, KeyValueType::Undefined{}); + } else { + const TagsPath &tagsPath = fields.getTagsPath(tagPathIdx); + GetByJsonPath(tagsPath, varr, KeyValueType::Undefined{}); + } if (varr.empty()) { throw Error(errParams, "PK serializing error: field [%s] cannot not be empty", fields.getJsonPath(tagPathIdx)); } @@ -281,8 +332,13 @@ size_t PayloadIface::GetHash(const FieldsSet &fields) const { ret ^= Field(field).Hash(); } else { assertrx(tagPathIdx < fields.getTagsPathsLength()); - const TagsPath &tagsPath = fields.getTagsPath(tagPathIdx++); - GetByJsonPath(tagsPath, keys1, KeyValueType::Undefined{}); + if (fields.isTagsPathIndexed(tagPathIdx)) { + const IndexedTagsPath &tagsPath = fields.getIndexedTagsPath(tagPathIdx++); + GetByJsonPath(tagsPath, keys1, KeyValueType::Undefined{}); + } else { + const TagsPath &tagsPath = fields.getTagsPath(tagPathIdx++); + GetByJsonPath(tagsPath, keys1, KeyValueType::Undefined{}); + } ret ^= keys1.Hash(); } } @@ -334,9 +390,15 @@ bool PayloadIface::IsEQ(const T &other, const FieldsSet &fields) const { if (!Field(field).IsEQ(o.Field(field))) return false; } } else { - const TagsPath &tagsPath = fields.getTagsPath(tagPathIdx++); - GetByJsonPath(tagsPath, keys1, KeyValueType::Undefined{}); - o.GetByJsonPath(tagsPath, keys2, KeyValueType::Undefined{}); + if (fields.isTagsPathIndexed(tagPathIdx)) { + const IndexedTagsPath &tagsPath = fields.getIndexedTagsPath(tagPathIdx++); + GetByJsonPath(tagsPath, keys1, KeyValueType::Undefined{}); + o.GetByJsonPath(tagsPath, keys2, KeyValueType::Undefined{}); + } else { + const TagsPath &tagsPath = fields.getTagsPath(tagPathIdx++); + GetByJsonPath(tagsPath, keys1, KeyValueType::Undefined{}); + o.GetByJsonPath(tagsPath, keys2, KeyValueType::Undefined{}); + } if (keys1 != keys2) { return false; } @@ -386,7 +448,6 @@ int PayloadIface::Compare(const T &other, const FieldsSet &fields, size_t &fi bool commonOpts = (collateOpts.size() == 1); for (size_t i = 0; i < fields.size(); ++i) { - int cmpRes = 0; const auto field(fields[i]); if (commonOpts) { assertrx(collateOpts.size()); @@ -395,31 +456,41 @@ int PayloadIface::Compare(const T &other, const FieldsSet &fields, size_t &fi } const CollateOpts *opts(commonOpts ? collateOpts[0] : collateOpts[i]); if (field != IndexValueType::SetByJsonPath) { - cmpRes = Field(field).Get().Compare(o.Field(field).Get(), opts ? *opts : CollateOpts()); + int cmpRes = Field(field).Get().Compare(o.Field(field).Get(), opts ? *opts : CollateOpts()); + if (cmpRes) { + firstDifferentFieldIdx = i; + return cmpRes; + } } else { assertrx(tagPathIdx < fields.getTagsPathsLength()); - const TagsPath &tagsPath = fields.getTagsPath(tagPathIdx++); - GetByJsonPath(tagsPath, krefs1, KeyValueType::Undefined{}); - o.GetByJsonPath(tagsPath, krefs2, KeyValueType::Undefined{}); + if (fields.isTagsPathIndexed(tagPathIdx)) { + const IndexedTagsPath &tagsPath = fields.getIndexedTagsPath(tagPathIdx++); + GetByJsonPath(tagsPath, krefs1, KeyValueType::Undefined{}); + o.GetByJsonPath(tagsPath, krefs2, KeyValueType::Undefined{}); + } else { + const TagsPath &tagsPath = fields.getTagsPath(tagPathIdx++); + GetByJsonPath(tagsPath, krefs1, KeyValueType::Undefined{}); + o.GetByJsonPath(tagsPath, krefs2, KeyValueType::Undefined{}); + } size_t length = std::min(krefs1.size(), krefs2.size()); for (size_t j = 0; j < length; ++j) { - cmpRes = krefs1[j].RelaxCompare(krefs2[j], opts ? *opts : CollateOpts()); - if (cmpRes) break; - } - if (cmpRes == 0) { - if (krefs1.size() < krefs2.size()) { - cmpRes = -1; - } else if (krefs1.size() > krefs2.size()) { - cmpRes = 1; + int cmpRes = krefs1[j].RelaxCompare(krefs2[j], opts ? *opts : CollateOpts()); + if (cmpRes) { + firstDifferentFieldIdx = i; + return cmpRes; } } - } - - firstDifferentFieldIdx = i; - if (cmpRes > 0) return 1; - if (cmpRes < 0) return -1; + if (krefs1.size() < krefs2.size()) { + firstDifferentFieldIdx = i; + return -1; + } + if (krefs1.size() > krefs2.size()) { + firstDifferentFieldIdx = i; + return 1; + } + } } return 0; } diff --git a/cpp_src/core/payload/payloadiface.h b/cpp_src/core/payload/payloadiface.h index 345622e56..4ff6e6b62 100644 --- a/cpp_src/core/payload/payloadiface.h +++ b/cpp_src/core/payload/payloadiface.h @@ -112,6 +112,9 @@ class PayloadIface { void GetByJsonPath(std::string_view jsonPath, TagsMatcher &tagsMatcher, VariantArray &, KeyValueType expectedType) const; void GetByJsonPath(const TagsPath &jsonPath, VariantArray &, KeyValueType expectedType) const; void GetByJsonPath(const IndexedTagsPath &jsonPath, VariantArray &, KeyValueType expectedType) const; + void GetByFieldsSet(const FieldsSet &, VariantArray &, KeyValueType expectedType, + const std::vector &expectedCompositeTypes) const; + [[nodiscard]] Variant GetComposite(const FieldsSet &, const std::vector &expectedTypes) const; VariantArray GetIndexedArrayData(const IndexedTagsPath &jsonPath, int field, int &offset, int &size) const; // Get fields count @@ -170,6 +173,8 @@ class PayloadIface { T CopyWithRemovedFields(PayloadType t); template void copyOrMoveStrings(int field, StrHolder &dest, bool copy); + template + void getByJsonPath(const P &path, VariantArray &, KeyValueType expectedType) const; template ::value>::type * = nullptr> void setArray(int field, const VariantArray &keys, bool append); @@ -179,6 +184,11 @@ class PayloadIface { T *v_; }; +template <> +int PayloadIface::ResizeArray(int, int, bool); +template <> +int PayloadIface::ResizeArray(int, int, bool) = delete; + template <> void PayloadIface::GetJSON(const TagsMatcher &, WrSerializer &); template <> diff --git a/cpp_src/core/payload/payloadtype.h b/cpp_src/core/payload/payloadtype.h index b6cb5a240..96e92f40d 100644 --- a/cpp_src/core/payload/payloadtype.h +++ b/cpp_src/core/payload/payloadtype.h @@ -18,7 +18,7 @@ class PayloadType : public shared_cow_ptr { PayloadType &operator=(PayloadType &&) = default; PayloadType &operator=(const PayloadType &) = default; PayloadType(const std::string &name, std::initializer_list fields = {}); - PayloadType(const PayloadTypeImpl &impl); + explicit PayloadType(const PayloadTypeImpl &impl); ~PayloadType(); const PayloadFieldType &Field(int field) const; diff --git a/cpp_src/core/payload/payloadvalue.cc b/cpp_src/core/payload/payloadvalue.cc index 6f8a8e6d7..fe1aafe97 100644 --- a/cpp_src/core/payload/payloadvalue.cc +++ b/cpp_src/core/payload/payloadvalue.cc @@ -1,27 +1,19 @@ #include "payloadvalue.h" -#include +#include #include "core/keyvalue/p_string.h" -#include "string.h" -#include "tools/errors.h" + namespace reindexer { PayloadValue::PayloadValue(size_t size, const uint8_t *ptr, size_t cap) : p_(nullptr) { p_ = alloc((cap != 0) ? cap : size); - if (ptr) + if (ptr) { memcpy(Ptr(), ptr, size); - else + } else { memset(Ptr(), 0, size); -} - -PayloadValue::PayloadValue(const PayloadValue &other) noexcept : p_(other.p_) { - if (p_) { - header()->refcount.fetch_add(1, std::memory_order_relaxed); } } -PayloadValue::~PayloadValue() { release(); } - uint8_t *PayloadValue::alloc(size_t cap) { auto pn = reinterpret_cast(operator new(cap + sizeof(dataHeader))); dataHeader *nheader = reinterpret_cast(pn); @@ -44,7 +36,7 @@ void PayloadValue::release() noexcept { void PayloadValue::Clone(size_t size) { // If we have exclusive data - just up lsn - if (p_ && header()->refcount.load() == 1) { + if (p_ && header()->refcount.load(std::memory_order_acquire) == 1) { return; } assertrx(size || p_); @@ -64,7 +56,7 @@ void PayloadValue::Clone(size_t size) { void PayloadValue::Resize(size_t oldSize, size_t newSize) { assertrx(p_); - assertrx(header()->refcount.load() == 1); + assertrx(header()->refcount.load(std::memory_order_acquire) == 1); if (newSize <= header()->cap) return; diff --git a/cpp_src/core/payload/payloadvalue.h b/cpp_src/core/payload/payloadvalue.h index 344fecbe5..4214f15f8 100644 --- a/cpp_src/core/payload/payloadvalue.h +++ b/cpp_src/core/payload/payloadvalue.h @@ -15,17 +15,21 @@ class PayloadValue { struct dataHeader { dataHeader() noexcept : refcount(1), cap(0), lsn(-1) {} - ~dataHeader() { assertrx(refcount.load() == 0); } + ~dataHeader() { assertrx(refcount.load(std::memory_order_acquire) == 0); } refcounter refcount; unsigned cap; lsn_t lsn; }; PayloadValue() noexcept : p_(nullptr) {} - PayloadValue(const PayloadValue &) noexcept; + PayloadValue(const PayloadValue &other) noexcept : p_(other.p_) { + if (p_) { + header()->refcount.fetch_add(1, std::memory_order_relaxed); + } + } // Alloc payload store with size, and copy data from another array PayloadValue(size_t size, const uint8_t *ptr = nullptr, size_t cap = 0); - ~PayloadValue(); + ~PayloadValue() { release(); } PayloadValue &operator=(const PayloadValue &other) noexcept { if (&other != this) { release(); diff --git a/cpp_src/core/query/dsl/dslencoder.cc b/cpp_src/core/query/dsl/dslencoder.cc index 988119ec6..66eaf1821 100644 --- a/cpp_src/core/query/dsl/dslencoder.cc +++ b/cpp_src/core/query/dsl/dslencoder.cc @@ -2,11 +2,11 @@ #include #include "core/cjson/jsonbuilder.h" -#include "core/keyvalue/key_string.h" #include "core/keyvalue/p_string.h" #include "core/query/query.h" #include "core/queryresults/aggregationresult.h" #include "dslparser.h" +#include "tools/logger.h" struct EnumClassHash { template @@ -18,19 +18,21 @@ struct EnumClassHash { namespace reindexer { namespace dsl { -const std::unordered_map join_types = { +static const std::unordered_map join_types = { {InnerJoin, "inner"}, {LeftJoin, "left"}, {OrInnerJoin, "orinner"}}; -const std::unordered_map cond_map = { +static const std::unordered_map cond_map = { {CondAny, "any"}, {CondEq, "eq"}, {CondLt, "lt"}, {CondLe, "le"}, {CondGt, "gt"}, {CondGe, "ge"}, {CondRange, "range"}, {CondSet, "set"}, {CondAllSet, "allset"}, {CondEmpty, "empty"}, {CondLike, "like"}, {CondDWithin, "dwithin"}, }; -const std::unordered_map op_map = {{OpOr, "or"}, {OpAnd, "and"}, {OpNot, "not"}}; +static const std::unordered_map op_map = {{OpOr, "or"}, {OpAnd, "and"}, {OpNot, "not"}}; -const std::unordered_map reqtotal_values = { +static const std::unordered_map reqtotal_values = { {ModeNoTotal, "disabled"}, {ModeAccurateTotal, "enabled"}, {ModeCachedTotal, "cached"}}; +enum class QueryScope { Main, Subquery }; + template std::string get(std::unordered_map const& m, const T& key) { auto it = m.find(key); @@ -39,7 +41,7 @@ std::string get(std::unordered_map const& m, cons return std::string(); } -void encodeSorting(const SortingEntries& sortingEntries, JsonBuilder& builder) { +static void encodeSorting(const SortingEntries& sortingEntries, JsonBuilder& builder) { auto arrNode = builder.Array("sort"); for (const SortingEntry& sortingEntry : sortingEntries) { @@ -47,10 +49,10 @@ void encodeSorting(const SortingEntries& sortingEntries, JsonBuilder& builder) { } } -void encodeSingleJoinQuery(const JoinedQuery& joinQuery, JsonBuilder& builder); +static void encodeSingleJoinQuery(const JoinedQuery& joinQuery, JsonBuilder& builder); -void encodeJoins(const Query& query, JsonBuilder& builder) { - for (const auto& joinQuery : query.joinQueries_) { +static void encodeJoins(const Query& query, JsonBuilder& builder) { + for (const auto& joinQuery : query.GetJoinQueries()) { if (joinQuery.joinType == LeftJoin) { auto node = builder.Object(); encodeSingleJoinQuery(joinQuery, node); @@ -58,7 +60,7 @@ void encodeJoins(const Query& query, JsonBuilder& builder) { } } -void encodeEqualPositions(const EqualPositions_t& equalPositions, JsonBuilder& builder) { +static void encodeEqualPositions(const EqualPositions_t& equalPositions, JsonBuilder& builder) { if (equalPositions.empty()) return; auto node = builder.Object(); auto epNodePositions = node.Array("equal_positions"); @@ -69,41 +71,55 @@ void encodeEqualPositions(const EqualPositions_t& equalPositions, JsonBuilder& b } } -void encodeFilters(const Query& query, JsonBuilder& builder) { +static void encodeFilters(const Query& query, JsonBuilder& builder) { auto arrNode = builder.Array("filters"); - query.entries.ToDsl(query, arrNode); + query.Entries().ToDsl(query, arrNode); encodeJoins(query, arrNode); - encodeEqualPositions(query.entries.equalPositions, arrNode); + encodeEqualPositions(query.Entries().equalPositions, arrNode); } -void toDsl(const Query& query, JsonBuilder& builder); +static void toDsl(const Query& query, QueryScope scope, JsonBuilder& builder); -void encodeMergedQueries(const Query& query, JsonBuilder& builder) { +static void encodeMergedQueries(const Query& query, JsonBuilder& builder) { auto arrNode = builder.Array("merge_queries"); - for (const Query& mq : query.mergeQueries_) { + for (const Query& mq : query.GetMergeQueries()) { auto node = arrNode.Object(); - toDsl(mq, node); + toDsl(mq, QueryScope::Main, node); } } -void encodeSelectFilter(const Query& query, JsonBuilder& builder) { +static void encodeSelectFilter(const Query& query, JsonBuilder& builder) { auto arrNode = builder.Array("select_filter"); - for (auto& str : query.selectFilter_) arrNode.Put(nullptr, str); + for (auto& str : query.SelectFilters()) arrNode.Put(nullptr, str); } -void encodeSelectFunctions(const Query& query, JsonBuilder& builder) { +static void encodeSelectFunctions(const Query& query, JsonBuilder& builder) { auto arrNode = builder.Array("select_functions"); for (auto& str : query.selectFunctions_) arrNode.Put(nullptr, str); } -void encodeAggregationFunctions(const Query& query, JsonBuilder& builder) { +static void encodeAggregationFunctions(const Query& query, JsonBuilder& builder) { auto arrNode = builder.Array("aggregations"); for (const auto& entry : query.aggregations_) { auto aggNode = arrNode.Object(); aggNode.Put("type", AggTypeToStr(entry.Type())); - encodeSorting(entry.Sorting(), aggNode); + switch (entry.Type()) { + case AggDistinct: + case AggFacet: + encodeSorting(entry.Sorting(), aggNode); + case AggSum: + case AggAvg: + case AggMin: + case AggMax: + case AggCount: + case AggCountCached: + case AggUnknown: + default: + break; + } + if (entry.Limit() != QueryEntry::kDefaultLimit) aggNode.Put("limit", entry.Limit()); if (entry.Offset() != QueryEntry::kDefaultOffset) aggNode.Put("offset", entry.Offset()); auto fldNode = aggNode.Array("fields"); @@ -113,21 +129,21 @@ void encodeAggregationFunctions(const Query& query, JsonBuilder& builder) { } } -void encodeJoinEntry(const QueryJoinEntry& joinEntry, JsonBuilder& builder) { - builder.Put("left_field", joinEntry.index_); - builder.Put("right_field", joinEntry.joinIndex_); - builder.Put("cond", get(cond_map, joinEntry.condition_)); - builder.Put("op", get(op_map, joinEntry.op_)); +static void encodeJoinEntry(const QueryJoinEntry& joinEntry, JsonBuilder& builder) { + builder.Put("left_field", joinEntry.LeftFieldName()); + builder.Put("right_field", joinEntry.RightFieldName()); + builder.Put("cond", get(cond_map, joinEntry.Condition())); + builder.Put("op", get(op_map, joinEntry.Operation())); } -void encodeSingleJoinQuery(const JoinedQuery& joinQuery, JsonBuilder& builder) { +static void encodeSingleJoinQuery(const JoinedQuery& joinQuery, JsonBuilder& builder) { using namespace std::string_view_literals; auto node = builder.Object("join_query"sv); node.Put("type", get(join_types, joinQuery.joinType)); - node.Put("namespace", joinQuery._namespace); - node.Put("limit", joinQuery.count); - node.Put("offset", joinQuery.start); + node.Put("namespace", joinQuery.NsName()); + node.Put("limit", joinQuery.Limit()); + node.Put("offset", joinQuery.Offset()); encodeFilters(joinQuery, node); encodeSorting(joinQuery.sortingEntries_, node); @@ -141,28 +157,32 @@ void encodeSingleJoinQuery(const JoinedQuery& joinQuery, JsonBuilder& builder) { arr1.End(); // Close array auto selectFilters = node.Array("select_filter"); - for (const auto& str : joinQuery.selectFilter_) { + for (const auto& str : joinQuery.SelectFilters()) { selectFilters.Put(nullptr, str); } } -void encodeFilter(const QueryEntry& qentry, JsonBuilder& builder) { - if (qentry.distinct) return; - builder.Put("cond", get(cond_map, CondType(qentry.condition))); - builder.Put("field", qentry.index); - - if (qentry.values.empty()) return; - if (qentry.values.size() > 1 || qentry.values[0].Type().Is()) { +static void putValues(JsonBuilder& builder, const VariantArray& values) { + if (values.empty()) { + return; + } else if (values.size() > 1 || values[0].Type().Is()) { auto arrNode = builder.Array("value"); - for (const Variant& kv : qentry.values) { + for (const Variant& kv : values) { arrNode.Put(nullptr, kv); } } else { - builder.Put("value", qentry.values[0]); + builder.Put("value", values[0]); } } -void encodeDropFields(const Query& query, JsonBuilder& builder) { +static void encodeFilter(const QueryEntry& qentry, JsonBuilder& builder) { + if (qentry.Distinct()) return; + builder.Put("cond", get(cond_map, CondType(qentry.Condition()))); + builder.Put("field", qentry.FieldName()); + putValues(builder, qentry.Values()); +} + +static void encodeDropFields(const Query& query, JsonBuilder& builder) { auto dropFields = builder.Array("drop_fields"); for (const UpdateEntry& updateEntry : query.UpdateFields()) { if (updateEntry.Mode() == FieldModeDrop) { @@ -171,7 +191,7 @@ void encodeDropFields(const Query& query, JsonBuilder& builder) { } } -void encodeUpdateFields(const Query& query, JsonBuilder& builder) { +static void encodeUpdateFields(const Query& query, JsonBuilder& builder) { auto updateFields = builder.Array("update_fields"); for (const UpdateEntry& updateEntry : query.UpdateFields()) { if (updateEntry.Mode() == FieldModeSet || updateEntry.Mode() == FieldModeSetJson) { @@ -198,35 +218,41 @@ void encodeUpdateFields(const Query& query, JsonBuilder& builder) { } } -void toDsl(const Query& query, JsonBuilder& builder) { +static void toDsl(const Query& query, QueryScope scope, JsonBuilder& builder) { switch (query.Type()) { case QueryType::QuerySelect: { - builder.Put("namespace", query._namespace); - builder.Put("limit", query.count); - builder.Put("offset", query.start); - builder.Put("req_total", get(reqtotal_values, query.calcTotal)); - builder.Put("explain", query.explain_); - if (query.local_) { - builder.Put("local", true); - } - builder.Put("type", "select"); - const auto& strictMode = strictModeToString(query.strictMode); - if (!strictMode.empty()) { - builder.Put("strict_mode", strictMode); + builder.Put("namespace", query.NsName()); + builder.Put("limit", query.Limit()); + builder.Put("offset", query.Offset()); + builder.Put("req_total", get(reqtotal_values, query.CalcTotal())); + if (scope != QueryScope::Subquery) { + builder.Put("explain", query.GetExplain()); + if (query.IsLocal()) { + builder.Put("local", true); + } + builder.Put("type", "select"); + auto strictMode = strictModeToString(query.GetStrictMode()); + if (!strictMode.empty()) { + builder.Put("strict_mode", strictMode); + } + builder.Put("select_with_rank", query.IsWithRank()); } - builder.Put("select_with_rank", query.IsWithRank()); encodeSelectFilter(query, builder); - encodeSelectFunctions(query, builder); + if (scope != QueryScope::Subquery) { + encodeSelectFunctions(query, builder); + } encodeSorting(query.sortingEntries_, builder); encodeFilters(query, builder); - encodeMergedQueries(query, builder); + if (scope != QueryScope::Subquery) { + encodeMergedQueries(query, builder); + } encodeAggregationFunctions(query, builder); break; } case QueryType::QueryUpdate: { - builder.Put("namespace", query._namespace); - builder.Put("explain", query.explain_); + builder.Put("namespace", query.NsName()); + builder.Put("explain", query.GetExplain()); builder.Put("type", "update"); encodeFilters(query, builder); bool withDropEntries = false, withUpdateEntries = false; @@ -247,26 +273,24 @@ void toDsl(const Query& query, JsonBuilder& builder) { break; } case QueryType::QueryDelete: { - builder.Put("namespace", query._namespace); - builder.Put("explain", query.explain_); + builder.Put("namespace", query.NsName()); + builder.Put("explain", query.GetExplain()); builder.Put("type", "delete"); encodeFilters(query, builder); break; } case QueryType::QueryTruncate: { - builder.Put("namespace", query._namespace); + builder.Put("namespace", query.NsName()); builder.Put("type", "truncate"); break; } - default: - break; } } std::string toDsl(const Query& query) { WrSerializer ser; JsonBuilder builder(ser); - toDsl(query, builder); + toDsl(query, QueryScope::Main, builder); builder.End(); return std::string(ser.Slice()); @@ -279,24 +303,45 @@ void QueryEntries::toDsl(const_iterator it, const_iterator to, const Query& pare auto node = builder.Object(); node.Put("op", dsl::get(dsl::op_map, it->operation)); it->InvokeAppropriate( - Skip{}, + [&node](const AlwaysFalse&) { + logPrintf(LogTrace, "Not normalized query to dsl"); + node.Put("always", false); + }, + [&node](const AlwaysTrue&) { + logPrintf(LogTrace, "Not normalized query to dsl"); + node.Put("always", true); + }, + [&node, &parentQuery](const SubQueryEntry& sqe) { + node.Put("cond", dsl::get(dsl::cond_map, CondType(sqe.Condition()))); + { + auto subquery = node.Object("subquery"); + dsl::toDsl(parentQuery.GetSubQuery(sqe.QueryIndex()), dsl::QueryScope::Subquery, subquery); + } + dsl::putValues(node, sqe.Values()); + }, + [&node, &parentQuery](const SubQueryFieldEntry& sqe) { + node.Put("cond", dsl::get(dsl::cond_map, CondType(sqe.Condition()))); + node.Put("field", sqe.FieldName()); + auto subquery = node.Object("subquery"); + dsl::toDsl(parentQuery.GetSubQuery(sqe.QueryIndex()), dsl::QueryScope::Subquery, subquery); + }, [&it, &node, &parentQuery](const QueryEntriesBracket& bracket) { auto arrNode = node.Array("filters"); toDsl(it.cbegin(), it.cend(), parentQuery, arrNode); dsl::encodeEqualPositions(bracket.equalPositions, arrNode); }, [&node](const QueryEntry& qe) { - if (qe.distinct) return; + if (qe.Distinct()) return; dsl::encodeFilter(qe, node); }, [&node, &parentQuery](const JoinQueryEntry& jqe) { - assertrx(jqe.joinIndex < parentQuery.joinQueries_.size()); - dsl::encodeSingleJoinQuery(parentQuery.joinQueries_[jqe.joinIndex], node); + assertrx(jqe.joinIndex < parentQuery.GetJoinQueries().size()); + dsl::encodeSingleJoinQuery(parentQuery.GetJoinQueries()[jqe.joinIndex], node); }, [&node](const BetweenFieldsQueryEntry& qe) { node.Put("cond", dsl::get(dsl::cond_map, CondType(qe.Condition()))); - node.Put("first_field", qe.firstIndex); - node.Put("second_field", qe.secondIndex); + node.Put("first_field", qe.LeftFieldName()); + node.Put("second_field", qe.RightFieldName()); }); } } diff --git a/cpp_src/core/query/dsl/dslparser.cc b/cpp_src/core/query/dsl/dslparser.cc index be7431a35..48c553be6 100644 --- a/cpp_src/core/query/dsl/dslparser.cc +++ b/cpp_src/core/query/dsl/dslparser.cc @@ -1,6 +1,5 @@ #include "dslparser.h" #include "core/cjson/jschemachecker.h" -#include "core/cjson/jsonbuilder.h" #include "core/query/query.h" #include "estl/fast_hash_map.h" #include "gason/gason.h" @@ -39,7 +38,7 @@ enum class Root { enum class Sort { Desc, Field, Values }; enum class JoinRoot { Type, On, Namespace, Filters, Sort, Limit, Offset, SelectFilter }; enum class JoinEntry { LeftField, RightField, Cond, Op }; -enum class Filter { Cond, Op, Field, Value, Filters, JoinQuery, FirstField, SecondField, EqualPositions }; +enum class Filter { Cond, Op, Field, Value, Filters, JoinQuery, FirstField, SecondField, EqualPositions, SubQuery, Always }; enum class Aggregation { Fields, Type, Sort, Limit, Offset }; enum class EqualPosition { Positions }; enum class UpdateField { Name, Type, Values, IsArray }; @@ -96,7 +95,9 @@ static const fast_str_map filter_map = {{"cond", Filter::Cond}, {"join_query", Filter::JoinQuery}, {"first_field", Filter::FirstField}, {"second_field", Filter::SecondField}, - {"equal_positions", Filter::EqualPositions}}; + {"equal_positions", Filter::EqualPositions}, + {"subquery", Filter::SubQuery}, + {"always", Filter::Always}}; // additional for 'filter::cond' field @@ -262,8 +263,10 @@ static void parseFilter(const JsonValue& filter, Query& q, std::vector subquery; + bool always{}; checkJsonValueType(filter, "filter", JSON_OBJECT); - enum { ENTRY, BRACKET, TWO_FIELDS_ENTRY, JOIN, EQUAL_POSITIONS } entryType = ENTRY; + enum { ENTRY, BRACKET, TWO_FIELDS_ENTRY, JOIN, EQUAL_POSITIONS, SUB_QUERY, ALWAYS } entryType = ENTRY; int elemsParsed = 0; for (const auto& elem : filter) { auto& v = elem.value; @@ -308,10 +311,10 @@ static void parseFilter(const JsonValue& filter, Query& q, std::vector(); + parse(v, *subquery); + entryType = SUB_QUERY; + break; + case Filter::Always: + if (v.getTag() == JSON_TRUE) { + always = true; + } else if (v.getTag() == JSON_FALSE) { + always = false; + } else { + throw Error{errParseDSL, "Wrong DSL format: 'always' fields in filter should be boolean"}; + } + entryType = ALWAYS; + break; } ++elemsParsed; } @@ -326,52 +344,41 @@ static void parseFilter(const JsonValue& filter, Query& q, std::vector 0); - const auto& qjoin = q.joinQueries_.back(); + assertrx(q.GetJoinQueries().size() > 0); + const auto& qjoin = q.GetJoinQueries().back(); if (qjoin.joinType != JoinType::LeftJoin) { - q.entries.Append((qjoin.joinType == JoinType::InnerJoin) ? OpAnd : OpOr, JoinQueryEntry(q.joinQueries_.size() - 1)); + q.AppendQueryEntry((qjoin.joinType == JoinType::InnerJoin) ? OpAnd : OpOr, q.GetJoinQueries().size() - 1); } break; } case EQUAL_POSITIONS: break; + case SUB_QUERY: + q.NextOp(op); + if (fields[0].empty()) { + q.Where(std::move(*subquery), condition, std::move(values)); + } else if (values.empty()) { + q.Where(std::move(fields[0]), condition, std::move(*subquery)); + } else { + throw Error{errParseDSL, "Wrong DSL format: 'value', 'subquery' and 'field' fields in one filter"}; + } + break; + case ALWAYS: + if (always) { + q.AppendQueryEntry(op); + } else { + q.AppendQueryEntry(op); + } + break; } } @@ -381,30 +388,32 @@ static void parseJoinedEntries(const JsonValue& joinEntries, JoinedQuery& qjoin) auto& joinEntry = element.value; checkJsonValueType(joinEntry, "Joined", JSON_OBJECT); - QueryJoinEntry qjoinEntry; + OpType op{OpAnd}; + CondType cond{}; + std::string leftField, rightField; for (const auto& subelement : joinEntry) { auto& value = subelement.value; std::string_view name = subelement.key; switch (get(joined_entry_map, name, "join_query.on"sv)) { case JoinEntry::LeftField: checkJsonValueType(value, name, JSON_STRING); - qjoinEntry.index_ = std::string(value.toString()); + leftField = std::string(value.toString()); break; case JoinEntry::RightField: checkJsonValueType(value, name, JSON_STRING); - qjoinEntry.joinIndex_ = std::string(value.toString()); + rightField = std::string(value.toString()); break; case JoinEntry::Cond: checkJsonValueType(value, name, JSON_STRING); - qjoinEntry.condition_ = get(cond_map, value.toString(), "condition enum"sv); + cond = get(cond_map, value.toString(), "condition enum"sv); break; case JoinEntry::Op: checkJsonValueType(value, name, JSON_STRING); - qjoinEntry.op_ = get(op_map, value.toString(), "operation enum"sv); + op = get(op_map, value.toString(), "operation enum"sv); break; } } - qjoin.joinEntries_.emplace_back(qjoinEntry); + qjoin.joinEntries_.emplace_back(op, cond, std::move(leftField), std::move(rightField)); } } @@ -421,7 +430,7 @@ void parseSingleJoinQuery(const JsonValue& join, Query& query) { break; case JoinRoot::Namespace: checkJsonValueType(value, name, JSON_STRING); - qjoin._namespace = std::string(value.toString()); + qjoin.SetNsName(value.toString()); break; case JoinRoot::Filters: checkJsonValueType(value, name, JSON_ARRAY); @@ -432,11 +441,11 @@ void parseSingleJoinQuery(const JsonValue& join, Query& query) { break; case JoinRoot::Limit: checkJsonValueType(value, name, JSON_NUMBER, JSON_DOUBLE); - qjoin.count = static_cast(value.toNumber()); + qjoin.Limit(static_cast(value.toNumber())); break; case JoinRoot::Offset: checkJsonValueType(value, name, JSON_NUMBER, JSON_DOUBLE); - qjoin.start = static_cast(value.toNumber()); + qjoin.Offset(static_cast(value.toNumber())); break; case JoinRoot::On: parseJoinedEntries(value, qjoin); @@ -446,19 +455,21 @@ void parseSingleJoinQuery(const JsonValue& join, Query& query) { if (!qjoin.CanAddSelectFilter()) { throw Error(errConflict, kAggregationWithSelectFieldsMsgError); } - parseStringArray(value, qjoin.selectFilter_); + std::vector selectFilters; + parseStringArray(value, selectFilters); + qjoin.Select(std::move(selectFilters)); break; } } } for (auto&& eqPos : equalPositions) { if (eqPos.first == 0) { - qjoin.entries.equalPositions.emplace_back(std::move(eqPos.second)); + qjoin.SetEqualPositions(std::move(eqPos.second)); } else { - qjoin.entries.Get(eqPos.first - 1).equalPositions.emplace_back(std::move(eqPos.second)); + qjoin.SetEqualPositions(eqPos.first - 1, std::move(eqPos.second)); } } - query.joinQueries_.emplace_back(std::move(qjoin)); + query.AddJoinQuery(std::move(qjoin)); } static void parseMergeQueries(const JsonValue& mergeQueries, Query& query) { @@ -468,7 +479,7 @@ static void parseMergeQueries(const JsonValue& mergeQueries, Query& query) { JoinedQuery qmerged; parse(merged, qmerged); qmerged.joinType = Merge; - query.mergeQueries_.emplace_back(std::move(qmerged)); + query.Merge(std::move(qmerged)); } } @@ -606,17 +617,17 @@ void parse(const JsonValue& root, Query& q) { switch (get(root_map, name, "root"sv)) { case Root::Namespace: checkJsonValueType(v, name, JSON_STRING); - q._namespace = std::string(v.toString()); + q.SetNsName(v.toString()); break; case Root::Limit: checkJsonValueType(v, name, JSON_NUMBER, JSON_DOUBLE); - q.count = static_cast(v.toNumber()); + q.Limit(static_cast(v.toNumber())); break; case Root::Offset: checkJsonValueType(v, name, JSON_NUMBER, JSON_DOUBLE); - q.start = static_cast(v.toNumber()); + q.Offset(static_cast(v.toNumber())); break; case Root::Filters: @@ -636,7 +647,9 @@ void parse(const JsonValue& root, Query& q) { throw Error(errConflict, kAggregationWithSelectFieldsMsgError); } checkJsonValueType(v, name, JSON_ARRAY); - parseStringArray(v, q.selectFilter_); + std::vector selectFilters; + parseStringArray(v, selectFilters); + q.Select(std::move(selectFilters)); break; } case Root::SelectFunctions: @@ -645,7 +658,7 @@ void parse(const JsonValue& root, Query& q) { break; case Root::ReqTotal: checkJsonValueType(v, name, JSON_STRING); - q.calcTotal = get(reqtotal_values, v.toString(), "req_total enum"sv); + q.CalcTotal(get(reqtotal_values, v.toString(), "req_total enum"sv)); break; case Root::Aggregations: checkJsonValueType(v, name, JSON_ARRAY); @@ -653,11 +666,11 @@ void parse(const JsonValue& root, Query& q) { break; case Root::Explain: checkJsonValueType(v, name, JSON_FALSE, JSON_TRUE); - q.explain_ = v.getTag() == JSON_TRUE; + q.Explain(v.getTag() == JSON_TRUE); break; case Root::Local: checkJsonValueType(v, name, JSON_FALSE, JSON_TRUE); - q.local_ = v.getTag() == JSON_TRUE; + q.Local(v.getTag() == JSON_TRUE); break; case Root::WithRank: checkJsonValueType(v, name, JSON_FALSE, JSON_TRUE); @@ -665,8 +678,8 @@ void parse(const JsonValue& root, Query& q) { break; case Root::StrictMode: checkJsonValueType(v, name, JSON_STRING); - q.strictMode = strictModeFromString(std::string(v.toString())); - if (q.strictMode == StrictModeNotSet) { + q.Strict(strictModeFromString(std::string(v.toString()))); + if (q.GetStrictMode() == StrictModeNotSet) { throw Error(errParseDSL, "Unexpected strict mode value: %s", v.toString()); } break; @@ -692,20 +705,20 @@ void parse(const JsonValue& root, Query& q) { } for (auto&& eqPos : equalPositions) { if (eqPos.first == 0) { - q.entries.equalPositions.emplace_back(std::move(eqPos.second)); + q.SetEqualPositions(std::move(eqPos.second)); } else { - q.entries.Get(eqPos.first - 1).equalPositions.emplace_back(std::move(eqPos.second)); + q.SetEqualPositions(eqPos.first - 1, std::move(eqPos.second)); } } } #include "query.json.h" -Error Parse(const std::string& str, Query& q) { +Error Parse(std::string_view str, Query& q) { static JsonSchemaChecker schemaChecker(kQueryJson, "query"); try { gason::JsonParser parser; - auto root = parser.Parse(std::string_view(str)); + auto root = parser.Parse(str); Error err = schemaChecker.Check(root); if (!err.ok()) return err; dsl::parse(root.value, q); diff --git a/cpp_src/core/query/dsl/dslparser.h b/cpp_src/core/query/dsl/dslparser.h index d9d6afd00..ef8df2bbf 100644 --- a/cpp_src/core/query/dsl/dslparser.h +++ b/cpp_src/core/query/dsl/dslparser.h @@ -1,12 +1,15 @@ #pragma once -#include -#include "core/type_consts.h" +#include #include "tools/errors.h" namespace reindexer { + class Query; + namespace dsl { -Error Parse(const std::string& dsl, Query& q); + +[[nodiscard]] Error Parse(std::string_view dsl, Query& q); + } // namespace dsl } // namespace reindexer diff --git a/cpp_src/core/query/dsl/query.json.h b/cpp_src/core/query/dsl/query.json.h index 4aa753618..f235462be 100644 --- a/cpp_src/core/query/dsl/query.json.h +++ b/cpp_src/core/query/dsl/query.json.h @@ -1 +1 @@ -inline const std::string kQueryJson = {0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6d,0x65,0x72,0x67,0x65,0x5f,0x71,0x75,0x65,0x72,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6d,0x65,0x72,0x67,0x65,0x5f,0x71,0x75,0x65,0x72,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6d,0x65,0x72,0x67,0x65,0x5f,0x71,0x75,0x65,0x72,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6d,0x65,0x72,0x67,0x65,0x5f,0x71,0x75,0x65,0x72,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x72,0x6f,0x70,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x75,0x70,0x64,0x61,0x74,0x65,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x65,0x78,0x70,0x72,0x65,0x73,0x73,0x69,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x69,0x73,0x5f,0x61,0x72,0x72,0x61,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x65,0x78,0x70,0x6c,0x61,0x69,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x77,0x69,0x74,0x68,0x5f,0x72,0x61,0x6e,0x6b,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x74,0x72,0x69,0x63,0x74,0x5f,0x6d,0x6f,0x64,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x72,0x6f,0x70,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x75,0x70,0x64,0x61,0x74,0x65,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x65,0x78,0x70,0x72,0x65,0x73,0x73,0x69,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x69,0x73,0x5f,0x61,0x72,0x72,0x61,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x65,0x78,0x70,0x6c,0x61,0x69,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x77,0x69,0x74,0x68,0x5f,0x72,0x61,0x6e,0x6b,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x74,0x72,0x69,0x63,0x74,0x5f,0x6d,0x6f,0x64,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x72,0x6f,0x70,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x75,0x70,0x64,0x61,0x74,0x65,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x65,0x78,0x70,0x72,0x65,0x73,0x73,0x69,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x69,0x73,0x5f,0x61,0x72,0x72,0x61,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x65,0x78,0x70,0x6c,0x61,0x69,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x77,0x69,0x74,0x68,0x5f,0x72,0x61,0x6e,0x6b,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x74,0x72,0x69,0x63,0x74,0x5f,0x6d,0x6f,0x64,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x72,0x6f,0x70,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x75,0x70,0x64,0x61,0x74,0x65,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x65,0x78,0x70,0x72,0x65,0x73,0x73,0x69,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x69,0x73,0x5f,0x61,0x72,0x72,0x61,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x65,0x78,0x70,0x6c,0x61,0x69,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x77,0x69,0x74,0x68,0x5f,0x72,0x61,0x6e,0x6b,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x74,0x72,0x69,0x63,0x74,0x5f,0x6d,0x6f,0x64,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x66,0x69,0x6e,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x51,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6d,0x65,0x72,0x67,0x65,0x5f,0x71,0x75,0x65,0x72,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6d,0x65,0x72,0x67,0x65,0x5f,0x71,0x75,0x65,0x72,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6d,0x65,0x72,0x67,0x65,0x5f,0x71,0x75,0x65,0x72,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6d,0x65,0x72,0x67,0x65,0x5f,0x71,0x75,0x65,0x72,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x72,0x6f,0x70,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x75,0x70,0x64,0x61,0x74,0x65,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x65,0x78,0x70,0x72,0x65,0x73,0x73,0x69,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x69,0x73,0x5f,0x61,0x72,0x72,0x61,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x65,0x78,0x70,0x6c,0x61,0x69,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x77,0x69,0x74,0x68,0x5f,0x72,0x61,0x6e,0x6b,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x74,0x72,0x69,0x63,0x74,0x5f,0x6d,0x6f,0x64,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x72,0x6f,0x70,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x75,0x70,0x64,0x61,0x74,0x65,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x65,0x78,0x70,0x72,0x65,0x73,0x73,0x69,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x69,0x73,0x5f,0x61,0x72,0x72,0x61,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x65,0x78,0x70,0x6c,0x61,0x69,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x77,0x69,0x74,0x68,0x5f,0x72,0x61,0x6e,0x6b,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x74,0x72,0x69,0x63,0x74,0x5f,0x6d,0x6f,0x64,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x72,0x6f,0x70,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x75,0x70,0x64,0x61,0x74,0x65,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x65,0x78,0x70,0x72,0x65,0x73,0x73,0x69,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x69,0x73,0x5f,0x61,0x72,0x72,0x61,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x65,0x78,0x70,0x6c,0x61,0x69,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x77,0x69,0x74,0x68,0x5f,0x72,0x61,0x6e,0x6b,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x74,0x72,0x69,0x63,0x74,0x5f,0x6d,0x6f,0x64,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x72,0x6f,0x70,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x75,0x70,0x64,0x61,0x74,0x65,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x65,0x78,0x70,0x72,0x65,0x73,0x73,0x69,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x69,0x73,0x5f,0x61,0x72,0x72,0x61,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x65,0x78,0x70,0x6c,0x61,0x69,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x77,0x69,0x74,0x68,0x5f,0x72,0x61,0x6e,0x6b,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x74,0x72,0x69,0x63,0x74,0x5f,0x6d,0x6f,0x64,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x46,0x69,0x6c,0x74,0x65,0x72,0x44,0x65,0x66,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x53,0x6f,0x72,0x74,0x44,0x65,0x66,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x4f,0x6e,0x44,0x65,0x66,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x4a,0x6f,0x69,0x6e,0x65,0x64,0x44,0x65,0x66,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x45,0x71,0x75,0x61,0x6c,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x44,0x65,0x66,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x55,0x70,0x64,0x61,0x74,0x65,0x46,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x65,0x78,0x70,0x72,0x65,0x73,0x73,0x69,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x69,0x73,0x5f,0x61,0x72,0x72,0x61,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x41,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x53,0x6f,0x72,0x74,0x44,0x65,0x66,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x41,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x44,0x65,0x66,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,}; \ No newline at end of file +inline const std::string kQueryJson = {0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6d,0x65,0x72,0x67,0x65,0x5f,0x71,0x75,0x65,0x72,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6d,0x65,0x72,0x67,0x65,0x5f,0x71,0x75,0x65,0x72,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6d,0x65,0x72,0x67,0x65,0x5f,0x71,0x75,0x65,0x72,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6d,0x65,0x72,0x67,0x65,0x5f,0x71,0x75,0x65,0x72,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x72,0x6f,0x70,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x75,0x70,0x64,0x61,0x74,0x65,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x65,0x78,0x70,0x72,0x65,0x73,0x73,0x69,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x69,0x73,0x5f,0x61,0x72,0x72,0x61,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x65,0x78,0x70,0x6c,0x61,0x69,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x77,0x69,0x74,0x68,0x5f,0x72,0x61,0x6e,0x6b,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x74,0x72,0x69,0x63,0x74,0x5f,0x6d,0x6f,0x64,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x72,0x6f,0x70,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x75,0x70,0x64,0x61,0x74,0x65,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x65,0x78,0x70,0x72,0x65,0x73,0x73,0x69,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x69,0x73,0x5f,0x61,0x72,0x72,0x61,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x65,0x78,0x70,0x6c,0x61,0x69,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x77,0x69,0x74,0x68,0x5f,0x72,0x61,0x6e,0x6b,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x74,0x72,0x69,0x63,0x74,0x5f,0x6d,0x6f,0x64,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x72,0x6f,0x70,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x75,0x70,0x64,0x61,0x74,0x65,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x65,0x78,0x70,0x72,0x65,0x73,0x73,0x69,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x69,0x73,0x5f,0x61,0x72,0x72,0x61,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x65,0x78,0x70,0x6c,0x61,0x69,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x77,0x69,0x74,0x68,0x5f,0x72,0x61,0x6e,0x6b,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x74,0x72,0x69,0x63,0x74,0x5f,0x6d,0x6f,0x64,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x72,0x6f,0x70,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x75,0x70,0x64,0x61,0x74,0x65,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x65,0x78,0x70,0x72,0x65,0x73,0x73,0x69,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x69,0x73,0x5f,0x61,0x72,0x72,0x61,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x65,0x78,0x70,0x6c,0x61,0x69,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x77,0x69,0x74,0x68,0x5f,0x72,0x61,0x6e,0x6b,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x74,0x72,0x69,0x63,0x74,0x5f,0x6d,0x6f,0x64,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x66,0x69,0x6e,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x51,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6d,0x65,0x72,0x67,0x65,0x5f,0x71,0x75,0x65,0x72,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6d,0x65,0x72,0x67,0x65,0x5f,0x71,0x75,0x65,0x72,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6d,0x65,0x72,0x67,0x65,0x5f,0x71,0x75,0x65,0x72,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6d,0x65,0x72,0x67,0x65,0x5f,0x71,0x75,0x65,0x72,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x72,0x6f,0x70,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x75,0x70,0x64,0x61,0x74,0x65,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x65,0x78,0x70,0x72,0x65,0x73,0x73,0x69,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x69,0x73,0x5f,0x61,0x72,0x72,0x61,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x65,0x78,0x70,0x6c,0x61,0x69,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x77,0x69,0x74,0x68,0x5f,0x72,0x61,0x6e,0x6b,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x74,0x72,0x69,0x63,0x74,0x5f,0x6d,0x6f,0x64,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x72,0x6f,0x70,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x75,0x70,0x64,0x61,0x74,0x65,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x65,0x78,0x70,0x72,0x65,0x73,0x73,0x69,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x69,0x73,0x5f,0x61,0x72,0x72,0x61,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x65,0x78,0x70,0x6c,0x61,0x69,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x77,0x69,0x74,0x68,0x5f,0x72,0x61,0x6e,0x6b,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x74,0x72,0x69,0x63,0x74,0x5f,0x6d,0x6f,0x64,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x72,0x6f,0x70,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x75,0x70,0x64,0x61,0x74,0x65,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x65,0x78,0x70,0x72,0x65,0x73,0x73,0x69,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x69,0x73,0x5f,0x61,0x72,0x72,0x61,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x65,0x78,0x70,0x6c,0x61,0x69,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x77,0x69,0x74,0x68,0x5f,0x72,0x61,0x6e,0x6b,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x74,0x72,0x69,0x63,0x74,0x5f,0x6d,0x6f,0x64,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x72,0x6f,0x70,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x75,0x70,0x64,0x61,0x74,0x65,0x5f,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x65,0x78,0x70,0x72,0x65,0x73,0x73,0x69,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x69,0x73,0x5f,0x61,0x72,0x72,0x61,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x65,0x78,0x70,0x6c,0x61,0x69,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x77,0x69,0x74,0x68,0x5f,0x72,0x61,0x6e,0x6b,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x73,0x74,0x72,0x69,0x63,0x74,0x5f,0x6d,0x6f,0x64,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x46,0x69,0x6c,0x74,0x65,0x72,0x44,0x65,0x66,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x53,0x6f,0x72,0x74,0x44,0x65,0x66,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x4f,0x6e,0x44,0x65,0x66,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x4a,0x6f,0x69,0x6e,0x65,0x64,0x44,0x65,0x66,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x53,0x75,0x62,0x51,0x75,0x65,0x72,0x79,0x41,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x44,0x65,0x66,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x53,0x75,0x62,0x51,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6a,0x6f,0x69,0x6e,0x5f,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x6e,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6c,0x65,0x66,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x72,0x69,0x67,0x68,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x63,0x6f,0x6e,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x70,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x66,0x69,0x72,0x73,0x74,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x65,0x63,0x6f,0x6e,0x64,0x5f,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x75,0x62,0x71,0x75,0x65,0x72,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x72,0x65,0x71,0x5f,0x74,0x6f,0x74,0x61,0x6c,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x66,0x69,0x6c,0x74,0x65,0x72,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x6c,0x77,0x61,0x79,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x65,0x71,0x75,0x61,0x6c,0x5f,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x73,0x65,0x6c,0x65,0x63,0x74,0x5f,0x66,0x69,0x6c,0x74,0x65,0x72,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x61,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x45,0x71,0x75,0x61,0x6c,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x44,0x65,0x66,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x55,0x70,0x64,0x61,0x74,0x65,0x46,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x65,0x78,0x70,0x72,0x65,0x73,0x73,0x69,0x6f,0x6e,0x22,0x2c,0x20,0x22,0x69,0x73,0x5f,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x6e,0x61,0x6d,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x69,0x73,0x5f,0x61,0x72,0x72,0x61,0x79,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x2c,0x20,0x22,0x76,0x61,0x6c,0x75,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x74,0x72,0x75,0x65,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x41,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x53,0x6f,0x72,0x74,0x44,0x65,0x66,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x41,0x67,0x67,0x72,0x65,0x67,0x61,0x74,0x69,0x6f,0x6e,0x73,0x44,0x65,0x66,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x7d,0x2c,0x20,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x73,0x6f,0x72,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x61,0x72,0x72,0x61,0x79,0x22,0x2c,0x20,0x22,0x69,0x74,0x65,0x6d,0x73,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x6f,0x62,0x6a,0x65,0x63,0x74,0x22,0x2c,0x20,0x22,0x72,0x65,0x71,0x75,0x69,0x72,0x65,0x64,0x22,0x3a,0x20,0x5b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x5d,0x2c,0x20,0x22,0x61,0x64,0x64,0x69,0x74,0x69,0x6f,0x6e,0x61,0x6c,0x50,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x66,0x61,0x6c,0x73,0x65,0x2c,0x20,0x22,0x70,0x72,0x6f,0x70,0x65,0x72,0x74,0x69,0x65,0x73,0x22,0x3a,0x20,0x7b,0x22,0x66,0x69,0x65,0x6c,0x64,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x73,0x74,0x72,0x69,0x6e,0x67,0x22,0x7d,0x2c,0x20,0x22,0x64,0x65,0x73,0x63,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x62,0x6f,0x6f,0x6c,0x65,0x61,0x6e,0x22,0x7d,0x7d,0x7d,0x7d,0x2c,0x20,0x22,0x6c,0x69,0x6d,0x69,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x2c,0x20,0x22,0x6f,0x66,0x66,0x73,0x65,0x74,0x22,0x3a,0x20,0x7b,0x22,0x74,0x79,0x70,0x65,0x22,0x3a,0x20,0x22,0x69,0x6e,0x74,0x65,0x67,0x65,0x72,0x22,0x7d,0x7d,0x7d,0x7d,0x7d,}; \ No newline at end of file diff --git a/cpp_src/core/query/query.cc b/cpp_src/core/query/query.cc index 40bdc1391..d78ffa5db 100644 --- a/cpp_src/core/query/query.cc +++ b/cpp_src/core/query/query.cc @@ -1,4 +1,3 @@ - #include "core/query/query.h" #include "core/query/dsl/dslencoder.h" #include "core/query/dsl/dslparser.h" @@ -13,31 +12,101 @@ using namespace std::string_view_literals; const std::string_view kLsnIndexName = "#lsn"sv; const std::string_view kSlaveVersionIndexName = "#slave_version"sv; -Query::Query(std::string __namespace, unsigned _start, unsigned _count, CalcTotalMode _calcTotal) - : _namespace(std::move(__namespace)), start(_start), count(_count), calcTotal(_calcTotal) {} +void Query::checkSubQuery() const { + if rx_unlikely (type_ != QuerySelect) { + throw Error{errQueryExec, "Subquery should be select"}; + } + if rx_unlikely (!joinQueries_.empty()) { + throw Error{errQueryExec, "Join cannot be in subquery"}; + } + if rx_unlikely (!mergeQueries_.empty()) { + throw Error{errQueryExec, "Merge cannot be in subquery"}; + } + if rx_unlikely (!subQueries_.empty()) { + throw Error{errQueryExec, "Subquery cannot be in subquery"}; + } + if rx_unlikely (!selectFunctions_.empty()) { + throw Error{errQueryExec, "Select function cannot be in subquery"}; + } + if rx_unlikely (!updateFields_.empty()) { + throw Error{errQueryExec, "Subquery cannot update"}; + } + if rx_unlikely (withRank_) { + throw Error{errQueryExec, "Subquery cannot request rank"}; + } + if rx_unlikely (isSystemNamespaceNameFast(NsName())) { + throw Error{errQueryExec, "Queries to system namespaces ('%s') are not supported inside subquery", NsName()}; + } + if rx_unlikely (IsWALQuery()) { + throw Error{errQueryExec, "WAL queries are not supported inside subquery"}; + } +} + +void Query::checkSubQueryNoData() const { + if rx_unlikely (!aggregations_.empty()) { + throw Error{errQueryExec, "Aggregaton cannot be in subquery with condition Any or Empty"}; + } + if rx_unlikely (HasLimit() && Limit() != 0) { + throw Error{errQueryExec, "Limit cannot be in subquery with condition Any or Empty"}; + } + if rx_unlikely (HasOffset()) { + throw Error{errQueryExec, "Offset cannot be in subquery with condition Any or Empty"}; + } + if rx_unlikely (calcTotal_ != ModeNoTotal) { + throw Error{errQueryExec, "Total request cannot be in subquery with condition Any or Empty"}; + } + if rx_unlikely (!selectFilter_.empty()) { + throw Error{errQueryExec, "Select fields filter cannot be in subquery with condition Any or Empty"}; + } + checkSubQuery(); +} + +void Query::checkSubQueryWithData() const { + if rx_unlikely ((aggregations_.size() + selectFilter_.size() + (calcTotal_ == ModeNoTotal ? 0 : 1)) != 1) { + throw Error{errQueryExec, "Subquery should contain exactly one of aggregation, select field filter or total request"}; + } + if (!aggregations_.empty()) { + switch (aggregations_[0].Type()) { + case AggDistinct: + case AggUnknown: + case AggFacet: + throw Error{errQueryExec, "Aggregation %s cannot be in subquery", AggTypeToStr(aggregations_[0].Type())}; + case AggMin: + case AggMax: + case AggAvg: + case AggSum: + case AggCount: + case AggCountCached: + break; + } + } + checkSubQuery(); +} + +void Query::VerifyForUpdate() const { + if (!subQueries_.empty()) { + throw Error{errQueryExec, "UPDATE and DELETE query cannot contain subqueries"}; + } + if (!joinQueries_.empty()) { + throw Error{errQueryExec, "UPDATE and DELETE query cannot contain join"}; + } +} bool Query::operator==(const Query &obj) const { - if (entries != obj.entries) return false; - if (aggregations_ != obj.aggregations_) return false; - - if (_namespace != obj._namespace) return false; - if (sortingEntries_ != obj.sortingEntries_) return false; - if (calcTotal != obj.calcTotal) return false; - if (start != obj.start) return false; - if (count != obj.count) return false; - if (debugLevel != obj.debugLevel) return false; - if (strictMode != obj.strictMode) return false; - if (forcedSortOrder_.size() != obj.forcedSortOrder_.size()) return false; + if (entries_ != obj.entries_ || aggregations_ != obj.aggregations_ || + + NsName() != obj.NsName() || sortingEntries_ != obj.sortingEntries_ || CalcTotal() != obj.CalcTotal() || Offset() != obj.Offset() || + Limit() != obj.Limit() || debugLevel_ != obj.debugLevel_ || strictMode_ != obj.strictMode_ || selectFilter_ != obj.selectFilter_ || + selectFunctions_ != obj.selectFunctions_ || joinQueries_ != obj.joinQueries_ || mergeQueries_ != obj.mergeQueries_ || + updateFields_ != obj.updateFields_ || subQueries_ != obj.subQueries_ || forcedSortOrder_.size() != obj.forcedSortOrder_.size()) { + return false; + } for (size_t i = 0, s = forcedSortOrder_.size(); i < s; ++i) { - if (forcedSortOrder_[i].RelaxCompare(obj.forcedSortOrder_[i]) != 0) return false; + if (forcedSortOrder_[i].RelaxCompare(obj.forcedSortOrder_[i]) != 0) { + return false; + } } - if (selectFilter_ != obj.selectFilter_) return false; - if (selectFunctions_ != obj.selectFunctions_) return false; - if (joinQueries_ != obj.joinQueries_) return false; - if (mergeQueries_ != obj.mergeQueries_) return false; - if (updateFields_ != obj.updateFields_) return false; - return true; } @@ -46,58 +115,88 @@ bool JoinedQuery::operator==(const JoinedQuery &obj) const { if (joinType != obj.joinType) return false; return Query::operator==(obj); } -void Query::FromSQL(std::string_view q) { SQLParser(*this).Parse(q); } +Query Query::FromSQL(std::string_view q) { return SQLParser::Parse(q); } -Error Query::FromJSON(const std::string &dsl) { return dsl::Parse(dsl, *this); } +Error Query::FromJSON(std::string_view dsl) { return dsl::Parse(dsl, *this); } std::string Query::GetJSON() const { return dsl::toDsl(*this); } -Query &Query::SetObject(std::string field, VariantArray value, bool hasExpressions) & { - for (auto &it : value) { - if (!it.Type().Is()) { - throw Error(errLogic, "Unexpected variant type in SetObject: %s. Expecting KeyValueType::String with JSON-content", - it.Type().Name()); - } - } - updateFields_.emplace_back(std::move(field), std::move(value), FieldModeSetJson, hasExpressions); - return *this; -} - WrSerializer &Query::GetSQL(WrSerializer &ser, bool stripArgs) const { return SQLEncoder(*this).GetSQL(ser, stripArgs); } +WrSerializer &Query::GetSQL(WrSerializer &ser, QueryType realType, bool stripArgs) const { + return SQLEncoder(*this, realType).GetSQL(ser, stripArgs); +} std::string Query::GetSQL(bool stripArgs) const { WrSerializer ser; return std::string(GetSQL(ser, stripArgs).Slice()); } +std::string Query::GetSQL(QueryType realType) const { + WrSerializer ser; + return std::string(SQLEncoder(*this, realType).GetSQL(ser, false).Slice()); +} + +void Query::Join(JoinedQuery &&jq) & { + switch (jq.joinType) { + case JoinType::Merge: + if (nextOp_ != OpAnd) { + throw Error(errParams, "Merge query with %s operation", OpTypeToStr(nextOp_)); + } + mergeQueries_.emplace_back(std::move(jq)); + return; + case JoinType::LeftJoin: + if (nextOp_ != OpAnd) { + throw Error(errParams, "Left join with %s operation", OpTypeToStr(nextOp_)); + } + break; + case JoinType::OrInnerJoin: + if (nextOp_ == OpNot) { + throw Error(errParams, "Or inner join with %s operation", OpTypeToStr(nextOp_)); + } + nextOp_ = OpOr; + [[fallthrough]]; + case JoinType::InnerJoin: + entries_.Append(nextOp_, JoinQueryEntry(joinQueries_.size())); + nextOp_ = OpAnd; + break; + } + joinQueries_.emplace_back(std::move(jq)); + adoptNested(joinQueries_.back()); +} + +VariantArray Query::deserializeValues(Serializer &ser, CondType cond) { + VariantArray values; + auto cnt = ser.GetVarUint(); + if (cond == CondDWithin) { + if (cnt != 3) { + throw Error(errParseBin, "Expected point and distance for DWithin"); + } + VariantArray point; + point.reserve(2); + point.emplace_back(ser.GetVariant().EnsureHold()); + point.emplace_back(ser.GetVariant().EnsureHold()); + values.reserve(2); + values.emplace_back(std::move(point)); + values.emplace_back(ser.GetVariant().EnsureHold()); + } else { + values.reserve(cnt); + while (cnt--) values.emplace_back(ser.GetVariant().EnsureHold()); + } + return values; +} + void Query::deserialize(Serializer &ser, bool &hasJoinConditions) { bool end = false; std::vector> equalPositions; while (!end && !ser.Eof()) { - int qtype = ser.GetVarUint(); + QueryItemType qtype = QueryItemType(ser.GetVarUint()); switch (qtype) { case QueryCondition: { - QueryEntry qe; - qe.index = std::string(ser.GetVString()); - OpType op = OpType(ser.GetVarUint()); - qe.condition = CondType(ser.GetVarUint()); - int cnt = ser.GetVarUint(); - if (qe.condition == CondDWithin) { - if (cnt != 3) { - throw Error(errParseBin, "Expected point and distance for DWithin"); - } - VariantArray point; - point.reserve(2); - point.emplace_back(ser.GetVariant().EnsureHold()); - point.emplace_back(ser.GetVariant().EnsureHold()); - qe.values.reserve(2); - qe.values.emplace_back(std::move(point)); - qe.values.emplace_back(ser.GetVariant().EnsureHold()); - } else { - qe.values.reserve(cnt); - while (cnt--) qe.values.emplace_back(ser.GetVariant().EnsureHold()); - } - entries.Append(op, std::move(qe)); + const auto fieldName = ser.GetVString(); + const OpType op = OpType(ser.GetVarUint()); + const CondType condition = CondType(ser.GetVarUint()); + VariantArray values = deserializeValues(ser, condition); + entries_.Append(op, std::string{fieldName}, condition, std::move(values)); break; } case QueryBetweenFieldsCondition: { @@ -105,12 +204,17 @@ void Query::deserialize(Serializer &ser, bool &hasJoinConditions) { std::string firstField{ser.GetVString()}; CondType condition = static_cast(ser.GetVarUint()); std::string secondField{ser.GetVString()}; - entries.Append(op, BetweenFieldsQueryEntry{std::move(firstField), condition, std::move(secondField)}); + entries_.Append(op, std::move(firstField), condition, std::move(secondField)); break; } case QueryAlwaysFalseCondition: { const OpType op = OpType(ser.GetVarUint()); - entries.Append(op, AlwaysFalse{}); + entries_.Append(op); + break; + } + case QueryAlwaysTrueCondition: { + const OpType op = OpType(ser.GetVarUint()); + entries_.Append(op); break; } case QueryJoinCondition: { @@ -119,7 +223,7 @@ void Query::deserialize(Serializer &ser, bool &hasJoinConditions) { JoinQueryEntry joinEntry(ser.GetVarUint()); hasJoinConditions = true; // NOLINTNEXTLINE(performance-move-const-arg) - entries.Append((type == JoinType::OrInnerJoin) ? OpOr : OpAnd, std::move(joinEntry)); + entries_.Append((type == JoinType::OrInnerJoin) ? OpOr : OpAnd, std::move(joinEntry)); break; } case QueryAggregation: { @@ -155,12 +259,9 @@ void Query::deserialize(Serializer &ser, bool &hasJoinConditions) { break; } case QueryDistinct: { - QueryEntry qe; - qe.index = std::string(ser.GetVString()); - if (!qe.index.empty()) { - qe.distinct = true; - qe.condition = CondAny; - entries.Append(OpAnd, std::move(qe)); + const auto fieldName = ser.GetVString(); + if (!fieldName.empty()) { + entries_.Append(OpAnd, std::string{fieldName}, QueryEntry::DistinctTag{}); } break; } @@ -180,28 +281,28 @@ void Query::deserialize(Serializer &ser, bool &hasJoinConditions) { break; } case QueryJoinOn: { - QueryJoinEntry qje; - qje.op_ = OpType(ser.GetVarUint()); - qje.condition_ = CondType(ser.GetVarUint()); - qje.index_ = std::string(ser.GetVString()); - qje.joinIndex_ = std::string(ser.GetVString()); - reinterpret_cast(this)->joinEntries_.push_back(std::move(qje)); + const OpType op = static_cast(ser.GetVarUint()); + const CondType condition = static_cast(ser.GetVarUint()); + std::string leftFieldName{ser.GetVString()}; + std::string rightFieldName{ser.GetVString()}; + reinterpret_cast(this)->joinEntries_.emplace_back(op, condition, std::move(leftFieldName), + std::move(rightFieldName)); break; } case QueryDebugLevel: - debugLevel = ser.GetVarUint(); + Debug(ser.GetVarUint()); break; case QueryStrictMode: - strictMode = StrictMode(ser.GetVarUint()); + strictMode_ = StrictMode(ser.GetVarUint()); break; case QueryLimit: - count = ser.GetVarUint(); + count_ = ser.GetVarUint(); break; case QueryOffset: - start = ser.GetVarUint(); + start_ = ser.GetVarUint(); break; case QueryReqTotal: - calcTotal = CalcTotalMode(ser.GetVarUint()); + calcTotal_ = CalcTotalMode(ser.GetVarUint()); break; case QuerySelectFilter: selectFilter_.push_back(std::string(ser.GetVString())); @@ -270,32 +371,53 @@ void Query::deserialize(Serializer &ser, bool &hasJoinConditions) { } case QueryOpenBracket: { OpType op = OpType(ser.GetVarUint()); - entries.OpenBracket(op); + entries_.OpenBracket(op); break; } case QueryCloseBracket: - entries.CloseBracket(); + entries_.CloseBracket(); break; case QueryEnd: end = true; break; + case QuerySubQueryCondition: { + OpType op = OpType(ser.GetVarUint()); + Serializer subQuery{ser.GetVString()}; + CondType condition = CondType(ser.GetVarUint()); + VariantArray values = deserializeValues(ser, condition); + NextOp(op); + Where(Query::Deserialize(subQuery), condition, std::move(values)); + break; + } + case QueryFieldSubQueryCondition: { + OpType op = OpType(ser.GetVarUint()); + const auto fieldName = ser.GetVString(); + CondType condition = CondType(ser.GetVarUint()); + Serializer subQuery{ser.GetVString()}; + NextOp(op); + Where(fieldName, condition, Query::Deserialize(subQuery)); + break; + } + case QueryAggregationSort: + case QueryAggregationOffset: + case QueryAggregationLimit: default: throw Error(errParseBin, "Unknown type %d while parsing binary buffer", qtype); } } for (auto &&eqPos : equalPositions) { if (eqPos.first == 0) { - entries.equalPositions.emplace_back(std::move(eqPos.second)); + entries_.equalPositions.emplace_back(std::move(eqPos.second)); } else { - entries.Get(eqPos.first - 1).equalPositions.emplace_back(std::move(eqPos.second)); + entries_.Get(eqPos.first - 1).equalPositions.emplace_back(std::move(eqPos.second)); } } return; } void Query::Serialize(WrSerializer &ser, uint8_t mode) const { - ser.PutVString(_namespace); - entries.Serialize(ser); + ser.PutVString(NsName()); + entries_.Serialize(ser, subQueries_); for (const auto &agg : aggregations_) { ser.PutVarUint(QueryAggregation); @@ -336,22 +458,22 @@ void Query::Serialize(WrSerializer &ser, uint8_t mode) const { if (mode & WithJoinEntries) { for (const auto &qje : reinterpret_cast(this)->joinEntries_) { ser.PutVarUint(QueryJoinOn); - ser.PutVarUint(qje.op_); - ser.PutVarUint(qje.condition_); - ser.PutVString(qje.index_); - ser.PutVString(qje.joinIndex_); + ser.PutVarUint(qje.Operation()); + ser.PutVarUint(qje.Condition()); + ser.PutVString(qje.LeftFieldName()); + ser.PutVString(qje.RightFieldName()); } } - for (const auto &equalPoses : entries.equalPositions) { + for (const auto &equalPoses : entries_.equalPositions) { ser.PutVarUint(QueryEqualPosition); ser.PutVarUint(0); ser.PutVarUint(equalPoses.size()); for (const auto &ep : equalPoses) ser.PutVString(ep); } - for (size_t i = 0; i < entries.Size(); ++i) { - if (entries.IsSubTree(i)) { - const auto &bracket = entries.Get(i); + for (size_t i = 0; i < entries_.Size(); ++i) { + if (entries_.IsSubTree(i)) { + const auto &bracket = entries_.Get(i); for (const auto &equalPoses : bracket.equalPositions) { ser.PutVarUint(QueryEqualPosition); ser.PutVarUint(i + 1); @@ -362,27 +484,27 @@ void Query::Serialize(WrSerializer &ser, uint8_t mode) const { } ser.PutVarUint(QueryDebugLevel); - ser.PutVarUint(debugLevel); + ser.PutVarUint(debugLevel_); - if (strictMode != StrictModeNotSet) { + if (strictMode_ != StrictModeNotSet) { ser.PutVarUint(QueryStrictMode); - ser.PutVarUint(int(strictMode)); + ser.PutVarUint(int(strictMode_)); } if (!(mode & SkipLimitOffset)) { if (HasLimit()) { ser.PutVarUint(QueryLimit); - ser.PutVarUint(count); + ser.PutVarUint(Limit()); } if (HasOffset()) { ser.PutVarUint(QueryOffset); - ser.PutVarUint(start); + ser.PutVarUint(Offset()); } } - if (calcTotal != ModeNoTotal) { + if (HasCalcTotal()) { ser.PutVarUint(QueryReqTotal); - ser.PutVarUint(calcTotal); + ser.PutVarUint(CalcTotal()); } for (const auto &sf : selectFilter_) { @@ -447,10 +569,10 @@ void Query::Serialize(WrSerializer &ser, uint8_t mode) const { } } -void Query::Deserialize(Serializer &ser) { - _namespace = std::string(ser.GetVString()); +Query Query::Deserialize(Serializer &ser) { + Query res(ser.GetVString()); bool hasJoinConditions = false; - deserialize(ser, hasJoinConditions); + res.deserialize(ser, hasJoinConditions); bool nested = false; while (!ser.Eof()) { @@ -458,92 +580,54 @@ void Query::Deserialize(Serializer &ser) { JoinedQuery q1(std::string(ser.GetVString())); q1.joinType = joinType; q1.deserialize(ser, hasJoinConditions); - q1.debugLevel = debugLevel; - q1.strictMode = strictMode; + res.adoptNested(q1); if (joinType == JoinType::Merge) { - mergeQueries_.emplace_back(std::move(q1)); + res.mergeQueries_.emplace_back(std::move(q1)); nested = true; } else { - Query &q = nested ? mergeQueries_.back() : *this; + Query &q = nested ? res.mergeQueries_.back() : res; if (joinType != JoinType::LeftJoin && !hasJoinConditions) { - const size_t joinIdx = joinQueries_.size(); - entries.Append((joinType == JoinType::OrInnerJoin) ? OpOr : OpAnd, JoinQueryEntry{joinIdx}); + const size_t joinIdx = res.joinQueries_.size(); + res.entries_.Append((joinType == JoinType::OrInnerJoin) ? OpOr : OpAnd, joinIdx); } q.joinQueries_.emplace_back(std::move(q1)); + q.adoptNested(q.joinQueries_.back()); } } + return res; } -Query &Query::Join(JoinType joinType, const std::string &index, const std::string &joinIndex, CondType cond, OpType op, Query &&qr) & { - QueryJoinEntry joinEntry; - joinEntry.op_ = op; - joinEntry.condition_ = cond; - joinEntry.index_ = index; - joinEntry.joinIndex_ = joinIndex; - auto &jq = joinQueries_.emplace_back(joinType, std::move(qr)); - jq.joinEntries_.emplace_back(std::move(joinEntry)); - if (joinType != JoinType::LeftJoin) { - entries.Append((joinType == JoinType::InnerJoin) ? OpType::OpAnd : OpType::OpOr, JoinQueryEntry(joinQueries_.size() - 1)); - } +Query &Query::Join(JoinType joinType, std::string leftField, std::string rightField, CondType cond, OpType op, Query &&qr) & { + auto jq = JoinedQuery{joinType, std::move(qr)}; + jq.joinEntries_.emplace_back(op, cond, std::move(leftField), std::move(rightField)); + Join(std::move(jq)); return *this; } -Query &Query::Join(JoinType joinType, const std::string &index, const std::string &joinIndex, CondType cond, OpType op, const Query &qr) & { - QueryJoinEntry joinEntry; - joinEntry.op_ = op; - joinEntry.condition_ = cond; - joinEntry.index_ = index; - joinEntry.joinIndex_ = joinIndex; - joinQueries_.emplace_back(joinType, qr); - joinQueries_.back().joinEntries_.emplace_back(std::move(joinEntry)); - if (joinType != JoinType::LeftJoin) { - entries.Append((joinType == JoinType::InnerJoin) ? OpType::OpAnd : OpType::OpOr, JoinQueryEntry(joinQueries_.size() - 1)); - } +Query &Query::Join(JoinType joinType, std::string leftField, std::string rightField, CondType cond, OpType op, const Query &qr) & { + auto jq = JoinedQuery{joinType, qr}; + jq.joinEntries_.emplace_back(op, cond, std::move(leftField), std::move(rightField)); + Join(std::move(jq)); return *this; } -Query::OnHelper Query::Join(JoinType joinType, Query &&q) & { - joinQueries_.emplace_back(joinType, std::move(q)); - if (joinType != JoinType::LeftJoin) { - entries.Append((joinType == JoinType::InnerJoin) ? OpType::OpAnd : OpType::OpOr, JoinQueryEntry(joinQueries_.size() - 1)); - } - return {*this, joinQueries_.back()}; -} - -Query::OnHelper Query::Join(JoinType joinType, const Query &q) & { - joinQueries_.emplace_back(joinType, q); - if (joinType != JoinType::LeftJoin) { - entries.Append((joinType == JoinType::InnerJoin) ? OpType::OpAnd : OpType::OpOr, JoinQueryEntry(joinQueries_.size() - 1)); - } - return {*this, joinQueries_.back()}; -} - -Query::OnHelperR Query::Join(JoinType joinType, Query &&q) && { - joinQueries_.emplace_back(joinType, std::move(q)); - if (joinType != JoinType::LeftJoin) { - entries.Append((joinType == JoinType::InnerJoin) ? OpType::OpAnd : OpType::OpOr, JoinQueryEntry(joinQueries_.size() - 1)); - } - return {std::move(*this), joinQueries_.back()}; -} - -Query::OnHelperR Query::Join(JoinType joinType, const Query &q) && { - joinQueries_.emplace_back(joinType, q); - if (joinType != JoinType::LeftJoin) { - entries.Append((joinType == JoinType::InnerJoin) ? OpType::OpAnd : OpType::OpOr, JoinQueryEntry(joinQueries_.size() - 1)); - } - return {std::move(*this), joinQueries_.back()}; -} - Query &Query::Merge(const Query &q) & { mergeQueries_.emplace_back(JoinType::Merge, q); + adoptNested(mergeQueries_.back()); return *this; } Query &Query::Merge(Query &&q) & { mergeQueries_.emplace_back(JoinType::Merge, std::move(q)); + adoptNested(mergeQueries_.back()); return *this; } +void Query::AddJoinQuery(JoinedQuery &&jq) { + adoptNested(jq); + joinQueries_.emplace_back(std::move(jq)); +} + Query &Query::SortStDistance(std::string_view field, Point p, bool desc) & { if (field.empty()) { throw Error(errParams, "Field name for ST_Distance can not be empty"); @@ -560,21 +644,60 @@ Query &Query::SortStDistance(std::string_view field1, std::string_view field2, b return *this; } -void Query::WalkNested(bool withSelf, bool withMerged, const std::function &visitor) const { +void Query::walkNested(bool withSelf, bool withMerged, bool withSubQueries, + const std::function &visitor) noexcept(noexcept(visitor(std::declval()))) { if (withSelf) visitor(*this); - if (withMerged) + if (withMerged) { for (auto &mq : mergeQueries_) visitor(mq); + if (withSubQueries) { + for (auto &mq : mergeQueries_) { + for (auto &nq : mq.subQueries_) { + nq.walkNested(true, true, true, visitor); + } + } + } + } for (auto &jq : joinQueries_) visitor(jq); - for (auto &mq : mergeQueries_) + for (auto &mq : mergeQueries_) { for (auto &jq : mq.joinQueries_) visitor(jq); + } + if (withSubQueries) { + for (auto &nq : subQueries_) { + nq.walkNested(true, withMerged, true, visitor); + } + } +} + +void Query::WalkNested(bool withSelf, bool withMerged, bool withSubQueries, const std::function &visitor) const + noexcept(noexcept(visitor(std::declval()))) { + if (withSelf) visitor(*this); + if (withMerged) { + for (auto &mq : mergeQueries_) visitor(mq); + if (withSubQueries) { + for (auto &mq : mergeQueries_) { + for (auto &nq : mq.subQueries_) { + nq.WalkNested(true, true, true, visitor); + } + } + } + } + for (auto &jq : joinQueries_) visitor(jq); + for (auto &mq : mergeQueries_) { + for (auto &jq : mq.joinQueries_) visitor(jq); + } + if (withSubQueries) { + for (auto &nq : subQueries_) { + nq.WalkNested(true, withMerged, true, visitor); + } + } } bool Query::IsWALQuery() const noexcept { - if (entries.Size() == 1 && entries.HoldsOrReferTo(0) && kLsnIndexName == entries.Get(0).index) { + if (entries_.Size() == 1 && entries_.Is(0) && kLsnIndexName == entries_.Get(0).FieldName()) { return true; - } else if (entries.Size() == 2 && entries.HoldsOrReferTo(0) && entries.HoldsOrReferTo(1)) { - const auto &index0 = entries.Get(0).index; - const auto &index1 = entries.Get(1).index; + } else if (entries_.Size() == 2 && entries_.Is(0) && entries_.Is(1)) { + const auto &index0 = entries_.Get(0).FieldName(); + const auto &index1 = entries_.Get(1).FieldName(); return (kLsnIndexName == index0 && kSlaveVersionIndexName == index1) || (kLsnIndexName == index1 && kSlaveVersionIndexName == index0); } diff --git a/cpp_src/core/query/query.h b/cpp_src/core/query/query.h index a2ce6d197..da934e88b 100644 --- a/cpp_src/core/query/query.h +++ b/cpp_src/core/query/query.h @@ -22,94 +22,64 @@ constexpr std::string_view kAggregationWithSelectFieldsMsgError = /// Allows to select data from DB. /// Analog to ansi-sql select query. class Query { - template - class OnHelperTempl; - template - class OnHelperGroup { - public: - OnHelperGroup &&Not() &&noexcept { - op_ = OpNot; - return std::move(*this); - } - OnHelperGroup &&Or() &&noexcept { - op_ = OpOr; - return std::move(*this); - } - OnHelperGroup &&On(std::string index, CondType cond, std::string joinIndex) &&; - Q CloseBracket() &&noexcept { return std::forward(q_); } - - private: - OnHelperGroup(Q q, JoinedQuery &jq) noexcept : q_{std::forward(q)}, jq_{jq} {} - Q q_; - JoinedQuery &jq_; - OpType op_{OpAnd}; - friend class OnHelperTempl; - }; - template - class OnHelperTempl { - public: - OnHelperTempl &&Not() &&noexcept { - op_ = OpNot; - return std::move(*this); - } - Q On(std::string index, CondType cond, std::string joinIndex) &&; - OnHelperGroup OpenBracket() &&noexcept { return {std::forward(q_), jq_}; } - - private: - OnHelperTempl(Q q, JoinedQuery &jq) noexcept : q_{std::forward(q)}, jq_{jq} {} - Q q_; - JoinedQuery &jq_; - OpType op_{OpAnd}; - friend class Query; - }; - using OnHelper = OnHelperTempl; - using OnHelperR = OnHelperTempl; - public: /// Creates an object for certain namespace with appropriate settings. /// @param nsName - name of the namespace the data to be selected from. /// @param start - number of the first row to get from selected set. Analog to sql OFFSET Offset. /// @param count - number of rows to get from result set. Analog to sql LIMIT RowsCount. /// @param calcTotal - calculation mode. - explicit Query(std::string nsName, unsigned start = QueryEntry::kDefaultOffset, unsigned count = QueryEntry::kDefaultLimit, - CalcTotalMode calcTotal = ModeNoTotal); + template > * = nullptr> + explicit Query(Str &&nsName, unsigned start = QueryEntry::kDefaultOffset, unsigned count = QueryEntry::kDefaultLimit, + CalcTotalMode calcTotal = ModeNoTotal) + : namespace_(std::forward(nsName)), start_(start), count_(count), calcTotal_(calcTotal) {} - /// Creates an empty object. Query() = default; /// Allows to compare 2 Query objects. - bool operator==(const Query &) const; + [[nodiscard]] bool operator==(const Query &) const; /// Parses pure sql select query and initializes Query object data members as a result. /// @param q - sql query. - void FromSQL(std::string_view q); + [[nodiscard]] static Query FromSQL(std::string_view q); /// Logs query in 'Select field1, ... field N from namespace ...' format. /// @param ser - serializer to store SQL string /// @param stripArgs - replace condition values with '?' WrSerializer &GetSQL(WrSerializer &ser, bool stripArgs = false) const; + /// Logs query in 'Select field1, ... field N from namespace ...' format. + /// @param ser - serializer to store SQL string + /// @param realType - replaces original query's type + /// @param stripArgs - replace condition values with '?' + WrSerializer &GetSQL(WrSerializer &ser, QueryType realType, bool stripArgs = false) const; + /// Logs query in 'Select field1, ... field N from namespace ...' format. /// @param stripArgs - replace condition values with '?' /// @return Query in SQL format - std::string GetSQL(bool stripArgs = false) const; + [[nodiscard]] std::string GetSQL(bool stripArgs = false) const; + + /// Logs query in 'Select field1, ... field N from namespace ...' format. + /// @param realType - replaces original query's type + /// @return Query in SQL format + [[nodiscard]] std::string GetSQL(QueryType realType) const; /// Parses JSON dsl set. /// @param dsl - dsl set. /// @return always returns errOk or throws an exception. - Error FromJSON(const std::string &dsl); + [[nodiscard]] Error FromJSON(std::string_view dsl); /// returns structure of a query in JSON dsl format - std::string GetJSON() const; + [[nodiscard]] std::string GetJSON() const; /// Enable explain query /// @param on - signaling on/off /// @return Query object ready to be executed - Query &Explain(bool on = true) & { // -V1071 - explain_ = on; + Query &Explain(bool on = true) & noexcept { + walkNested(true, true, true, [on](Query &q) noexcept { q.explain_ = on; }); return *this; } - Query &&Explain(bool on = true) && { return std::move(Explain(on)); } + [[nodiscard]] Query &&Explain(bool on = true) && noexcept { return std::move(Explain(on)); } + [[nodiscard]] bool GetExplain() const noexcept { return explain_; } Query &Local(bool on = true) & { // -V1071 local_ = on; @@ -118,79 +88,75 @@ class Query { Query &&Local(bool on = true) && { return std::move(Local(on)); } /// Adds a condition with a single value. Analog to sql Where clause. - /// @param idx - index used in condition clause. + /// @param field - field used in condition clause. /// @param cond - type of condition. /// @param val - value of index to be compared with. /// @return Query object ready to be executed. - template - Query &Where(const std::string &idx, CondType cond, Input val) & { - return Where(idx, cond, {val}); + template > * = nullptr> + Query &Where(Str &&field, CondType cond, Input val) & { + return Where(std::forward(field), cond, {std::forward(val)}); } - template - Query &&Where(const std::string &idx, CondType cond, Input val) && { - return std::move(Where(idx, cond, {val})); + template > * = nullptr> + [[nodiscard]] Query &&Where(Str &&field, CondType cond, Input val) && { + return std::move(Where(std::forward(field), cond, {std::move(val)})); } /// Adds a condition with several values. Analog to sql Where clause. - /// @param idx - index used in condition clause. + /// @param field - field used in condition clause. /// @param cond - type of condition. /// @param l - list of index values to be compared with. /// @return Query object ready to be executed. - template - Query &Where(const std::string &idx, CondType cond, std::initializer_list l) & { - QueryEntry qe; - qe.condition = cond; - qe.index = idx; - for (auto it = l.begin(); it != l.end(); it++) qe.values.push_back(Variant(*it)); - entries.Append(nextOp_, std::move(qe)); + template > * = nullptr> + Query &Where(Str &&field, CondType cond, std::initializer_list l) & { + VariantArray values; + values.reserve(l.size()); + for (auto it = l.begin(); it != l.end(); it++) values.emplace_back(*it); + entries_.Append(nextOp_, std::forward(field), cond, std::move(values)); nextOp_ = OpAnd; return *this; } - template - Query &&Where(const std::string &idx, CondType cond, std::initializer_list l) && { - return std::move(Where(idx, cond, std::move(l))); + template > * = nullptr> + [[nodiscard]] Query &&Where(Str &&field, CondType cond, std::initializer_list l) && { + return std::move(Where(std::forward(field), cond, std::move(l))); } /// Adds a condition with several values. Analog to sql Where clause. - /// @param idx - index used in condition clause. + /// @param field - field used in condition clause. /// @param cond - type of condition. /// @param l - vector of index values to be compared with. /// @return Query object ready to be executed. - template - Query &Where(const std::string &idx, CondType cond, const std::vector &l) & { - QueryEntry qe; - qe.condition = cond; - qe.index = idx; - qe.values.reserve(l.size()); - for (auto it = l.begin(); it != l.end(); it++) qe.values.push_back(Variant(*it)); - entries.Append(nextOp_, std::move(qe)); + template > * = nullptr> + Query &Where(Str &&field, CondType cond, const std::vector &l) & { + VariantArray values; + values.reserve(l.size()); + for (auto it = l.begin(); it != l.end(); it++) values.emplace_back(*it); + entries_.Append(nextOp_, std::forward(field), cond, std::move(values)); nextOp_ = OpAnd; return *this; } - template - Query &&Where(const std::string &idx, CondType cond, const std::vector &l) && { - return std::move(Where(idx, cond, l)); + template > * = nullptr> + [[nodiscard]] Query &&Where(Str &&field, CondType cond, const std::vector &l) && { + return std::move(Where(std::forward(field), cond, l)); } /// Adds a condition with several values. Analog to sql Where clause. - /// @param idx - index used in condition clause. + /// @param field - field used in condition clause. /// @param cond - type of condition. /// @param l - vector of index values to be compared with. /// @return Query object ready to be executed. - Query &Where(const std::string &idx, CondType cond, const VariantArray &l) & { - QueryEntry qe; - qe.condition = cond; - qe.index = idx; - qe.values.reserve(l.size()); - for (auto it = l.begin(); it != l.end(); it++) qe.values.push_back(Variant(*it)); - entries.Append(nextOp_, std::move(qe)); + template > * = nullptr> + Query &Where(Str &&field, CondType cond, VariantArray l) & { + entries_.Append(nextOp_, std::forward(field), cond, std::move(l)); nextOp_ = OpAnd; return *this; } - Query &&Where(const std::string &idx, CondType cond, const VariantArray &l) && { return std::move(Where(idx, cond, l)); } + template > * = nullptr> + [[nodiscard]] Query &&Where(Str &&field, CondType cond, VariantArray l) && { + return std::move(Where(std::forward(field), cond, std::move(l))); + } /// Adds a condition with several values to a composite index. - /// @param idx - index name. + /// @param idx - composite index name. /// @param cond - type of condition. /// @param l - list of values to be compared according to the order /// of indexes in composite index name. @@ -200,308 +166,414 @@ class Query { /// in case of CondRange) belongs to "bookid" and l[0][1] (and l[1][1] in case of CondRange) /// belongs to "price" indexes. /// @return Query object ready to be executed. - Query &WhereComposite(const std::string &idx, CondType cond, std::initializer_list l) & { - QueryEntry qe; - qe.condition = cond; - qe.index = idx; - qe.values.reserve(l.size()); + template > * = nullptr> + Query &WhereComposite(Str &&idx, CondType cond, std::initializer_list l) & { + VariantArray values; + values.reserve(l.size()); for (auto it = l.begin(); it != l.end(); it++) { - qe.values.push_back(Variant(*it)); + values.emplace_back(*it); } - entries.Append(nextOp_, std::move(qe)); + entries_.Append(nextOp_, std::forward(idx), cond, std::move(values)); nextOp_ = OpAnd; return *this; } - Query &&WhereComposite(const std::string &idx, CondType cond, std::initializer_list l) && { - return std::move(WhereComposite(idx, cond, l)); + template > * = nullptr> + [[nodiscard]] Query &&WhereComposite(Str &&idx, CondType cond, std::initializer_list l) && { + return std::move(WhereComposite(std::forward(idx), cond, std::move(l))); } - Query &WhereComposite(const std::string &idx, CondType cond, const std::vector &v) & { - QueryEntry qe; - qe.condition = cond; - qe.index = idx; - qe.values.reserve(v.size()); + template > * = nullptr> + Query &WhereComposite(Str &&idx, CondType cond, const std::vector &v) & { + VariantArray values; + values.reserve(v.size()); for (auto it = v.begin(); it != v.end(); it++) { - qe.values.push_back(Variant(*it)); + values.emplace_back(*it); } - entries.Append(nextOp_, std::move(qe)); + entries_.Append(nextOp_, std::forward(idx), cond, std::move(values)); nextOp_ = OpAnd; return *this; } - Query &&WhereComposite(const std::string &idx, CondType cond, const std::vector &v) && { - return std::move(WhereComposite(idx, cond, v)); + template > * = nullptr> + [[nodiscard]] Query &&WhereComposite(Str &&idx, CondType cond, const std::vector &v) && { + return std::move(WhereComposite(std::forward(idx), cond, v)); } - Query &WhereBetweenFields(std::string firstIdx, CondType cond, std::string secondIdx) & { - entries.Append(nextOp_, BetweenFieldsQueryEntry{std::move(firstIdx), cond, std::move(secondIdx)}); + template + Query &WhereBetweenFields(Str1 &&firstIdx, CondType cond, Str2 &&secondIdx) & { + entries_.Append(nextOp_, std::forward(firstIdx), cond, std::forward(secondIdx)); nextOp_ = OpAnd; return *this; } - Query &&WhereBetweenFields(std::string firstIdx, CondType cond, std::string secondIdx) && { - return std::move(WhereBetweenFields(std::move(firstIdx), cond, std::move(secondIdx))); + template + [[nodiscard]] Query &&WhereBetweenFields(Str1 &&firstIdx, CondType cond, Str2 &&secondIdx) && { + return std::move(WhereBetweenFields(std::forward(firstIdx), cond, std::forward(secondIdx))); } - Query &DWithin(const std::string &idx, Point p, double distance) & { - QueryEntry qe; - qe.condition = CondDWithin; - qe.index = idx; - qe.values.reserve(2); - qe.values.emplace_back(p); - qe.values.emplace_back(distance); - entries.Append(nextOp_, std::move(qe)); + template > * = nullptr> + Query &DWithin(Str &&field, Point p, double distance) & { + entries_.Append(nextOp_, std::forward(field), CondDWithin, VariantArray::Create(p, distance)); nextOp_ = OpAnd; return *this; } - Query &&DWithin(const std::string &idx, Point p, double distance) && { return std::move(DWithin(idx, p, distance)); } + template > * = nullptr> + [[nodiscard]] Query &&DWithin(Str &&field, Point p, double distance) && { + return std::move(DWithin(std::forward(field), p, distance)); + } + Query &DWithin(Query &&q, Point p, double distance) & { return Where(std::move(q), CondDWithin, VariantArray::Create(p, distance)); } + [[nodiscard]] Query &&DWithin(Query &&q, Point p, double distance) && { return std::move(DWithin(std::move(q), p, distance)); } + Query &Where(Query &&q, CondType cond, VariantArray &&values) & { + if (cond == CondEmpty || cond == CondAny) { + q.checkSubQueryNoData(); + q.Limit(0); + } else { + q.checkSubQueryWithData(); + if (!q.selectFilter_.empty() && !q.HasLimit() && !q.HasOffset()) { + q.sortingEntries_.clear(); + q.Where(q.selectFilter_[0], cond, std::move(values)); + q.selectFilter_.clear(); + return Where(std::move(q), CondAny, {}); + } else if (q.HasCalcTotal() || (!q.aggregations_.empty() && + (q.aggregations_[0].Type() == AggCount || q.aggregations_[0].Type() == AggCountCached))) { + q.Limit(0); + } + } + q.Strict(strictMode_).Debug(debugLevel_).Explain(explain_); + entries_.Append(nextOp_, cond, subQueries_.size(), std::move(values)); + PopBackQEGuard guard{&entries_}; + adoptNested(q); + subQueries_.emplace_back(std::move(q)); + guard.Reset(); + nextOp_ = OpAnd; + return *this; + } + [[nodiscard]] Query &&Where(Query &&q, CondType cond, VariantArray &&values) && { + return std::move(Where(std::move(q), cond, std::move(values))); + } + template + Query &Where(Query &&q, CondType cond, std::initializer_list values) & { + return Where(std::move(q), cond, VariantArray::Create(values)); + } + template + [[nodiscard]] Query &&Where(Query &&q, CondType cond, std::initializer_list values) && { + return std::move(Where(std::move(q), cond, VariantArray::Create(values))); + } + + template > * = nullptr> + Query &Where(Str &&field, CondType cond, Query &&q) & { + if (cond == CondDWithin) { + throw Error(errQueryExec, "DWithin between field and subquery"); + } + q.checkSubQueryWithData(); + adoptNested(q); + if (q.HasCalcTotal() || + (!q.aggregations_.empty() && (q.aggregations_[0].Type() == AggCount || q.aggregations_[0].Type() == AggCountCached))) { + q.Limit(0); + } + entries_.Append(nextOp_, std::forward(field), cond, subQueries_.size()); + PopBackQEGuard guard{&entries_}; + adoptNested(q); + subQueries_.emplace_back(std::move(q)); + guard.Reset(); + nextOp_ = OpAnd; + return *this; + } + template > * = nullptr> + [[nodiscard]] Query &&Where(Str &&field, CondType cond, Query &&q) && { + return std::move(Where(std::forward(field), cond, std::move(q))); + } /// Sets a new value for a field. /// @param field - field name. /// @param value - new value. /// @param hasExpressions - true: value has expresions in it - template - Query &Set(std::string field, ValueType value, bool hasExpressions = false) & { - return Set(std::move(field), {value}, hasExpressions); + template > * = nullptr> + Query &Set(Str &&field, ValueType value, bool hasExpressions = false) & { + return Set(std::forward(field), {value}, hasExpressions); } - template - Query &&Set(std::string field, ValueType value, bool hasExpressions = false) && { - return std::move(Set(std::move(field), std::move(value), hasExpressions)); + template > * = nullptr> + [[nodiscard]] Query &&Set(Str &&field, ValueType value, bool hasExpressions = false) && { + return std::move(Set(std::forward(field), std::move(value), hasExpressions)); } /// Sets a new value for a field. /// @param field - field name. /// @param l - new value. /// @param hasExpressions - true: value has expresions in it - template - Query &Set(std::string field, std::initializer_list l, bool hasExpressions = false) & { + template > * = nullptr> + Query &Set(Str &&field, std::initializer_list l, bool hasExpressions = false) & { VariantArray value; value.reserve(l.size()); for (auto it = l.begin(); it != l.end(); it++) value.emplace_back(*it); - return Set(std::move(field), std::move(value), hasExpressions); + return Set(std::forward(field), std::move(value), hasExpressions); } - template - Query &&Set(std::string field, std::initializer_list l, bool hasExpressions = false) && { - return std::move(Set(std::move(field), std::move(l), hasExpressions)); + template > * = nullptr> + [[nodiscard]] Query &&Set(Str &&field, std::initializer_list l, bool hasExpressions = false) && { + return std::move(Set(std::forward(field), std::move(l), hasExpressions)); } /// Sets a new value for a field. /// @param field - field name. /// @param l - new value. /// @param hasExpressions - true: value has expresions in it - template - Query &Set(std::string field, const std::vector &l, bool hasExpressions = false) & { + template > * = nullptr> + Query &Set(Str &&field, const std::vector &l, bool hasExpressions = false) & { VariantArray value; value.reserve(l.size()); for (auto it = l.begin(); it != l.end(); it++) value.emplace_back(*it); - return Set(std::move(field), std::move(value.MarkArray()), hasExpressions); + return Set(std::forward(field), std::move(value.MarkArray()), hasExpressions); } - template - Query &&Set(std::string field, const std::vector &l, bool hasExpressions = false) && { - return std::move(Set(std::move(field), l, hasExpressions)); + template > * = nullptr> + [[nodiscard]] Query &&Set(Str &&field, const std::vector &l, bool hasExpressions = false) && { + return std::move(Set(std::forward(field), l, hasExpressions)); } /// Sets a new value for a field. /// @param field - field name. /// @param value - new value. /// @param hasExpressions - true: value has expresions in it - Query &Set(std::string field, VariantArray value, bool hasExpressions = false) & { - updateFields_.emplace_back(std::move(field), std::move(value), FieldModeSet, hasExpressions); + template > * = nullptr> + Query &Set(Str &&field, VariantArray value, bool hasExpressions = false) & { + updateFields_.emplace_back(std::forward(field), std::move(value), FieldModeSet, hasExpressions); return *this; } - Query &&Set(std::string field, VariantArray value, bool hasExpressions = false) && { - return std::move(Set(std::move(field), std::move(value), hasExpressions)); + template > * = nullptr> + [[nodiscard]] Query &&Set(Str &&field, VariantArray value, bool hasExpressions = false) && { + return std::move(Set(std::forward(field), std::move(value), hasExpressions)); } /// Sets a value for a field as an object. /// @param field - field name. /// @param value - new value. /// @param hasExpressions - true: value has expresions in it - template - Query &SetObject(std::string field, ValueType value, bool hasExpressions = false) & { - return SetObject(std::move(field), {value}, hasExpressions); + template > * = nullptr> + Query &SetObject(Str &&field, ValueType value, bool hasExpressions = false) & { + return SetObject(std::forward(field), {value}, hasExpressions); } - template - Query &&SetObject(std::string field, ValueType value, bool hasExpressions = false) && { - return std::move(SetObject(std::move(field), std::move(value), hasExpressions)); + template > * = nullptr> + [[nodiscard]] Query &&SetObject(Str &&field, ValueType value, bool hasExpressions = false) && { + return std::move(SetObject(std::forward(field), std::move(value), hasExpressions)); } /// Sets a new value for a field as an object. /// @param field - field name. /// @param l - new value. /// @param hasExpressions - true: value has expresions in it - template - Query &SetObject(std::string field, std::initializer_list l, bool hasExpressions = false) & { + template > * = nullptr> + Query &SetObject(Str &&field, std::initializer_list l, bool hasExpressions = false) & { VariantArray value; value.reserve(l.size()); - for (auto it = l.begin(); it != l.end(); it++) value.emplace_back(Variant(*it)); - return SetObject(std::move(field), std::move(value), hasExpressions); + for (auto it = l.begin(); it != l.end(); it++) value.emplace_back(*it); + return SetObject(std::forward(field), std::move(value), hasExpressions); } - template - Query &&SetObject(std::string field, std::initializer_list l, bool hasExpressions = false) && { - return std::move(SetObject(std::move(field), std::move(l), hasExpressions)); + template > * = nullptr> + [[nodiscard]] Query &&SetObject(Str &&field, std::initializer_list l, bool hasExpressions = false) && { + return std::move(SetObject(std::forward(field), std::move(l), hasExpressions)); } /// Sets a new value for a field as an object. /// @param field - field name. /// @param l - new value. /// @param hasExpressions - true: value has expresions in it - template - Query &SetObject(std::string field, const std::vector &l, bool hasExpressions = false) & { + template > * = nullptr> + Query &SetObject(Str &&field, const std::vector &l, bool hasExpressions = false) & { VariantArray value; value.reserve(l.size()); for (auto it = l.begin(); it != l.end(); it++) value.emplace_back(Variant(*it)); - return SetObject(std::move(field), std::move(value.MarkArray()), hasExpressions); + return SetObject(std::forward(field), std::move(value.MarkArray()), hasExpressions); } - template - Query &&SetObject(std::string field, const std::vector &l, bool hasExpressions = false) && { - return std::move(SetObject(std::move(field), l, hasExpressions)); + template > * = nullptr> + [[nodiscard]] Query &&SetObject(Str &&field, const std::vector &l, bool hasExpressions = false) && { + return std::move(SetObject(std::forward(field), l, hasExpressions)); } /// Sets a value for a field as an object. /// @param field - field name. /// @param value - new value. /// @param hasExpressions - true: value has expresions in it - Query &SetObject(std::string field, VariantArray value, bool hasExpressions = false) &; - Query &&SetObject(std::string field, VariantArray value, bool hasExpressions = false) && { - return std::move(SetObject(std::move(field), std::move(value), hasExpressions)); + template > * = nullptr> + Query &SetObject(Str &&field, VariantArray value, bool hasExpressions = false) & { + for (auto &it : value) { + if (!it.Type().Is()) { + throw Error(errLogic, "Unexpected variant type in SetObject: %s. Expecting KeyValueType::String with JSON-content", + it.Type().Name()); + } + } + updateFields_.emplace_back(std::forward(field), std::move(value), FieldModeSetJson, hasExpressions); + return *this; + } + template > * = nullptr> + [[nodiscard]] Query &&SetObject(Str &&field, VariantArray value, bool hasExpressions = false) && { + return std::move(SetObject(std::forward(field), std::move(value), hasExpressions)); } /// Drops a value for a field. /// @param field - field name. - Query &Drop(std::string field) & { - updateFields_.emplace_back(std::move(field), VariantArray(), FieldModeDrop); + template > * = nullptr> + Query &Drop(Str &&field) & { + updateFields_.emplace_back(std::forward(field), VariantArray(), FieldModeDrop); return *this; } - Query &&Drop(std::string field) && { return std::move(Drop(std::move(field))); } + template > * = nullptr> + [[nodiscard]] Query &&Drop(Str &&field) && { + return std::move(Drop(std::forward(field))); + } /// Add sql-function to query. /// @param function - function declaration. - void AddFunction(std::string function) { selectFunctions_.emplace_back(std::move(function)); } + template > * = nullptr> + void AddFunction(Str &&function) { + selectFunctions_.emplace_back(std::forward(function)); + } /// Adds equal position fields to arrays queries. /// @param equalPosition - list of fields with equal array index position. Query &AddEqualPosition(h_vector equalPosition) & { - auto *const bracket = entries.LastOpenBracket(); - auto &eqPos = (bracket ? bracket->equalPositions : entries.equalPositions); + auto *const bracket = entries_.LastOpenBracket(); + auto &eqPos = (bracket ? bracket->equalPositions : entries_.equalPositions); eqPos.emplace_back(std::make_move_iterator(equalPosition.begin()), std::make_move_iterator(equalPosition.end())); return *this; } Query &AddEqualPosition(std::vector equalPosition) & { - auto *const bracket = entries.LastOpenBracket(); - auto &eqPos = (bracket ? bracket->equalPositions : entries.equalPositions); + auto *const bracket = entries_.LastOpenBracket(); + auto &eqPos = (bracket ? bracket->equalPositions : entries_.equalPositions); eqPos.emplace_back(std::make_move_iterator(equalPosition.begin()), std::make_move_iterator(equalPosition.end())); return *this; } Query &AddEqualPosition(std::initializer_list l) & { - auto *const bracket = entries.LastOpenBracket(); - auto &eqPos = (bracket ? bracket->equalPositions : entries.equalPositions); + auto *const bracket = entries_.LastOpenBracket(); + auto &eqPos = (bracket ? bracket->equalPositions : entries_.equalPositions); eqPos.emplace_back(l); return *this; } - Query &&AddEqualPosition(h_vector equalPosition) && { return std::move(AddEqualPosition(std::move(equalPosition))); } - Query &&AddEqualPosition(std::vector equalPosition) && { return std::move(AddEqualPosition(std::move(equalPosition))); } - Query &&AddEqualPosition(std::initializer_list l) && { return std::move(AddEqualPosition(l)); } + [[nodiscard]] Query &&AddEqualPosition(h_vector equalPosition) && { + return std::move(AddEqualPosition(std::move(equalPosition))); + } + [[nodiscard]] Query &&AddEqualPosition(std::vector equalPosition) && { + return std::move(AddEqualPosition(std::move(equalPosition))); + } + [[nodiscard]] Query &&AddEqualPosition(std::initializer_list l) && { return std::move(AddEqualPosition(l)); } /// Joins namespace with another namespace. Analog to sql JOIN. /// @param joinType - type of Join (Inner, Left or OrInner). - /// @param index - name of the field in the namespace of this Query object. - /// @param joinIndex - name of the field in the namespace of qr Query object. + /// @param leftField - name of the field in the namespace of this Query object. + /// @param rightField - name of the field in the namespace of qr Query object. /// @param cond - condition type (Eq, Leq, Geq, etc). /// @param op - operation type (and, or, not). /// @param qr - query of the namespace that is going to be joined with this one. /// @return Query object ready to be executed. - Query &Join(JoinType joinType, const std::string &index, const std::string &joinIndex, CondType cond, OpType op, Query &&qr) &; - Query &&Join(JoinType joinType, const std::string &index, const std::string &joinIndex, CondType cond, OpType op, Query &&qr) && { - return std::move(Join(joinType, index, joinIndex, cond, op, std::move(qr))); + Query &Join(JoinType joinType, std::string leftField, std::string rightField, CondType cond, OpType op, Query &&qr) &; + template + [[nodiscard]] Query &&Join(JoinType joinType, StrL &&leftField, StrR &&rightField, CondType cond, OpType op, Query &&qr) && { + return std::move(Join(joinType, std::forward(leftField), std::forward(rightField), cond, op, std::move(qr))); } - Query &Join(JoinType joinType, const std::string &index, const std::string &joinIndex, CondType cond, OpType op, const Query &qr) &; - Query &&Join(JoinType joinType, const std::string &index, const std::string &joinIndex, CondType cond, OpType op, const Query &qr) && { - return std::move(Join(joinType, index, joinIndex, cond, op, qr)); + Query &Join(JoinType joinType, std::string leftField, std::string rightField, CondType cond, OpType op, const Query &qr) &; + template + [[nodiscard]] Query &&Join(JoinType joinType, StrL &&leftField, StrR &&rightField, CondType cond, OpType op, const Query &qr) && { + return std::move(Join(joinType, std::forward(leftField), std::forward(rightField), cond, op, qr)); } - OnHelper Join(JoinType joinType, Query &&q) &; - OnHelper Join(JoinType joinType, const Query &q) &; - OnHelperR Join(JoinType joinType, Query &&q) &&; - OnHelperR Join(JoinType joinType, const Query &q) &&; + [[nodiscard]] auto Join(JoinType joinType, Query &&q) &; + [[nodiscard]] auto Join(JoinType joinType, const Query &q) &; + [[nodiscard]] auto Join(JoinType joinType, Query &&q) &&; + [[nodiscard]] auto Join(JoinType joinType, const Query &q) &&; /// @public /// Inner Join of this namespace with another one. - /// @param index - name of the field in the namespace of this Query object. - /// @param joinIndex - name of the field in the namespace of qr Query object. + /// @param leftField - name of the field in the namespace of this Query object. + /// @param rightField - name of the field in the namespace of qr Query object. /// @param cond - condition type (Eq, Leq, Geq, etc). /// @param qr - query of the namespace that is going to be joined with this one. /// @return Query object ready to be executed. - Query &InnerJoin(const std::string &index, const std::string &joinIndex, CondType cond, Query &&qr) & { // -V1071 - return Join(JoinType::InnerJoin, index, joinIndex, cond, OpAnd, std::move(qr)); + template + Query &InnerJoin(StrL &&leftField, StrR &&rightField, CondType cond, Query &&qr) & { // -V1071 + return Join(JoinType::InnerJoin, std::forward(leftField), std::forward(rightField), cond, OpAnd, std::move(qr)); } - Query &&InnerJoin(const std::string &index, const std::string &joinIndex, CondType cond, Query &&qr) && { - return std::move(InnerJoin(index, joinIndex, cond, std::move(qr))); + template + [[nodiscard]] Query &&InnerJoin(StrL &&leftField, StrR &&rightField, CondType cond, Query &&qr) && { + return std::move(InnerJoin(std::forward(leftField), std::forward(rightField), cond, std::move(qr))); } - Query &InnerJoin(const std::string &index, const std::string &joinIndex, CondType cond, const Query &qr) & { - return Join(JoinType::InnerJoin, index, joinIndex, cond, OpAnd, qr); + template + Query &InnerJoin(StrL &&leftField, StrR &&rightField, CondType cond, const Query &qr) & { + return Join(JoinType::InnerJoin, std::forward(leftField), std::forward(rightField), cond, OpAnd, qr); } - Query &&InnerJoin(const std::string &index, const std::string &joinIndex, CondType cond, const Query &qr) && { - return std::move(InnerJoin(index, joinIndex, cond, qr)); + template + [[nodiscard]] Query &&InnerJoin(StrL &&leftField, StrR &&rightField, CondType cond, const Query &qr) && { + return std::move(InnerJoin(std::forward(leftField), std::forward(rightField), cond, qr)); } /// Left Join of this namespace with another one. - /// @param index - name of the field in the namespace of this Query object. - /// @param joinIndex - name of the field in the namespace of qr Query object. + /// @param leftField - name of the field in the namespace of this Query object. + /// @param rightField - name of the field in the namespace of qr Query object. /// @param cond - condition type (Eq, Leq, Geq, etc). /// @param qr - query of the namespace that is going to be joined with this one. /// @return Query object ready to be executed. - Query &LeftJoin(const std::string &index, const std::string &joinIndex, CondType cond, Query &&qr) & { - return Join(JoinType::LeftJoin, index, joinIndex, cond, OpAnd, std::move(qr)); + template + Query &LeftJoin(StrL &&leftField, StrR &&rightField, CondType cond, Query &&qr) & { + return Join(JoinType::LeftJoin, std::forward(leftField), std::forward(rightField), cond, OpAnd, std::move(qr)); } - Query &&LeftJoin(const std::string &index, const std::string &joinIndex, CondType cond, Query &&qr) && { - return std::move(LeftJoin(index, joinIndex, cond, std::move(qr))); + template + [[nodiscard]] Query &&LeftJoin(StrL &&leftField, StrR &&rightField, CondType cond, Query &&qr) && { + return std::move(LeftJoin(std::forward(leftField), std::forward(rightField), cond, std::move(qr))); } - Query &LeftJoin(const std::string &index, const std::string &joinIndex, CondType cond, const Query &qr) & { - return Join(JoinType::LeftJoin, index, joinIndex, cond, OpAnd, qr); + template + Query &LeftJoin(StrL &&leftField, StrR &&rightField, CondType cond, const Query &qr) & { + return Join(JoinType::LeftJoin, std::forward(leftField), std::forward(rightField), cond, OpAnd, qr); } - Query &&LeftJoin(const std::string &index, const std::string &joinIndex, CondType cond, const Query &qr) && { - return std::move(LeftJoin(index, joinIndex, cond, qr)); + template + [[nodiscard]] Query &&LeftJoin(StrL &&leftField, StrR &&rightField, CondType cond, const Query &qr) && { + return std::move(LeftJoin(std::forward(leftField), std::forward(rightField), cond, qr)); } /// OrInnerJoin of this namespace with another one. - /// @param index - name of the field in the namespace of this Query object. - /// @param joinIndex - name of the field in the namespace of qr Query object. + /// @param leftField - name of the field in the namespace of this Query object. + /// @param rightField - name of the field in the namespace of qr Query object. /// @param cond - condition type (Eq, Leq, Geq, etc). /// @param qr - query of the namespace that is going to be joined with this one. /// @return a reference to a query object ready to be executed. - Query &OrInnerJoin(const std::string &index, const std::string &joinIndex, CondType cond, Query &&qr) & { - return Join(JoinType::OrInnerJoin, index, joinIndex, cond, OpAnd, std::move(qr)); + template + Query &OrInnerJoin(StrL &&leftField, StrR &&rightField, CondType cond, Query &&qr) & { + return Join(JoinType::OrInnerJoin, std::forward(leftField), std::forward(rightField), cond, OpAnd, std::move(qr)); } - Query &&OrInnerJoin(const std::string &index, const std::string &joinIndex, CondType cond, Query &&qr) && { - return std::move(OrInnerJoin(index, joinIndex, cond, std::move(qr))); + template + [[nodiscard]] Query &&OrInnerJoin(StrL &&leftField, StrR &&rightField, CondType cond, Query &&qr) && { + return std::move(OrInnerJoin(std::forward(leftField), std::forward(rightField), cond, std::move(qr))); } - Query &OrInnerJoin(const std::string &index, const std::string &joinIndex, CondType cond, const Query &qr) & { - return Join(JoinType::OrInnerJoin, index, joinIndex, cond, OpAnd, qr); + template + Query &OrInnerJoin(StrL &&leftField, StrR &&rightField, CondType cond, const Query &qr) & { + return Join(JoinType::OrInnerJoin, std::forward(leftField), std::forward(rightField), cond, OpAnd, qr); } - Query &&OrInnerJoin(const std::string &index, const std::string &joinIndex, CondType cond, const Query &qr) && { - return std::move(OrInnerJoin(index, joinIndex, cond, qr)); + template + [[nodiscard]] Query &&OrInnerJoin(StrL &&leftField, StrR &&rightField, CondType cond, const Query &qr) && { + return std::move(OrInnerJoin(std::forward(leftField), std::forward(rightField), cond, qr)); } Query &Merge(const Query &q) &; - Query &&Merge(const Query &q) && { return std::move(Merge(q)); } + [[nodiscard]] Query &&Merge(const Query &q) && { return std::move(Merge(q)); } Query &Merge(Query &&q) &; - Query &&Merge(Query &&q) && { return std::move(Merge(std::move(q))); } + [[nodiscard]] Query &&Merge(Query &&q) && { return std::move(Merge(std::move(q))); } /// Changes debug level. /// @param level - debug level. /// @return Query object. - Query &Debug(int level) &noexcept { - debugLevel = level; + Query &Debug(int level) & noexcept { + walkNested(true, true, true, [level](Query &q) noexcept { q.debugLevel_ = level; }); return *this; } - Query &&Debug(int level) &&noexcept { return std::move(Debug(level)); } + [[nodiscard]] Query &&Debug(int level) && noexcept { return std::move(Debug(level)); } + [[nodiscard]] int GetDebugLevel() const noexcept { return debugLevel_; } /// Changes strict mode. /// @param mode - strict mode. /// @return Query object. - Query &Strict(StrictMode mode) &noexcept { - strictMode = mode; + Query &Strict(StrictMode mode) & noexcept { + walkNested(true, true, true, [mode](Query &q) noexcept { q.strictMode_ = mode; }); return *this; } - Query &&Strict(StrictMode mode) &&noexcept { return std::move(Strict(mode)); } + [[nodiscard]] Query &&Strict(StrictMode mode) && noexcept { return std::move(Strict(mode)); } + [[nodiscard]] StrictMode GetStrictMode() const noexcept { return strictMode_; } /// Performs sorting by certain column. Same as sql 'ORDER BY'. /// @param sort - sorting column name. /// @param desc - is sorting direction descending or ascending. /// @return Query object. - Query &Sort(std::string sort, bool desc) & { // -V1071 - if (sort.length()) sortingEntries_.emplace_back(std::move(sort), desc); + template > * = nullptr> + Query &Sort(Str &&sort, bool desc) & { // -V1071 + if (!strEmpty(sort)) sortingEntries_.emplace_back(std::forward(sort), desc); return *this; } - Query &&Sort(std::string sort, bool desc) && { return std::move(Sort(std::move(sort), desc)); } + template > * = nullptr> + [[nodiscard]] Query &&Sort(Str &&sort, bool desc) && { + return std::move(Sort(std::forward(sort), desc)); + } /// Performs sorting by ST_Distance() expressions for geometry index. Sorting function will use distance between field and target point. /// @param field - field's name. This field must contain Point. @@ -509,14 +581,16 @@ class Query { /// @param desc - is sorting direction descending or ascending. /// @return Query object. Query &SortStDistance(std::string_view field, reindexer::Point p, bool desc) &; - Query &&SortStDistance(std::string_view field, reindexer::Point p, bool desc) && { return std::move(SortStDistance(field, p, desc)); } + [[nodiscard]] Query &&SortStDistance(std::string_view field, reindexer::Point p, bool desc) && { + return std::move(SortStDistance(field, p, desc)); + } /// Performs sorting by ST_Distance() expressions for geometry index. Sorting function will use distance 2 fields. /// @param field1 - first field name. This field must contain Point. /// @param field2 - second field name.This field must contain Point. /// @param desc - is sorting direction descending or ascending. /// @return Query object. Query &SortStDistance(std::string_view field1, std::string_view field2, bool desc) &; - Query &&SortStDistance(std::string_view field1, std::string_view field2, bool desc) && { + [[nodiscard]] Query &&SortStDistance(std::string_view field1, std::string_view field2, bool desc) && { return std::move(SortStDistance(field1, field2, desc)); } @@ -525,17 +599,17 @@ class Query { /// @param desc - is sorting direction descending or ascending. /// @param forcedSortOrder - list of values for forced sort order. /// @return Query object. - template - Query &Sort(std::string sort, bool desc, std::initializer_list forcedSortOrder) & { + template > * = nullptr> + Query &Sort(Str &&sort, bool desc, std::initializer_list forcedSortOrder) & { if (!sortingEntries_.empty() && !std::empty(forcedSortOrder)) throw Error(errParams, "Forced sort order is allowed for the first sorting entry only"); - sortingEntries_.emplace_back(std::move(sort), desc); + sortingEntries_.emplace_back(std::forward(sort), desc); for (const T &v : forcedSortOrder) forcedSortOrder_.emplace_back(v); return *this; } - template - Query &&Sort(std::string sort, bool desc, std::initializer_list forcedSortOrder) && { - return std::move(Sort(std::move(sort), desc, std::move(forcedSortOrder))); + template > * = nullptr> + [[nodiscard]] Query &&Sort(Str &&sort, bool desc, std::initializer_list forcedSortOrder) && { + return std::move(Sort(std::forward(sort), desc, std::move(forcedSortOrder))); } /// Performs sorting by certain column. Analog to sql ORDER BY. @@ -543,39 +617,59 @@ class Query { /// @param desc - is sorting direction descending or ascending. /// @param forcedSortOrder - list of values for forced sort order. /// @return Query object. - template - Query &Sort(std::string sort, bool desc, const T &forcedSortOrder) & { + template > * = nullptr> + Query &Sort(Str &&sort, bool desc, const T &forcedSortOrder) & { if (!sortingEntries_.empty() && !forcedSortOrder.empty()) throw Error(errParams, "Forced sort order is allowed for the first sorting entry only"); - sortingEntries_.emplace_back(std::move(sort), desc); + sortingEntries_.emplace_back(std::forward(sort), desc); for (const auto &v : forcedSortOrder) forcedSortOrder_.emplace_back(v); return *this; } - template - Query &&Sort(std::string sort, bool desc, const T &forcedSortOrder) && { - return std::move(Sort(std::move(sort), desc, forcedSortOrder)); + template > * = nullptr> + [[nodiscard]] Query &&Sort(Str &&sort, bool desc, const T &forcedSortOrder) && { + return std::move(Sort(std::forward(sort), desc, forcedSortOrder)); } /// Performs distinct for a certain index. /// @param indexName - name of index for distict operation. - Query &Distinct(std::string indexName) & { - if (indexName.length()) { - aggregations_.emplace_back(AggDistinct, h_vector{std::move(indexName)}); + template > * = nullptr> + Query &Distinct(Str &&indexName) & { + if (!strEmpty(indexName)) { + aggregations_.emplace_back(AggDistinct, h_vector{std::forward(indexName)}); } return *this; } - Query &&Distinct(std::string indexName) && { return std::move(Distinct(std::move(indexName))); } + template > * = nullptr> + [[nodiscard]] Query &&Distinct(Str &&indexName) && { + return std::move(Distinct(std::forward(indexName))); + } /// Sets list of columns in this namespace to be finally selected. /// @param l - list of columns to be selected. - Query &Select(std::initializer_list l) & { // -V1071 + template > * = nullptr> + Query &Select(std::initializer_list l) & { + return Select>(std::move(l)); + } + template > * = nullptr> + [[nodiscard]] Query &&Select(std::initializer_list l) && { + return std::move(Select>(std::move(l))); + } + + template + Query &Select(StrCont &&l) & { + using namespace std::string_view_literals; if (!CanAddSelectFilter()) { throw Error(errConflict, kAggregationWithSelectFieldsMsgError); } selectFilter_.insert(selectFilter_.begin(), l.begin(), l.end()); + selectFilter_.erase(std::remove_if(selectFilter_.begin(), selectFilter_.end(), [](const auto &s) { return s == "*"sv; }), + selectFilter_.end()); return *this; } - Query &&Select(std::initializer_list l) && { return std::move(Select(l)); } + template + [[nodiscard]] Query &&Select(StrCont &&l) && { + return std::move(Select(std::forward(l))); + } /// Adds an aggregate function for certain column. /// Analog to sql aggregate functions (min, max, avg, etc). @@ -599,95 +693,111 @@ class Query { aggregations_.emplace_back(type, std::move(fields), std::move(sorting), limit, offset); return *this; } - Query &&Aggregate(AggType type, h_vector fields, const std::vector> &sort = {}, - unsigned limit = QueryEntry::kDefaultLimit, unsigned offset = QueryEntry::kDefaultOffset) && { + [[nodiscard]] Query &&Aggregate(AggType type, h_vector fields, + const std::vector> &sort = {}, unsigned limit = QueryEntry::kDefaultLimit, + unsigned offset = QueryEntry::kDefaultOffset) && { return std::move(Aggregate(type, std::move(fields), sort, limit, offset)); } /// Sets next operation type to Or. /// @return Query object. - Query &Or() & { // -V1071 + Query &Or() & { + assertrx_dbg(nextOp_ == OpAnd); nextOp_ = OpOr; return *this; } - Query &&Or() && { return std::move(Or()); } + [[nodiscard]] Query &&Or() && { return std::move(Or()); } /// Sets next operation type to Not. /// @return Query object. - Query &Not() & { // -V1071 + Query &Not() & { + assertrx_dbg(nextOp_ == OpAnd); nextOp_ = OpNot; return *this; } - Query &&Not() && { return std::move(Not()); } + [[nodiscard]] Query &&Not() && { return std::move(Not()); } + /// Sets next operation type to And. + /// @return Query object. + Query &And() & { + nextOp_ = OpAnd; + return *this; + } + [[nodiscard]] Query &&And() && { return std::move(And()); } + Query &NextOp(OpType op) & { + nextOp_ = op; + return *this; + } + [[nodiscard]] Query &&NextOp(OpType op) && { return std::move(NextOp(op)); } + [[nodiscard]] OpType NextOp() const noexcept { return nextOp_; } /// Insert open bracket to order logic operations. /// @return Query object. - Query &OpenBracket() & { // -V1071 - entries.OpenBracket(nextOp_); + Query &OpenBracket() & { + entries_.OpenBracket(nextOp_); nextOp_ = OpAnd; return *this; } - Query &&OpenBracket() && { return std::move(OpenBracket()); } + [[nodiscard]] Query &&OpenBracket() && { return std::move(OpenBracket()); } /// Insert close bracket to order logic operations. /// @return Query object. - Query &CloseBracket() & { // -V1071 - entries.CloseBracket(); + Query &CloseBracket() & { + entries_.CloseBracket(); return *this; } - Query &&CloseBracket() && { return std::move(CloseBracket()); } + [[nodiscard]] Query &&CloseBracket() && { return std::move(CloseBracket()); } /// Sets the limit of selected rows. /// Analog to sql LIMIT rowsNumber. /// @param limit - number of rows to get from result set. /// @return Query object. - Query &Limit(unsigned limit) &noexcept { // -V1071 - count = limit; + Query &Limit(unsigned limit) & noexcept { + count_ = limit; return *this; } - Query &&Limit(unsigned limit) &&noexcept { return std::move(Limit(limit)); } + [[nodiscard]] Query &&Limit(unsigned limit) && noexcept { return std::move(Limit(limit)); } /// Sets the number of the first selected row from result query. /// Analog to sql LIMIT OFFSET. /// @param offset - index of the first row to get from result set. /// @return Query object. - Query &Offset(unsigned offset) &noexcept { // -V1071 - start = offset; + Query &Offset(unsigned offset) & noexcept { + start_ = offset; return *this; } - Query &&Offset(unsigned offset) &&noexcept { return std::move(Offset(offset)); } + [[nodiscard]] Query &&Offset(unsigned offset) && noexcept { return std::move(Offset(offset)); } /// Set the total count calculation mode to Accurate /// @return Query object - Query &ReqTotal() &noexcept { // -V1071 - calcTotal = ModeAccurateTotal; + Query &ReqTotal() & noexcept { + calcTotal_ = ModeAccurateTotal; return *this; } - Query &&ReqTotal() &&noexcept { return std::move(ReqTotal()); } + [[nodiscard]] Query &&ReqTotal() && noexcept { return std::move(ReqTotal()); } /// Set the total count calculation mode to Cached. /// It will be use LRUCache for total count result /// @return Query object - Query &CachedTotal() &noexcept { // -V1071 - calcTotal = ModeCachedTotal; + Query &CachedTotal() & noexcept { + calcTotal_ = ModeCachedTotal; return *this; } - Query &&CachedTotal() &&noexcept { return std::move(CachedTotal()); } + [[nodiscard]] Query &&CachedTotal() && noexcept { return std::move(CachedTotal()); } /// Output fulltext rank /// Allowed only with fulltext query /// @return Query object - Query &WithRank() &noexcept { // -V1071 + Query &WithRank() & noexcept { withRank_ = true; return *this; } - Query &&WithRank() &&noexcept { return std::move(WithRank()); } - bool IsWithRank() const noexcept { return withRank_; } + [[nodiscard]] Query &&WithRank() && noexcept { return std::move(WithRank()); } + [[nodiscard]] bool IsWithRank() const noexcept { return withRank_; } /// Can we add aggregation functions /// or new select fields to a current query? - bool CanAddAggregation(AggType type) const noexcept { return type == AggDistinct || (selectFilter_.empty()); } - bool CanAddSelectFilter() const noexcept { + [[nodiscard]] bool CanAddAggregation(AggType type) const noexcept { return type == AggDistinct || (selectFilter_.empty()); } + [[nodiscard]] bool CanAddSelectFilter() const noexcept { return aggregations_.empty() || (aggregations_.size() == 1 && aggregations_.front().Type() == AggDistinct); } @@ -698,73 +808,203 @@ class Query { /// Deserializes query data from stream. /// @param ser - serializer object. - void Deserialize(Serializer &ser); + [[nodiscard]] static Query Deserialize(Serializer &ser); + + void WalkNested(bool withSelf, bool withMerged, bool withSubQueries, const std::function &visitor) const + noexcept(noexcept(visitor(std::declval()))); + + [[nodiscard]] bool HasLimit() const noexcept { return count_ != QueryEntry::kDefaultLimit; } + [[nodiscard]] bool HasOffset() const noexcept { return start_ != QueryEntry::kDefaultOffset; } + [[nodiscard]] bool IsWALQuery() const noexcept; + [[nodiscard]] const std::vector &UpdateFields() const noexcept { return updateFields_; } + [[nodiscard]] QueryType Type() const noexcept { return type_; } + [[nodiscard]] const std::string &NsName() const & noexcept { return namespace_; } + [[nodiscard]] bool IsLocal() const noexcept { return local_; } + template + void SetNsName(T &&nsName) & noexcept { + namespace_ = std::forward(nsName); + } + [[nodiscard]] unsigned Limit() const noexcept { return count_; } + [[nodiscard]] unsigned Offset() const noexcept { return start_; } + [[nodiscard]] CalcTotalMode CalcTotal() const noexcept { return calcTotal_; } + [[nodiscard]] bool HasCalcTotal() const noexcept { return calcTotal_ != ModeNoTotal; } + void CalcTotal(CalcTotalMode calcTotal) noexcept { calcTotal_ = calcTotal; } - void WalkNested(bool withSelf, bool withMerged, const std::function &visitor) const; + QueryType type_ = QuerySelect; /// Query type + SortingEntries sortingEntries_; /// Sorting data. + std::vector forcedSortOrder_; /// Keys that always go first - before any ordered values. + std::vector selectFunctions_; /// List of sql functions - bool HasLimit() const noexcept { return count != QueryEntry::kDefaultLimit; } - bool HasOffset() const noexcept { return start != QueryEntry::kDefaultOffset; } - bool IsWALQuery() const noexcept; - const std::vector &UpdateFields() const noexcept { return updateFields_; } + std::vector aggregations_; - QueryType Type() const noexcept { return type_; } - const std::string &Namespace() const &noexcept { return _namespace; } + auto NsName() const && = delete; + [[nodiscard]] const QueryEntries &Entries() const noexcept { return entries_; } + template + [[nodiscard]] T &GetUpdatableEntry(size_t i) & noexcept { + return entries_.Get(i); + } + template + void SetEntry(size_t i, Args &&...args) { + entries_.SetValue(i, T{std::forward(args)...}); + } + void UpdateField(UpdateEntry &&ue) & { updateFields_.emplace_back(std::move(ue)); } + void SetEqualPositions(EqualPosition_t &&ep) & { entries_.equalPositions.emplace_back(std::move(ep)); } + void SetEqualPositions(size_t bracketPosition, EqualPosition_t &&ep) & { + entries_.Get(bracketPosition).equalPositions.emplace_back(std::move(ep)); + } + void Join(JoinedQuery &&) &; + void ReserveQueryEntries(size_t s) & { entries_.Reserve(s); } + template + Query &AppendQueryEntry(OpType op, Args &&...args) & { + entries_.Append(op, std::forward(args)...); + return *this; + } + template + Query &&AppendQueryEntry(OpType op, Args &&...args) && { + entries_.Append(op, std::forward(args)...); + return std::move(*this); + } + void SetLastOperation(OpType op) & { entries_.SetLastOperation(op); } + [[nodiscard]] const Query &GetSubQuery(size_t i) const & noexcept { return subQueries_.at(i); } + [[nodiscard]] const auto &GetSubQueries() const & noexcept { return subQueries_; } + [[nodiscard]] const auto &GetJoinQueries() const & noexcept { return joinQueries_; } + [[nodiscard]] const auto &GetMergeQueries() const & noexcept { return mergeQueries_; } + [[nodiscard]] const auto &SelectFilters() const & noexcept { return selectFilter_; } + void AddJoinQuery(JoinedQuery &&); + void VerifyForUpdate() const; + + auto GetSubQuery(size_t) const && = delete; + auto GetSubQueries() const && = delete; + auto GetJoinQueries() const && = delete; + auto GetMergeQueries() const && = delete; + auto SelectFilters() const && = delete; -protected: - void deserialize(Serializer &ser, bool &hasJoinConditions); +private: + class [[nodiscard]] PopBackQEGuard { + public: + explicit PopBackQEGuard(QueryEntries *e) noexcept : e_{e} {} + ~PopBackQEGuard() { + if (e_) e_->PopBack(); + } + void Reset() noexcept { e_ = nullptr; } -public: - std::string _namespace; /// Name of the namespace. - unsigned start = QueryEntry::kDefaultOffset; /// First row index from result set. - unsigned count = QueryEntry::kDefaultLimit; /// Number of rows from result set. - int debugLevel = 0; /// Debug level. - StrictMode strictMode = StrictModeNotSet; /// Strict mode. - bool explain_ = false; /// Explain query if true - bool local_ = false; /// Local query if true - CalcTotalMode calcTotal = ModeNoTotal; /// Calculation mode. - QueryType type_ = QuerySelect; /// Query type - OpType nextOp_ = OpAnd; /// Next operation constant. - SortingEntries sortingEntries_; /// Sorting data. - std::vector forcedSortOrder_; /// Keys that always go first - before any ordered values. - std::vector joinQueries_; /// List of queries for join. - std::vector mergeQueries_; /// List of merge queries. - h_vector selectFilter_; /// List of columns in a final result set. - std::vector selectFunctions_; /// List of sql functions - - QueryEntries entries; + private: + QueryEntries *e_; + }; - std::vector aggregations_; + template + class [[nodiscard]] OnHelperTempl; + template + class [[nodiscard]] OnHelperGroup { + public: + [[nodiscard]] OnHelperGroup &&Not() && noexcept { + op_ = OpNot; + return std::move(*this); + } + [[nodiscard]] OnHelperGroup &&Or() && noexcept { + op_ = OpOr; + return std::move(*this); + } + [[nodiscard]] OnHelperGroup &&On(std::string index, CondType cond, std::string joinIndex) &&; + [[nodiscard]] Q CloseBracket() && noexcept { return std::forward(q_); } -private: + private: + OnHelperGroup(Q q, JoinedQuery &jq) noexcept : q_{std::forward(q)}, jq_{jq} {} + Q q_; + JoinedQuery &jq_; + OpType op_{OpAnd}; + friend class OnHelperTempl; + }; + template + class [[nodiscard]] OnHelperTempl { + public: + [[nodiscard]] OnHelperTempl &&Not() && noexcept { + op_ = OpNot; + return std::move(*this); + } + [[nodiscard]] Q On(std::string index, CondType cond, std::string joinIndex) &&; + [[nodiscard]] OnHelperGroup OpenBracket() && noexcept { return {std::forward(q_), jq_}; } + + private: + OnHelperTempl(Q q, JoinedQuery &jq) noexcept : q_{std::forward(q)}, jq_{jq} {} + Q q_; + JoinedQuery &jq_; + OpType op_{OpAnd}; + friend class Query; + }; + using OnHelper = OnHelperTempl; + using OnHelperR = OnHelperTempl; + + void deserialize(Serializer &ser, bool &hasJoinConditions); + VariantArray deserializeValues(Serializer &, CondType); + void checkSubQueryNoData() const; + void checkSubQueryWithData() const; + void checkSubQuery() const; + void walkNested(bool withSelf, bool withMerged, bool withSubQueries, + const std::function &visitor) noexcept(noexcept(visitor(std::declval()))); + void adoptNested(Query &nq) const noexcept { nq.Strict(GetStrictMode()).Explain(GetExplain()).Debug(GetDebugLevel()); } + + std::string namespace_; /// Name of the namespace. + unsigned start_ = QueryEntry::kDefaultOffset; /// First row index from result set. + unsigned count_ = QueryEntry::kDefaultLimit; /// Number of rows from result set. + CalcTotalMode calcTotal_ = ModeNoTotal; /// Calculation mode. + QueryEntries entries_; std::vector updateFields_; /// List of fields (and values) for update. + std::vector joinQueries_; /// List of queries for join. + std::vector mergeQueries_; /// List of merge queries. + std::vector subQueries_; + h_vector selectFilter_; /// List of columns in a final result set. + bool local_ = false; /// Local query if true bool withRank_ = false; - friend class SQLParser; + StrictMode strictMode_ = StrictModeNotSet; /// Strict mode. + int debugLevel_ = 0; /// Debug level. + bool explain_ = false; /// Explain query if true + OpType nextOp_ = OpAnd; /// Next operation constant. }; class JoinedQuery : public Query { public: - JoinedQuery() = default; JoinedQuery(JoinType jt, const Query &q) : Query(q), joinType{jt} {} JoinedQuery(JoinType jt, Query &&q) : Query(std::move(q)), joinType{jt} {} using Query::Query; - bool operator==(const JoinedQuery &obj) const; - const std::string &RightNsName() const noexcept { return _namespace; } + [[nodiscard]] bool operator==(const JoinedQuery &obj) const; + const std::string &RightNsName() const noexcept { return NsName(); } JoinType joinType{JoinType::LeftJoin}; /// Default join type. h_vector joinEntries_; /// Condition for join. Filled in each subqueries, empty in root query }; template -Q Query::OnHelperTempl::On(std::string index, CondType cond, std::string joinIndex) && { +[[nodiscard]] Q Query::OnHelperTempl::On(std::string index, CondType cond, std::string joinIndex) && { jq_.joinEntries_.emplace_back(op_, cond, std::move(index), std::move(joinIndex)); return std::forward(q_); } template -Query::OnHelperGroup &&Query::OnHelperGroup::On(std::string index, CondType cond, std::string joinIndex) && { +[[nodiscard]] Query::OnHelperGroup &&Query::OnHelperGroup::On(std::string index, CondType cond, std::string joinIndex) && { jq_.joinEntries_.emplace_back(op_, cond, std::move(index), std::move(joinIndex)); op_ = OpAnd; return std::move(*this); } +[[nodiscard]] inline auto Query::Join(JoinType joinType, Query &&q) & { + Join({joinType, std::move(q)}); + return OnHelper{*this, joinQueries_.back()}; +} + +[[nodiscard]] inline auto Query::Join(JoinType joinType, const Query &q) & { + Join({joinType, q}); + return OnHelper{*this, joinQueries_.back()}; +} + +[[nodiscard]] inline auto Query::Join(JoinType joinType, Query &&q) && { + Join({joinType, std::move(q)}); + return OnHelperR{std::move(*this), joinQueries_.back()}; +} + +[[nodiscard]] inline auto Query::Join(JoinType joinType, const Query &q) && { + Join({joinType, q}); + return OnHelperR{std::move(*this), joinQueries_.back()}; +} + } // namespace reindexer diff --git a/cpp_src/core/query/queryentry.cc b/cpp_src/core/query/queryentry.cc index a7cc7f4e3..74b62dfab 100644 --- a/cpp_src/core/query/queryentry.cc +++ b/cpp_src/core/query/queryentry.cc @@ -8,7 +8,6 @@ #include "query.h" #include "tools/serializer.h" #include "tools/string_regexp_functions.h" -#include "tools/stringstools.h" namespace reindexer { @@ -21,11 +20,11 @@ std::string JoinQueryEntry::Dump(const std::vector &joinedSelectors) const { ser << '('; for (const auto &jqe : q.joinEntries_) { if (&jqe != &q.joinEntries_.front()) { - ser << ' ' << jqe.op_ << ' '; + ser << ' ' << jqe.Operation() << ' '; } else { - assertrx(jqe.op_ == OpAnd); + assertrx(jqe.Operation() == OpAnd); } - ser << q._namespace << '.' << jqe.joinIndex_ << ' ' << InvertJoinCondition(jqe.condition_) << ' ' << jqe.index_; + ser << q.NsName() << '.' << jqe.RightFieldName() << ' ' << InvertJoinCondition(jqe.Condition()) << ' ' << jqe.LeftFieldName(); } ser << ')'; return std::string{ser.Slice()}; @@ -41,30 +40,133 @@ std::string JoinQueryEntry::DumpOnCondition(const std::vector &joinedSelecto ser << js.Type() << " ON ("; for (const auto &jqe : q.joinEntries_) { if (&jqe != &q.joinEntries_.front()) { - ser << ' ' << jqe.op_ << ' '; + ser << ' ' << jqe.Operation() << ' '; } - ser << q._namespace << '.' << jqe.joinIndex_ << ' ' << InvertJoinCondition(jqe.condition_) << ' ' << jqe.index_; + ser << q.NsName() << '.' << jqe.RightFieldName() << ' ' << InvertJoinCondition(jqe.Condition()) << ' ' << jqe.LeftFieldName(); } ser << ')'; return std::string{ser.Slice()}; } template std::string JoinQueryEntry::DumpOnCondition(const JoinedSelectors &) const; -bool QueryEntry::operator==(const QueryEntry &obj) const { - return condition == obj.condition && index == obj.index && idxNo == obj.idxNo && distinct == obj.distinct && - values.RelaxCompare(obj.values) == 0; +bool QueryField::operator==(const QueryField &other) const noexcept { + if (fieldName_ != other.fieldName_ || idxNo_ != other.idxNo_ || fieldsSet_ != other.fieldsSet_ || + !fieldType_.IsSame(other.fieldType_) || !selectType_.IsSame(other.selectType_) || + compositeFieldsTypes_.size() != other.compositeFieldsTypes_.size()) { + return false; + } + for (size_t i = 0, s = compositeFieldsTypes_.size(); i < s; ++i) { + if (!compositeFieldsTypes_[i].IsSame(other.compositeFieldsTypes_[i])) { + return false; + } + } + return true; +} + +void QueryField::SetField(FieldsSet &&fields) & { + assertrx_throw(fields.size() == 1); + assertrx_throw(fields[0] == IndexValueType::SetByJsonPath); + assertrx_throw(idxNo_ == IndexValueType::NotSet); + idxNo_ = IndexValueType::SetByJsonPath; + fieldsSet_ = std::move(fields); +} + +static void checkIndexData([[maybe_unused]] int idxNo, [[maybe_unused]] const FieldsSet &fields, KeyValueType fieldType, + [[maybe_unused]] const std::vector &compositeFieldsTypes) { + assertrx_throw(idxNo >= 0); + if (fieldType.Is()) { + assertrx_throw(fields.size() == compositeFieldsTypes.size()); + } else { + assertrx_throw(fields.size() == 1); + assertrx_throw(compositeFieldsTypes.empty()); + } +} + +void QueryField::SetIndexData(int idxNo, FieldsSet &&fields, KeyValueType fieldType, KeyValueType selectType, + std::vector &&compositeFieldsTypes) & { + checkIndexData(idxNo, fields, fieldType, compositeFieldsTypes); + idxNo_ = idxNo; + fieldsSet_ = std::move(fields); + fieldType_ = fieldType; + selectType_ = selectType; + compositeFieldsTypes_ = std::move(compositeFieldsTypes); } +bool QueryField::HaveEmptyField() const noexcept { + size_t tagsNo = 0; + for (auto f : Fields()) { + if (f == IndexValueType::SetByJsonPath) { + if (Fields().getTagsPath(tagsNo).empty()) { + return true; + } + ++tagsNo; + } + } + return Fields().empty(); +} + +bool QueryEntry::operator==(const QueryEntry &other) const noexcept { + return QueryField::operator==(other) && condition_ == other.condition_ && distinct_ == other.distinct_ && + values_.RelaxCompare(other.values_) == 0; +} + +template +void VerifyQueryEntryValues(CondType cond, const VariantArray &values) { + if constexpr (flags & VerifyQueryEntryFlags::ignoreEmptyValues) { + if (values.empty()) { + return; + } + } + const auto checkArgsCount = [&](size_t argsCountReq) { + if (values.size() != argsCountReq) { + throw Error{errLogic, "Condition %s must have exact %d argument, but %d arguments was provided", CondTypeToStr(cond), + argsCountReq, values.size()}; + } + }; + switch (cond) { + case CondEq: + case CondSet: + case CondAllSet: + break; + case CondAny: + case CondEmpty: + if (!values.empty() && !(values.size() == 1 && values[0].Type().Is())) { + throw Error{errLogic, "Condition %s must have no argument or single null argument, but %d not null arguments was provided", + CondTypeToStr(cond), values.size()}; + } + break; + case CondGe: + case CondGt: + case CondLt: + case CondLe: + checkArgsCount(1); + break; + case CondLike: + checkArgsCount(1); + if (!values[0].Type().Is()) { + throw Error{errLogic, "Condition %s must have string argument, but %d argument was provided", CondTypeToStr(cond), + values[0].Type().Name()}; + } + break; + case CondRange: + case CondDWithin: + checkArgsCount(2); + break; + } +} +template void VerifyQueryEntryValues(CondType, const VariantArray &); +template void VerifyQueryEntryValues(CondType, const VariantArray &); + std::string QueryEntry::Dump() const { WrSerializer ser; - if (distinct) { - ser << "Distinct index: " << index; + if (Distinct()) { + ser << "Distinct index: " << FieldName(); } else { - ser << index << ' ' << condition << ' '; - const bool severalValues = (values.size() > 1); + ser << FieldName() << ' ' << condition_ << ' '; + const bool severalValues = (Values().size() > 1); if (severalValues) ser << '('; - for (auto &v : values) { - if (&v != &*values.begin()) ser << ','; + for (auto &v : Values()) { + if (&v != &*Values().begin()) ser << ','; ser << '\'' << v.As() << '\''; } if (severalValues) ser << ')'; @@ -75,18 +177,18 @@ std::string QueryEntry::Dump() const { std::string QueryEntry::DumpBrief() const { WrSerializer ser; { - ser << index << ' ' << condition << ' '; - const bool severalValues = (values.size() > 1); + ser << FieldName() << ' ' << Condition() << ' '; + const bool severalValues = (Values().size() > 1); if (severalValues) { ser << "(...)"; } else { - ser << '\'' << values.front().As() << '\''; + ser << '\'' << Values().front().As() << '\''; } } return std::string(ser.Slice()); } -AggregateEntry::AggregateEntry(AggType type, h_vector fields, SortingEntries sort, unsigned limit, unsigned offset) +AggregateEntry::AggregateEntry(AggType type, h_vector &&fields, SortingEntries &&sort, unsigned limit, unsigned offset) : type_(type), fields_(std::move(fields)), sortingEntries_{std::move(sort)}, limit_(limit), offset_(offset) { switch (type_) { case AggFacet: @@ -134,7 +236,7 @@ AggregateEntry::AggregateEntry(AggType type, h_vector fields, So } } -void AggregateEntry::AddSortingEntry(SortingEntry sorting) { +void AggregateEntry::AddSortingEntry(SortingEntry &&sorting) { if (type_ != AggFacet) { throw Error(errQueryExec, "Sort is not available for aggregation %s", AggTypeToStr(type_)); } @@ -155,60 +257,73 @@ void AggregateEntry::SetOffset(unsigned o) { offset_ = o; } -BetweenFieldsQueryEntry::BetweenFieldsQueryEntry(std::string fstIdx, CondType cond, std::string sndIdx) - : firstIndex{std::move(fstIdx)}, secondIndex{std::move(sndIdx)}, condition_{cond} { - if (condition_ == CondAny || condition_ == CondEmpty || condition_ == CondDWithin) { - throw Error{errLogic, "Condition '%s' is inapplicable between two fields", std::string{CondTypeToStr(condition_)}}; - } -} - bool BetweenFieldsQueryEntry::operator==(const BetweenFieldsQueryEntry &other) const noexcept { - return firstIdxNo == other.firstIdxNo && secondIdxNo == other.secondIdxNo && Condition() == other.Condition() && - firstIndex == other.firstIndex && secondIndex == other.secondIndex; + return leftField_ == other.leftField_ && rightField_ == other.rightField_ && Condition() == other.Condition(); } std::string BetweenFieldsQueryEntry::Dump() const { WrSerializer ser; - ser << firstIndex << ' ' << Condition() << ' ' << secondIndex; + ser << LeftFieldName() << ' ' << Condition() << ' ' << RightFieldName(); return std::string{ser.Slice()}; } -void QueryEntries::serialize(const_iterator it, const_iterator to, WrSerializer &ser) { +void QueryEntries::serialize(CondType cond, const VariantArray &values, WrSerializer &ser) { + ser.PutVarUint(cond); + if (cond == CondDWithin) { + assertrx_throw(values.size() == 2); + ser.PutVarUint(3); + if (values[0].Type().Is()) { + const Point point = static_cast(values[0]); + ser.PutDouble(point.X()); + ser.PutDouble(point.Y()); + ser.PutVariant(values[1]); + } else { + const Point point = static_cast(values[1]); + ser.PutDouble(point.X()); + ser.PutDouble(point.Y()); + ser.PutVariant(values[0]); + } + } else { + ser.PutVarUint(values.size()); + for (auto &kv : values) ser.PutVariant(kv); + } +} + +void QueryEntries::serialize(const_iterator it, const_iterator to, WrSerializer &ser, const std::vector &subQueries) { for (; it != to; ++it) { const OpType op = it->operation; it->InvokeAppropriate( - [&ser, op, &it](const QueryEntriesBracket &) { + [&ser, op, &subQueries](const SubQueryEntry &sqe) { + ser.PutVarUint(QuerySubQueryCondition); + ser.PutVarUint(op); + { + const auto sizePosSaver = ser.StartVString(); + subQueries.at(sqe.QueryIndex()).Serialize(ser); + } + serialize(sqe.Condition(), sqe.Values(), ser); + }, + [&ser, op, &subQueries](const SubQueryFieldEntry &sqe) { + ser.PutVarUint(QueryFieldSubQueryCondition); + ser.PutVarUint(op); + ser.PutVString(sqe.FieldName()); + ser.PutVarUint(sqe.Condition()); + { + const auto sizePosSaver = ser.StartVString(); + subQueries.at(sqe.QueryIndex()).Serialize(ser); + } + }, + [&](const QueryEntriesBracket &) { ser.PutVarUint(QueryOpenBracket); ser.PutVarUint(op); - serialize(it.cbegin(), it.cend(), ser); + serialize(it.cbegin(), it.cend(), ser, subQueries); ser.PutVarUint(QueryCloseBracket); }, [&ser, op](const QueryEntry &entry) { - entry.distinct ? ser.PutVarUint(QueryDistinct) : ser.PutVarUint(QueryCondition); - ser.PutVString(entry.index); - if (entry.distinct) return; + entry.Distinct() ? ser.PutVarUint(QueryDistinct) : ser.PutVarUint(QueryCondition); + ser.PutVString(entry.FieldName()); + if (entry.Distinct()) return; ser.PutVarUint(op); - ser.PutVarUint(entry.condition); - if (entry.condition == CondDWithin) { - if (entry.values.size() != 2) { - throw Error(errLogic, "Condition DWithin must have exact 2 value, but %d values was provided", entry.values.size()); - } - ser.PutVarUint(3); - if (entry.values[0].Type().Is()) { - const Point point = static_cast(entry.values[0]); - ser.PutDouble(point.X()); - ser.PutDouble(point.Y()); - ser.PutVariant(entry.values[1]); - } else { - const Point point = static_cast(entry.values[1]); - ser.PutDouble(point.X()); - ser.PutDouble(point.Y()); - ser.PutVariant(entry.values[0]); - } - } else { - ser.PutVarUint(entry.values.size()); - for (auto &kv : entry.values) ser.PutVariant(kv); - } + serialize(entry.Condition(), entry.Values(), ser); }, [&ser, op](const JoinQueryEntry &jqe) { ser.PutVarUint(QueryJoinCondition); @@ -218,13 +333,17 @@ void QueryEntries::serialize(const_iterator it, const_iterator to, WrSerializer [&ser, op](const BetweenFieldsQueryEntry &entry) { ser.PutVarUint(QueryBetweenFieldsCondition); ser.PutVarUint(op); - ser.PutVString(entry.firstIndex); + ser.PutVString(entry.LeftFieldName()); ser.PutVarUint(entry.Condition()); - ser.PutVString(entry.secondIndex); + ser.PutVString(entry.RightFieldName()); }, [&ser, op](const AlwaysFalse &) { ser.PutVarUint(QueryAlwaysFalseCondition); ser.PutVarUint(op); + }, + [&ser, op](const AlwaysTrue &) { + ser.PutVarUint(QueryAlwaysTrueCondition); + ser.PutVarUint(op); }); } } @@ -233,13 +352,8 @@ bool UpdateEntry::operator==(const UpdateEntry &obj) const noexcept { return isExpression_ == obj.isExpression_ && column_ == obj.column_ && mode_ == obj.mode_ && values_ == obj.values_; } -bool QueryJoinEntry::operator==(const QueryJoinEntry &obj) const noexcept { - if (op_ != obj.op_) return false; - if (static_cast(condition_) != obj.condition_) return false; - if (index_ != obj.index_) return false; - if (joinIndex_ != obj.joinIndex_) return false; - if (idxNo != obj.idxNo) return false; - return true; +bool QueryJoinEntry::operator==(const QueryJoinEntry &other) const noexcept { + return op_ == other.op_ && condition_ == other.condition_ && leftField_ == other.leftField_ && rightField_ == other.rightField_; } bool AggregateEntry::operator==(const AggregateEntry &obj) const noexcept { @@ -254,7 +368,7 @@ bool SortingEntry::operator==(const SortingEntry &obj) const noexcept { return true; } -bool QueryEntries::checkIfSatisfyConditions(const_iterator begin, const_iterator end, const ConstPayload &pl, TagsMatcher &tagsMatcher) { +bool QueryEntries::checkIfSatisfyConditions(const_iterator begin, const_iterator end, const ConstPayload &pl) { assertrx(begin != end && begin->operation != OpOr); bool result = true; for (auto it = begin; it != end; ++it) { @@ -264,44 +378,39 @@ bool QueryEntries::checkIfSatisfyConditions(const_iterator begin, const_iterator break; } const bool lastResult = it->InvokeAppropriate( - [&it, &pl, &tagsMatcher](const QueryEntriesBracket &) { - return checkIfSatisfyConditions(it.cbegin(), it.cend(), pl, tagsMatcher); + [](const SubQueryEntry &) -> bool { + assertrx_throw(0); + abort(); + }, + [](const SubQueryFieldEntry &) -> bool { + assertrx_throw(0); + abort(); }, - [&pl, &tagsMatcher](const QueryEntry &qe) { return checkIfSatisfyCondition(qe, pl, tagsMatcher); }, - [&pl, &tagsMatcher](const BetweenFieldsQueryEntry &qe) { return checkIfSatisfyCondition(qe, pl, tagsMatcher); }, - [](const JoinQueryEntry &) -> bool { abort(); }, [](const AlwaysFalse &) { return false; }); + [&it, &pl](const QueryEntriesBracket &) { return checkIfSatisfyConditions(it.cbegin(), it.cend(), pl); }, + [&pl](const QueryEntry &qe) { return checkIfSatisfyCondition(qe, pl); }, + [&pl](const BetweenFieldsQueryEntry &qe) { return checkIfSatisfyCondition(qe, pl); }, + [](const JoinQueryEntry &) -> bool { abort(); }, [](const AlwaysFalse &) noexcept { return false; }, + [](const AlwaysTrue &) noexcept { return true; }); result = (lastResult != (it->operation == OpNot)); } return result; } -bool QueryEntries::checkIfSatisfyCondition(const QueryEntry &qEntry, const ConstPayload &pl, TagsMatcher &tagsMatcher) { +bool QueryEntries::checkIfSatisfyCondition(const QueryEntry &qEntry, const ConstPayload &pl) { VariantArray values; - if (qEntry.idxNo == IndexValueType::SetByJsonPath) { - pl.GetByJsonPath(qEntry.index, tagsMatcher, values, KeyValueType::Undefined{}); - } else { - pl.Get(qEntry.idxNo, values); - } - return checkIfSatisfyCondition(values, qEntry.condition, qEntry.values); + pl.GetByFieldsSet(qEntry.Fields(), values, qEntry.FieldType(), qEntry.CompositeFieldsTypes()); + return CheckIfSatisfyCondition(values, qEntry.Condition(), qEntry.Values()); } -bool QueryEntries::checkIfSatisfyCondition(const BetweenFieldsQueryEntry &qEntry, const ConstPayload &pl, TagsMatcher &tagsMatcher) { +bool QueryEntries::checkIfSatisfyCondition(const BetweenFieldsQueryEntry &qEntry, const ConstPayload &pl) { VariantArray lValues; - if (qEntry.firstIdxNo == IndexValueType::SetByJsonPath) { - pl.GetByJsonPath(qEntry.firstIndex, tagsMatcher, lValues, KeyValueType::Undefined{}); - } else { - pl.Get(qEntry.firstIdxNo, lValues); - } + pl.GetByFieldsSet(qEntry.LeftFields(), lValues, qEntry.LeftFieldType(), qEntry.LeftCompositeFieldsTypes()); VariantArray rValues; - if (qEntry.secondIdxNo == IndexValueType::SetByJsonPath) { - pl.GetByJsonPath(qEntry.secondIndex, tagsMatcher, rValues, KeyValueType::Undefined{}); - } else { - pl.Get(qEntry.secondIdxNo, rValues); - } - return checkIfSatisfyCondition(lValues, qEntry.Condition(), rValues); + pl.GetByFieldsSet(qEntry.RightFields(), rValues, qEntry.RightFieldType(), qEntry.RightCompositeFieldsTypes()); + return CheckIfSatisfyCondition(lValues, qEntry.Condition(), rValues); } -bool QueryEntries::checkIfSatisfyCondition(const VariantArray &lValues, CondType condition, const VariantArray &rValues) { +bool QueryEntries::CheckIfSatisfyCondition(const VariantArray &lValues, CondType condition, const VariantArray &rValues) { switch (condition) { case CondType::CondAny: return !lValues.empty(); @@ -326,42 +435,39 @@ bool QueryEntries::checkIfSatisfyCondition(const VariantArray &lValues, CondType } return true; case CondType::CondLt: - case CondType::CondLe: { - auto lit = lValues.cbegin(); - auto rit = rValues.cbegin(); - for (; lit != lValues.cend() && rit != rValues.cend(); ++lit, ++rit) { - const int res = lit->RelaxCompare(*rit); - if (res < 0) return true; - if (res > 0) return false; + for (const auto &lhs : lValues) { + for (const auto &rhs : rValues) { + if (lhs.RelaxCompare(rhs) < 0) return true; + } + } + return false; + case CondType::CondLe: + for (const auto &lhs : lValues) { + for (const auto &rhs : rValues) { + if (lhs.RelaxCompare(rhs) <= 0) return true; + } } - if (lit == lValues.cend() && ((rit == rValues.cend()) == (condition == CondType::CondLe))) return true; return false; - } case CondType::CondGt: - case CondType::CondGe: { - auto lit = lValues.cbegin(); - auto rit = rValues.cbegin(); - for (; lit != lValues.cend() && rit != rValues.cend(); ++lit, ++rit) { - const int res = lit->RelaxCompare(*rit); - if (res > 0) return true; - if (res < 0) return false; + for (const auto &lhs : lValues) { + for (const auto &rhs : rValues) { + if (lhs.RelaxCompare(rhs) > 0) return true; + } + } + return false; + case CondType::CondGe: + for (const auto &lhs : lValues) { + for (const auto &rhs : rValues) { + if (lhs.RelaxCompare(rhs) >= 0) return true; + } } - if (rit == rValues.cend() && ((lit == lValues.cend()) == (condition == CondType::CondGe))) return true; return false; - } case CondType::CondRange: - if (rValues.size() != 2) throw Error(errParams, "For ranged query reuqired 2 arguments, but provided %d", rValues.size()); for (const auto &v : lValues) { if (v.RelaxCompare(rValues[0]) < 0 || v.RelaxCompare(rValues[1]) > 0) return false; } return true; case CondType::CondLike: - if (rValues.size() != 1) { - throw Error(errLogic, "Condition LIKE must have exact 1 value, but %d values was provided", rValues.size()); - } - if (!rValues[0].Type().Is()) { - throw Error(errLogic, "Condition LIKE must have value of string type, but %s value was provided", rValues[0].Type().Name()); - } for (const auto &v : lValues) { if (!v.Type().Is()) { throw Error(errLogic, "Condition LIKE must be applied to data of string type, but %s was provided", v.Type().Name()); @@ -370,9 +476,6 @@ bool QueryEntries::checkIfSatisfyCondition(const VariantArray &lValues, CondType } return false; case CondType::CondDWithin: { - if (rValues.size() != 2) { - throw Error(errLogic, "Condition DWithin must have exact 2 value, but %d values was provided", rValues.size()); - } Point point; double distance; if (rValues[0].Type().Is()) { @@ -390,4 +493,77 @@ bool QueryEntries::checkIfSatisfyCondition(const VariantArray &lValues, CondType return false; } +template +std::string QueryJoinEntry::DumpCondition(const JS &joinedSelector, bool needOp) const { + WrSerializer ser; + const auto &q = joinedSelector.JoinQuery(); + if (needOp) { + ser << ' ' << op_ << ' '; + } + ser << q.NsName() << '.' << RightFieldName() << ' ' << InvertJoinCondition(condition_) << ' ' << LeftFieldName(); + return std::string{ser.Slice()}; +} +template std::string QueryJoinEntry::DumpCondition(const JoinedSelector &, bool) const; + +void QueryEntries::dumpEqualPositions(size_t level, WrSerializer &ser, const EqualPositions_t &equalPositions) { + for (const auto &eq : equalPositions) { + for (size_t i = 0; i < level; ++i) { + ser << " "; + } + ser << "equal_poisition("; + for (size_t i = 0, s = eq.size(); i < s; ++i) { + if (i != 0) ser << ", "; + ser << eq[i]; + } + ser << ")\n"; + } +} + +std::string SubQueryEntry::Dump(const std::vector &subQueries) const { + std::stringstream ss; + ss << '(' << subQueries.at(QueryIndex()).GetSQL() << ") " << Condition() << ' '; + if (Values().size() > 1) ss << '['; + for (size_t i = 0, s = Values().size(); i != s; ++i) { + if (i != 0) ss << ','; + ss << '\'' << Values()[i].As() << '\''; + } + if (Values().size() > 1) ss << ']'; + return ss.str(); +} + +std::string SubQueryFieldEntry::Dump(const std::vector &subQueries) const { + std::stringstream ss; + ss << FieldName() << ' ' << Condition() << " (" << subQueries.at(QueryIndex()).GetSQL() << ')'; + return ss.str(); +} + +template +void QueryEntries::dump(size_t level, const_iterator begin, const_iterator end, const std::vector &joinedSelectors, + const std::vector &subQueries, WrSerializer &ser) { + for (const_iterator it = begin; it != end; ++it) { + for (size_t i = 0; i < level; ++i) { + ser << " "; + } + if (it != begin || it->operation != OpAnd) { + ser << it->operation << ' '; + } + it->InvokeAppropriate([&ser, subQueries](const SubQueryEntry &sqe) { ser << sqe.Dump(subQueries); }, + [&ser, subQueries](const SubQueryFieldEntry &sqe) { ser << sqe.Dump(subQueries); }, + [&](const QueryEntriesBracket &b) { + ser << "(\n"; + dump(level + 1, it.cbegin(), it.cend(), joinedSelectors, ser); + dumpEqualPositions(level + 1, ser, b.equalPositions); + for (size_t i = 0; i < level; ++i) { + ser << " "; + } + ser << ")\n"; + }, + [&ser](const QueryEntry &qe) { ser << qe.Dump() << '\n'; }, + [&joinedSelectors, &ser](const JoinQueryEntry &jqe) { ser << jqe.Dump(joinedSelectors) << '\n'; }, + [&ser](const BetweenFieldsQueryEntry &qe) { ser << qe.Dump() << '\n'; }, + [&ser](const AlwaysFalse &) { ser << "AlwaysFalse" << 'n'; }, + [&ser](const AlwaysTrue &) { ser << "AlwaysTrue" << 'n'; }); + } +} + } // namespace reindexer diff --git a/cpp_src/core/query/queryentry.h b/cpp_src/core/query/queryentry.h index d2b53ebf8..4fba5cc57 100644 --- a/cpp_src/core/query/queryentry.h +++ b/cpp_src/core/query/queryentry.h @@ -5,10 +5,11 @@ #include #include "core/expressiontree.h" #include "core/keyvalue/variant.h" +#include "core/payload/fieldsset.h" #include "core/type_consts.h" -#include "core/type_consts_helpers.h" #include "estl/h_vector.h" #include "tools/serializer.h" +#include "tools/verifying_updater.h" namespace reindexer { @@ -31,49 +32,193 @@ struct JoinQueryEntry { std::string DumpOnCondition(const std::vector &joinedSelectors) const; }; -struct QueryEntry { +class QueryField { +public: + template + explicit QueryField(Str &&fieldName) noexcept : fieldName_{std::forward(fieldName)} {} + QueryField(std::string &&fieldName, int idxNo, FieldsSet fields, KeyValueType fieldType, + std::vector &&compositeFieldsTypes); + QueryField(QueryField &&) noexcept = default; + QueryField(const QueryField &) = default; + QueryField &operator=(QueryField &&) noexcept = default; + + [[nodiscard]] bool operator==(const QueryField &) const noexcept; + [[nodiscard]] bool operator!=(const QueryField &other) const noexcept { return !operator==(other); } + + [[nodiscard]] int IndexNo() const noexcept { return idxNo_; } + [[nodiscard]] bool IsFieldIndexed() const noexcept { return idxNo_ >= 0; } + [[nodiscard]] bool FieldsHaveBeenSet() const noexcept { return idxNo_ != IndexValueType::NotSet; } + [[nodiscard]] const FieldsSet &Fields() const & noexcept { return fieldsSet_; } + [[nodiscard]] const std::string &FieldName() const & noexcept { return fieldName_; } + [[nodiscard]] KeyValueType FieldType() const noexcept { return fieldType_; } + [[nodiscard]] KeyValueType SelectType() const noexcept { return selectType_; } + [[nodiscard]] const std::vector &CompositeFieldsTypes() const & noexcept { return compositeFieldsTypes_; } + [[nodiscard]] bool HaveEmptyField() const noexcept; + void SetField(FieldsSet &&fields) &; + void SetIndexData(int idxNo, FieldsSet &&fields, KeyValueType fieldType, KeyValueType selectType, + std::vector &&compositeFieldsTypes) &; + + QueryField &operator=(const QueryField &) = delete; + auto Fields() const && = delete; + auto FieldName() const && = delete; + auto CompositeFieldsTypes() const && = delete; + +private: + std::string fieldName_; + int idxNo_{IndexValueType::NotSet}; + FieldsSet fieldsSet_; + KeyValueType fieldType_{KeyValueType::Undefined{}}; + KeyValueType selectType_{KeyValueType::Undefined{}}; + std::vector compositeFieldsTypes_; +}; + +enum class VerifyQueryEntryFlags : unsigned { null = 0u, ignoreEmptyValues = 1u }; +[[nodiscard]] RX_ALWAYS_INLINE constexpr bool operator&(VerifyQueryEntryFlags lhs, VerifyQueryEntryFlags rhs) noexcept { + using UnderlyingType = std::underlying_type_t; + return static_cast(lhs) & static_cast(rhs); +} + +template +void VerifyQueryEntryValues(CondType, const VariantArray &); +extern template void VerifyQueryEntryValues(CondType, const VariantArray &); +extern template void VerifyQueryEntryValues(CondType, const VariantArray &); + +class QueryEntry : private QueryField { +public: + struct DistinctTag {}; + struct IgnoreEmptyValues {}; static constexpr unsigned kDefaultLimit = UINT_MAX; static constexpr unsigned kDefaultOffset = 0; - QueryEntry(std::string idx, CondType cond, VariantArray v) : index{std::move(idx)}, condition{cond}, values(std::move(v)) {} - QueryEntry(CondType cond, std::string idx, int idxN, bool dist = false) - : index(std::move(idx)), idxNo(idxN), condition(cond), distinct(dist) {} - QueryEntry() = default; + template + QueryEntry(Str &&fieldName, CondType cond, VariantArray &&v) + : QueryField(std::forward(fieldName)), values_(std::move(v)), condition_(cond) { + Verify(); + } + template + QueryEntry(Str &&fieldName, DistinctTag) : QueryField(std::forward(fieldName)), condition_(CondAny), distinct_(true) { + Verify(); + } + QueryEntry(QueryField &&field, CondType cond, VariantArray &&v) + : QueryField(std::move(field)), values_(std::move(v)), condition_(cond) { + Verify(); + } + QueryEntry(QueryField &&field, CondType cond, IgnoreEmptyValues) : QueryField(std::move(field)), condition_(cond) { + verifyIgnoringEmptyValues(); + } + [[nodiscard]] CondType Condition() const noexcept { return condition_; } + [[nodiscard]] const VariantArray &Values() const & noexcept { return values_; } + [[nodiscard]] VariantArray &&Values() && noexcept { return std::move(values_); } + [[nodiscard]] auto UpdatableValues(IgnoreEmptyValues) & noexcept { + return VerifyingUpdater{*this}; + } + [[nodiscard]] bool Distinct() const noexcept { return distinct_; } + void Distinct(bool d) noexcept { distinct_ = d; } + using QueryField::IndexNo; + using QueryField::IsFieldIndexed; + using QueryField::FieldsHaveBeenSet; + using QueryField::Fields; + using QueryField::FieldName; + using QueryField::FieldType; + using QueryField::SelectType; + using QueryField::CompositeFieldsTypes; + using QueryField::SetField; + using QueryField::SetIndexData; + using QueryField::HaveEmptyField; + void SetCondAndValues(CondType cond, VariantArray &&values) { + VerifyQueryEntryValues(cond, values); + condition_ = cond; + values_ = std::move(values); + } + + const QueryField &FieldData() const & noexcept { return static_cast(*this); } + QueryField &FieldData() & noexcept { return static_cast(*this); } + void ConvertValuesToFieldType() & { + for (Variant &v : values_) { + v.convert(SelectType()); + } + } + void ConvertValuesToFieldType(const PayloadType &pt) & { + if (SelectType().Is() || Condition() == CondDWithin) { + return; + } + for (Variant &v : values_) { + v.convert(SelectType(), &pt, &Fields()); + } + } + void Verify() const { VerifyQueryEntryValues(condition_, values_); } + + [[nodiscard]] bool operator==(const QueryEntry &) const noexcept; + [[nodiscard]] bool operator!=(const QueryEntry &other) const noexcept { return !operator==(other); } - bool operator==(const QueryEntry &) const; - bool operator!=(const QueryEntry &other) const { return !operator==(other); } + [[nodiscard]] std::string Dump() const; + [[nodiscard]] std::string DumpBrief() const; - std::string index; - int idxNo = IndexValueType::NotSet; - CondType condition = CondType::CondAny; - bool distinct = false; - VariantArray values; + auto Values() const && = delete; + auto FieldData() const && = delete; - std::string Dump() const; - std::string DumpBrief() const; +private: + void verifyIgnoringEmptyValues() const { VerifyQueryEntryValues(condition_, values_); } + + VariantArray values_; + CondType condition_; + bool distinct_ = false; }; class BetweenFieldsQueryEntry { public: - BetweenFieldsQueryEntry(std::string fstIdx, CondType cond, std::string sndIdx); - - bool operator==(const BetweenFieldsQueryEntry &) const noexcept; - bool operator!=(const BetweenFieldsQueryEntry &other) const noexcept { return !operator==(other); } - - std::string firstIndex; - std::string secondIndex; - int firstIdxNo = IndexValueType::NotSet; - int secondIdxNo = IndexValueType::NotSet; + template + BetweenFieldsQueryEntry(StrL &&fstIdx, CondType cond, StrR &&sndIdx) + : leftField_{std::forward(fstIdx)}, rightField_{std::forward(sndIdx)}, condition_{cond} { + if (condition_ == CondAny || condition_ == CondEmpty || condition_ == CondDWithin) { + throw Error{errLogic, "Condition '%s' is inapplicable between two fields", std::string{CondTypeToStr(condition_)}}; + } + } - CondType Condition() const noexcept { return condition_; } - std::string Dump() const; + [[nodiscard]] bool operator==(const BetweenFieldsQueryEntry &) const noexcept; + [[nodiscard]] bool operator!=(const BetweenFieldsQueryEntry &other) const noexcept { return !operator==(other); } + + [[nodiscard]] CondType Condition() const noexcept { return condition_; } + [[nodiscard]] int LeftIdxNo() const noexcept { return leftField_.IndexNo(); } + [[nodiscard]] int RightIdxNo() const noexcept { return rightField_.IndexNo(); } + [[nodiscard]] const std::string &LeftFieldName() const & noexcept { return leftField_.FieldName(); } + [[nodiscard]] const std::string &RightFieldName() const & noexcept { return rightField_.FieldName(); } + [[nodiscard]] const FieldsSet &LeftFields() const & noexcept { return leftField_.Fields(); } + [[nodiscard]] const FieldsSet &RightFields() const & noexcept { return rightField_.Fields(); } + [[nodiscard]] KeyValueType LeftFieldType() const noexcept { return leftField_.FieldType(); } + [[nodiscard]] KeyValueType RightFieldType() const noexcept { return rightField_.FieldType(); } + [[nodiscard]] const std::vector &LeftCompositeFieldsTypes() const & noexcept { return leftField_.CompositeFieldsTypes(); } + [[nodiscard]] const std::vector &RightCompositeFieldsTypes() const & noexcept { + return rightField_.CompositeFieldsTypes(); + } + [[nodiscard]] const QueryField &LeftFieldData() const & noexcept { return leftField_; } + [[nodiscard]] QueryField &LeftFieldData() & noexcept { return leftField_; } + [[nodiscard]] const QueryField &RightFieldData() const & noexcept { return rightField_; } + [[nodiscard]] QueryField &RightFieldData() & noexcept { return rightField_; } + [[nodiscard]] bool FieldsHaveBeenSet() const noexcept { return leftField_.FieldsHaveBeenSet() && rightField_.FieldsHaveBeenSet(); } + [[nodiscard]] bool IsLeftFieldIndexed() const noexcept { return leftField_.IsFieldIndexed(); } + [[nodiscard]] bool IsRightFieldIndexed() const noexcept { return rightField_.IsFieldIndexed(); } + [[nodiscard]] std::string Dump() const; + + auto LeftFieldName() const && = delete; + auto RightFieldName() const && = delete; + auto LeftFields() const && = delete; + auto RightFields() const && = delete; + auto LeftCompositeFieldsTypes() const && = delete; + auto RightCompositeFieldsTypes() const && = delete; + auto LeftFieldData() const && = delete; + auto RightFieldData() const && = delete; private: + QueryField leftField_; + QueryField rightField_; CondType condition_; }; struct AlwaysFalse {}; constexpr bool operator==(AlwaysFalse, AlwaysFalse) noexcept { return true; } +struct AlwaysTrue {}; +constexpr bool operator==(AlwaysTrue, AlwaysTrue) noexcept { return true; } class JsonBuilder; @@ -88,9 +233,60 @@ struct QueryEntriesBracket : public Bracket { EqualPositions_t equalPositions; }; -class QueryEntries - : public ExpressionTree { - using Base = ExpressionTree; +class SubQueryEntry { +public: + SubQueryEntry(CondType cond, size_t qIdx, VariantArray &&values) : condition_{cond}, queryIndex_{qIdx}, values_{std::move(values)} { + VerifyQueryEntryValues(condition_, values_); + } + [[nodiscard]] CondType Condition() const noexcept { return condition_; } + [[nodiscard]] size_t QueryIndex() const noexcept { return queryIndex_; } + [[nodiscard]] const VariantArray &Values() const & noexcept { return values_; } + [[nodiscard]] bool operator==(const SubQueryEntry &other) const noexcept { + return condition_ == other.condition_ && queryIndex_ == other.queryIndex_; + } + [[nodiscard]] bool operator!=(const SubQueryEntry &other) const noexcept { return !operator==(other); } + [[nodiscard]] std::string Dump(const std::vector &subQueries) const; + + auto Values() const && = delete; + +private: + CondType condition_; + // index of Query in Query::subQueries_ + size_t queryIndex_; + VariantArray values_; +}; + +class SubQueryFieldEntry { +public: + template + SubQueryFieldEntry(Str &&field, CondType cond, size_t qIdx) : field_{std::forward(field)}, condition_{cond}, queryIndex_{qIdx} { + if (cond == CondAny || cond == CondEmpty) { + throw Error{errQueryExec, "Condition %s with field and subquery", cond == CondAny ? "Any" : "Empty"}; + } + } + [[nodiscard]] const std::string &FieldName() const & noexcept { return field_; } + [[nodiscard]] std::string &&FieldName() && noexcept { return std::move(field_); } + [[nodiscard]] CondType Condition() const noexcept { return condition_; } + [[nodiscard]] size_t QueryIndex() const noexcept { return queryIndex_; } + [[nodiscard]] bool operator==(const SubQueryFieldEntry &other) const noexcept { + return field_ == other.field_ && condition_ == other.condition_ && queryIndex_ == other.queryIndex_; + } + [[nodiscard]] bool operator!=(const SubQueryFieldEntry &other) const noexcept { return !operator==(other); } + [[nodiscard]] std::string Dump(const std::vector &subQueries) const; + + auto FieldName() const && = delete; + +private: + std::string field_; + CondType condition_; + // index of Query in Query::subQueries_ + size_t queryIndex_; +}; + +class QueryEntries : public ExpressionTree { + using Base = ExpressionTree; QueryEntries(Base &&b) : Base{std::move(b)} {} public: @@ -98,18 +294,16 @@ class QueryEntries QueryEntries(QueryEntries &&) = default; QueryEntries(const QueryEntries &) = default; QueryEntries &operator=(QueryEntries &&) = default; - QueryEntries MakeLazyCopy() & { return {makeLazyCopy()}; } void ToDsl(const Query &parentQuery, JsonBuilder &builder) const { return toDsl(cbegin(), cend(), parentQuery, builder); } void WriteSQLWhere(const Query &parentQuery, WrSerializer &, bool stripArgs) const; - void Serialize(WrSerializer &ser) const { serialize(cbegin(), cend(), ser); } - bool CheckIfSatisfyConditions(const ConstPayload &pl, TagsMatcher &tm) const { - return checkIfSatisfyConditions(cbegin(), cend(), pl, tm); - } + void Serialize(WrSerializer &ser, const std::vector &subQueries) const { serialize(cbegin(), cend(), ser, subQueries); } + bool CheckIfSatisfyConditions(const ConstPayload &pl) const { return checkIfSatisfyConditions(cbegin(), cend(), pl); } + static bool CheckIfSatisfyCondition(const VariantArray &lValues, CondType, const VariantArray &rValues); template - std::string Dump(const std::vector &joinedSelectors) const { + std::string Dump(const std::vector &joinedSelectors, const std::vector &subQueries) const { WrSerializer ser; - dump(0, cbegin(), cend(), joinedSelectors, ser); + dump(0, cbegin(), cend(), joinedSelectors, subQueries, ser); dumpEqualPositions(0, ser, equalPositions); return std::string{ser.Slice()}; } @@ -119,58 +313,24 @@ class QueryEntries private: static void toDsl(const_iterator it, const_iterator to, const Query &parentQuery, JsonBuilder &); static void writeSQL(const Query &parentQuery, const_iterator from, const_iterator to, WrSerializer &, bool stripArgs); - static void serialize(const_iterator it, const_iterator to, WrSerializer &); - static bool checkIfSatisfyConditions(const_iterator begin, const_iterator end, const ConstPayload &, TagsMatcher &); - static bool checkIfSatisfyCondition(const QueryEntry &, const ConstPayload &, TagsMatcher &); - static bool checkIfSatisfyCondition(const BetweenFieldsQueryEntry &, const ConstPayload &, TagsMatcher &); - static bool checkIfSatisfyCondition(const VariantArray &lValues, CondType, const VariantArray &rValues); + static void serialize(const_iterator it, const_iterator to, WrSerializer &, const std::vector &subQueries); + static void serialize(CondType, const VariantArray &values, WrSerializer &); + static bool checkIfSatisfyConditions(const_iterator begin, const_iterator end, const ConstPayload &); + static bool checkIfSatisfyCondition(const QueryEntry &, const ConstPayload &); + static bool checkIfSatisfyCondition(const BetweenFieldsQueryEntry &, const ConstPayload &); protected: - static void dumpEqualPositions(size_t level, WrSerializer &ser, const EqualPositions_t &equalPositions) { - for (const auto &eq : equalPositions) { - for (size_t i = 0; i < level; ++i) { - ser << " "; - } - ser << "equal_poisition("; - for (size_t i = 0, s = eq.size(); i < s; ++i) { - if (i != 0) ser << ", "; - ser << eq[i]; - } - ser << ")\n"; - } - } - + static void dumpEqualPositions(size_t level, WrSerializer &, const EqualPositions_t &); template - static void dump(size_t level, const_iterator begin, const_iterator end, const std::vector &joinedSelectors, WrSerializer &ser) { - for (const_iterator it = begin; it != end; ++it) { - for (size_t i = 0; i < level; ++i) { - ser << " "; - } - if (it != begin || it->operation != OpAnd) { - ser << it->operation << ' '; - } - it->InvokeAppropriate( - [&](const QueryEntriesBracket &b) { - ser << "(\n"; - dump(level + 1, it.cbegin(), it.cend(), joinedSelectors, ser); - dumpEqualPositions(level + 1, ser, b.equalPositions); - for (size_t i = 0; i < level; ++i) { - ser << " "; - } - ser << ")\n"; - }, - [&ser](const QueryEntry &qe) { ser << qe.Dump() << '\n'; }, - [&joinedSelectors, &ser](const JoinQueryEntry &jqe) { ser << jqe.Dump(joinedSelectors) << '\n'; }, - [&ser](const BetweenFieldsQueryEntry &qe) { ser << qe.Dump() << '\n'; }, - [&ser](const AlwaysFalse &) { ser << "AlwaysFalse" << 'n'; }); - } - } + static void dump(size_t level, const_iterator begin, const_iterator end, const std::vector &joinedSelectors, + const std::vector &subQueries, WrSerializer &); }; class UpdateEntry { public: - UpdateEntry(std::string c, VariantArray v, FieldModifyMode m = FieldModeSet, bool e = false) - : column_(std::move(c)), values_(std::move(v)), mode_(m), isExpression_(e) { + template + UpdateEntry(Str &&c, VariantArray &&v, FieldModifyMode m = FieldModeSet, bool e = false) + : column_(std::forward(c)), values_(std::move(v)), mode_(m), isExpression_(e) { if (column_.empty()) { throw Error{errParams, "Empty update column name"}; } @@ -192,36 +352,71 @@ class UpdateEntry { bool isExpression_ = false; }; -struct QueryJoinEntry { - QueryJoinEntry() = default; - QueryJoinEntry(OpType op, CondType cond, std::string idx, std::string jIdx) - : op_{op}, condition_{cond}, index_{std::move(idx)}, joinIndex_{std::move(jIdx)} {} - bool operator==(const QueryJoinEntry &) const noexcept; - bool operator!=(const QueryJoinEntry &qje) const noexcept { return !operator==(qje); } - OpType op_ = OpAnd; - CondType condition_ = CondEq; ///< Condition applied to expression: index_ COND joinIndex_ - std::string index_; ///< main ns index field name - std::string joinIndex_; ///< joining ns index field name - int idxNo = -1; ///< index_ field Index number in main ns - bool reverseNamespacesOrder = false; ///< controls SQL encoding order - ///< false: mainNs.index Condition joinNs.joinIndex - ///< true: joinNs.joinIndex Invert(Condition) mainNs.index +class QueryJoinEntry { +public: + QueryJoinEntry(OpType op, CondType cond, std::string &&leftFld, std::string &&rightFld, bool reverseNs = false) noexcept + : leftField_{std::move(leftFld)}, rightField_{std::move(rightFld)}, op_{op}, condition_{cond}, reverseNamespacesOrder_{reverseNs} {} + [[nodiscard]] bool operator==(const QueryJoinEntry &) const noexcept; + [[nodiscard]] bool operator!=(const QueryJoinEntry &other) const noexcept { return !operator==(other); } + [[nodiscard]] bool IsLeftFieldIndexed() const noexcept { return leftField_.IsFieldIndexed(); } + [[nodiscard]] bool IsRightFieldIndexed() const noexcept { return rightField_.IsFieldIndexed(); } + [[nodiscard]] int LeftIdxNo() const noexcept { return leftField_.IndexNo(); } + [[nodiscard]] int RightIdxNo() const noexcept { return rightField_.IndexNo(); } + [[nodiscard]] const FieldsSet &LeftFields() const & noexcept { return leftField_.Fields(); } + [[nodiscard]] const FieldsSet &RightFields() const & noexcept { return rightField_.Fields(); } + [[nodiscard]] KeyValueType LeftFieldType() const noexcept { return leftField_.FieldType(); } + [[nodiscard]] KeyValueType RightFieldType() const noexcept { return rightField_.FieldType(); } + [[nodiscard]] const std::vector &LeftCompositeFieldsTypes() const & noexcept { return leftField_.CompositeFieldsTypes(); } + [[nodiscard]] const std::vector &RightCompositeFieldsTypes() const & noexcept { + return rightField_.CompositeFieldsTypes(); + } + [[nodiscard]] OpType Operation() const noexcept { return op_; } + [[nodiscard]] CondType Condition() const noexcept { return condition_; } + [[nodiscard]] const std::string &LeftFieldName() const & noexcept { return leftField_.FieldName(); } + [[nodiscard]] const std::string &RightFieldName() const & noexcept { return rightField_.FieldName(); } + [[nodiscard]] bool ReverseNamespacesOrder() const noexcept { return reverseNamespacesOrder_; } + [[nodiscard]] const QueryField &LeftFieldData() const & noexcept { return leftField_; } + [[nodiscard]] QueryField &LeftFieldData() & noexcept { return leftField_; } + [[nodiscard]] const QueryField &RightFieldData() const & noexcept { return rightField_; } + [[nodiscard]] QueryField &RightFieldData() & noexcept { return rightField_; } + void SetLeftIndexData(int idxNo, FieldsSet &&fields, KeyValueType fieldType, KeyValueType selectType, + std::vector &&compositeFieldsTypes) & { + leftField_.SetIndexData(idxNo, std::move(fields), fieldType, selectType, std::move(compositeFieldsTypes)); + } + void SetRightIndexData(int idxNo, FieldsSet &&fields, KeyValueType fieldType, KeyValueType selectType, + std::vector &&compositeFieldsTypes) & { + rightField_.SetIndexData(idxNo, std::move(fields), fieldType, selectType, std::move(compositeFieldsTypes)); + } + void SetLeftField(FieldsSet &&fields) & { leftField_.SetField(std::move(fields)); } + void SetRightField(FieldsSet &&fields) & { rightField_.SetField(std::move(fields)); } + [[nodiscard]] bool FieldsHaveBeenSet() const noexcept { return leftField_.FieldsHaveBeenSet() && rightField_.FieldsHaveBeenSet(); } template - std::string DumpCondition(const JS &joinedSelector, bool needOp = false) const { - WrSerializer ser; - const auto &q = joinedSelector.JoinQuery(); - if (needOp) { - ser << ' ' << op_ << ' '; - } - ser << q._namespace << '.' << joinIndex_ << ' ' << InvertJoinCondition(condition_) << ' ' << index_; - return std::string{ser.Slice()}; - } + std::string DumpCondition(const JS &joinedSelector, bool needOp = false) const; + + auto LeftFields() const && = delete; + auto RightFields() const && = delete; + auto LeftCompositeFieldsTypes() const && = delete; + auto RightCompositeFieldsTypes() const && = delete; + auto LeftFieldName() const && = delete; + auto RightFieldName() const && = delete; + auto LeftFieldData() const && = delete; + auto RightFieldData() const && = delete; + +private: + QueryField leftField_; + QueryField rightField_; + const OpType op_; + const CondType condition_; + const bool reverseNamespacesOrder_; ///< controls SQL encoding order + ///< false: mainNs.index Condition joinNs.joinIndex + ///< true: joinNs.joinIndex Invert(Condition) mainNs.index }; struct SortingEntry { SortingEntry() noexcept = default; - SortingEntry(std::string e, bool d) noexcept : expression(std::move(e)), desc(d) {} + template + SortingEntry(Str &&e, bool d) noexcept : expression(std::forward(e)), desc(d) {} bool operator==(const SortingEntry &) const noexcept; bool operator!=(const SortingEntry &se) const noexcept { return !operator==(se); } std::string expression; @@ -233,7 +428,7 @@ struct SortingEntries : public h_vector {}; class AggregateEntry { public: - AggregateEntry(AggType type, h_vector fields, SortingEntries sort = {}, unsigned limit = QueryEntry::kDefaultLimit, + AggregateEntry(AggType type, h_vector &&fields, SortingEntries &&sort = {}, unsigned limit = QueryEntry::kDefaultLimit, unsigned offset = QueryEntry::kDefaultOffset); [[nodiscard]] bool operator==(const AggregateEntry &) const noexcept; [[nodiscard]] bool operator!=(const AggregateEntry &ae) const noexcept { return !operator==(ae); } @@ -242,7 +437,7 @@ class AggregateEntry { [[nodiscard]] const SortingEntries &Sorting() const noexcept { return sortingEntries_; } [[nodiscard]] unsigned Limit() const noexcept { return limit_; } [[nodiscard]] unsigned Offset() const noexcept { return offset_; } - void AddSortingEntry(SortingEntry); + void AddSortingEntry(SortingEntry &&); void SetLimit(unsigned); void SetOffset(unsigned); diff --git a/cpp_src/core/query/sql/sqlencoder.cc b/cpp_src/core/query/sql/sqlencoder.cc index b961257a0..200703796 100644 --- a/cpp_src/core/query/sql/sqlencoder.cc +++ b/cpp_src/core/query/sql/sqlencoder.cc @@ -1,22 +1,25 @@ #include "core/query/sql/sqlencoder.h" #include "core/keyvalue/geometry.h" +#include "core/keyvalue/p_string.h" #include "core/queryresults/aggregationresult.h" #include "core/sorting/sortexpression.h" #include "core/type_consts_helpers.h" +#include "tools/logger.h" #include "tools/serializer.h" -namespace reindexer { +enum class NeedQuote : bool { No = false, Yes = true }; -static void indexToSql(const std::string &index, WrSerializer &ser) { - if (index.find('+') == std::string::npos) { +template +static void indexToSql(std::string_view index, reindexer::WrSerializer &ser) { + if (needQuote == NeedQuote::No || index.find('+') == std::string::npos) { ser << index; } else { ser << '"' << index << '"'; } } -static WrSerializer &stringToSql(const std::string &str, WrSerializer &ser) { +static reindexer::WrSerializer &stringToSql(std::string_view str, reindexer::WrSerializer &ser) { ser << '\''; for (auto c : str) { switch (c) { @@ -49,14 +52,14 @@ static WrSerializer &stringToSql(const std::string &str, WrSerializer &ser) { return ser; } -SQLEncoder::SQLEncoder(const Query &q) : query_(q) {} +namespace reindexer { void SQLEncoder::DumpSingleJoinQuery(size_t idx, WrSerializer &ser, bool stripArgs) const { - assertrx(idx < query_.joinQueries_.size()); - const auto &jq = query_.joinQueries_[idx]; + assertrx(idx < query_.GetJoinQueries().size()); + const auto &jq = query_.GetJoinQueries()[idx]; ser << ' ' << jq.joinType; - if (jq.entries.Empty() && jq.count == QueryEntry::kDefaultLimit && jq.sortingEntries_.empty()) { - ser << ' ' << jq._namespace << " ON "; + if (jq.Entries().Empty() && !jq.HasLimit() && jq.sortingEntries_.empty()) { + ser << ' ' << jq.NsName() << " ON "; } else { ser << " ("; jq.GetSQL(ser, stripArgs); @@ -65,28 +68,28 @@ void SQLEncoder::DumpSingleJoinQuery(size_t idx, WrSerializer &ser, bool stripAr if (jq.joinEntries_.size() != 1) ser << "("; for (auto &e : jq.joinEntries_) { if (&e != &*jq.joinEntries_.begin()) { - ser << ' ' << e.op_ << ' '; + ser << ' ' << e.Operation() << ' '; } - if (e.reverseNamespacesOrder) { - ser << jq._namespace << '.' << e.joinIndex_ << ' ' << InvertJoinCondition(e.condition_) << ' ' << query_._namespace << '.' - << e.index_; + if (e.ReverseNamespacesOrder()) { + ser << jq.NsName() << '.' << e.RightFieldName() << ' ' << InvertJoinCondition(e.Condition()) << ' ' << query_.NsName() << '.' + << e.LeftFieldName(); } else { - ser << query_._namespace << '.' << e.index_ << ' ' << e.condition_ << ' ' << jq._namespace << '.' << e.joinIndex_; + ser << query_.NsName() << '.' << e.LeftFieldName() << ' ' << e.Condition() << ' ' << jq.NsName() << '.' << e.RightFieldName(); } } if (jq.joinEntries_.size() != 1) ser << ')'; } void SQLEncoder::dumpJoined(WrSerializer &ser, bool stripArgs) const { - for (size_t i = 0; i < query_.joinQueries_.size(); ++i) { - if (query_.joinQueries_[i].joinType == JoinType::LeftJoin) { + for (size_t i = 0; i < query_.GetJoinQueries().size(); ++i) { + if (query_.GetJoinQueries()[i].joinType == JoinType::LeftJoin) { DumpSingleJoinQuery(i, ser, stripArgs); } } } void SQLEncoder::dumpMerged(WrSerializer &ser, bool stripArgs) const { - for (auto &me : query_.mergeQueries_) { + for (auto &me : query_.GetMergeQueries()) { ser << ' ' << me.joinType << "( "; me.GetSQL(ser, stripArgs); ser << ')'; @@ -137,9 +140,9 @@ void SQLEncoder::dumpEqualPositions(WrSerializer &ser, const EqualPositions_t &e } WrSerializer &SQLEncoder::GetSQL(WrSerializer &ser, bool stripArgs) const { - switch (query_.type_) { + switch (realQueryType_) { case QuerySelect: { - if (query_.local_) { + if (query_.IsLocal()) { ser << "LOCAL "; } ser << "SELECT "; @@ -173,16 +176,16 @@ WrSerializer &SQLEncoder::GetSQL(WrSerializer &ser, bool stripArgs) const { assertrx(query_.aggregations_[0].Fields().size() == 1); distinctIndex = query_.aggregations_[0].Fields()[0]; } - if (query_.selectFilter_.empty()) { - if (query_.count != 0 || query_.calcTotal == ModeNoTotal) { + if (query_.SelectFilters().empty()) { + if (query_.Limit() != 0 || !query_.HasCalcTotal()) { if (needComma) ser << ", "; ser << '*'; - if (query_.calcTotal != ModeNoTotal) { + if (query_.HasCalcTotal()) { needComma = true; } } } else { - for (const auto &filter : query_.selectFilter_) { + for (const auto &filter : query_.SelectFilters()) { if (filter == distinctIndex) continue; if (needComma) { ser << ", "; @@ -193,19 +196,19 @@ WrSerializer &SQLEncoder::GetSQL(WrSerializer &ser, bool stripArgs) const { } } } - if (query_.calcTotal != ModeNoTotal) { + if (query_.HasCalcTotal()) { if (needComma) ser << ", "; - if (query_.calcTotal == ModeAccurateTotal) ser << "COUNT(*)"; - if (query_.calcTotal == ModeCachedTotal) ser << "COUNT_CACHED(*)"; + if (query_.CalcTotal() == ModeAccurateTotal) ser << "COUNT(*)"; + if (query_.CalcTotal() == ModeCachedTotal) ser << "COUNT_CACHED(*)"; } - ser << " FROM " << query_._namespace; + ser << " FROM " << query_.NsName(); } break; case QueryDelete: - ser << "DELETE FROM " << query_._namespace; + ser << "DELETE FROM " << query_.NsName(); break; case QueryUpdate: { if (query_.UpdateFields().empty()) break; - ser << "UPDATE " << query_._namespace; + ser << "UPDATE " << query_.NsName(); FieldModifyMode mode = query_.UpdateFields().front().Mode(); bool isUpdate = (mode == FieldModeSet || mode == FieldModeSetJson); if (isUpdate) { @@ -222,11 +225,19 @@ WrSerializer &SQLEncoder::GetSQL(WrSerializer &ser, bool stripArgs) const { if (isArray) ser << '['; for (const Variant &v : field.Values()) { if (&v != &*field.Values().begin()) ser << ','; - if (v.Type().Is() && !field.IsExpression() && (mode != FieldModeSetJson)) { - stringToSql(v.As(), ser); - } else { - ser << v.As(); - } + v.Type().EvaluateOneOf(overloaded{ + [&](KeyValueType::String) { + if (!field.IsExpression() && mode != FieldModeSetJson) { + stringToSql(v.As(), ser); + } else { + ser << v.As(); + } + }, + [&](KeyValueType::Uuid) { ser << '\'' << v.As() << '\''; }, + [&](OneOf) { + ser << v.As(); + }}); } if (isArray) ser << "]"; } @@ -234,7 +245,7 @@ WrSerializer &SQLEncoder::GetSQL(WrSerializer &ser, bool stripArgs) const { break; } case QueryTruncate: - ser << "TRUNCATE " << query_._namespace; + ser << "TRUNCATE " << query_.NsName(); break; default: throw Error(errParams, "Not implemented"); @@ -245,13 +256,68 @@ WrSerializer &SQLEncoder::GetSQL(WrSerializer &ser, bool stripArgs) const { dumpMerged(ser, stripArgs); dumpOrderBy(ser, stripArgs); - if (query_.start != QueryEntry::kDefaultOffset && !stripArgs) ser << " OFFSET " << query_.start; - if (query_.count != QueryEntry::kDefaultLimit && !stripArgs) ser << " LIMIT " << query_.count; + if (query_.HasOffset() && !stripArgs) ser << " OFFSET " << query_.Offset(); + if (query_.HasLimit() && !stripArgs) ser << " LIMIT " << query_.Limit(); return ser; } static const char *opNames[] = {"-", "OR", "AND", "AND NOT"}; +template +static void dumpCondWithValues(WrSerializer &ser, std::string_view fieldName, CondType cond, const VariantArray &values, bool stripArgs) { + switch (cond) { + case CondDWithin: + ser << "ST_DWithin("; + indexToSql(fieldName, ser); + if (stripArgs) { + ser << ", ?, ?)"; + } else { + assertrx(values.size() == 2); + Point point; + double distance; + if (values[0].Type().Is()) { + point = static_cast(values[0]); + distance = values[1].As(); + } else { + point = static_cast(values[1]); + distance = values[0].As(); + } + ser << ", ST_GeomFromText('POINT(" << point.X() << ' ' << point.Y() << ")'), " << distance << ')'; + } + break; + case CondAny: + case CondEmpty: + indexToSql(fieldName, ser); + ser << ' ' << cond; + break; + case CondGt: + case CondGe: + case CondLt: + case CondLe: + case CondEq: + case CondSet: + case CondAllSet: + case CondRange: + case CondLike: + indexToSql(fieldName, ser); + ser << ' ' << cond << ' '; + if (stripArgs) { + ser << '?'; + } else { + if (values.size() != 1) ser << '('; + for (auto &v : values) { + if (&v != &values[0]) ser << ','; + v.Type().EvaluateOneOf(overloaded{ + [&](KeyValueType::String) { stringToSql(v.As(), ser); }, + [&](KeyValueType::Uuid) { ser << '\'' << v.As() << '\''; }, + [&](OneOf) { ser << v.As(); }}); + } + if (values.size() != 1) ser << ")"; + } + } +} + void SQLEncoder::dumpWhereEntries(QueryEntries::const_iterator from, QueryEntries::const_iterator to, WrSerializer &ser, bool stripArgs) const { int encodedEntries = 0; @@ -263,7 +329,29 @@ void SQLEncoder::dumpWhereEntries(QueryEntries::const_iterator from, QueryEntrie ser << "NOT "; } it->InvokeAppropriate( - Skip{}, + [&ser](const AlwaysTrue &) { + logPrintf(LogTrace, "Not normalized query to dsl"); + ser << "true"; + }, + [&ser](const AlwaysFalse &) { + logPrintf(LogTrace, "Not normalized query to dsl"); + ser << "false"; + }, + [&](const SubQueryEntry &sqe) { + if (encodedEntries) { + ser << opNames[op] << ' '; + } + dumpCondWithValues(ser, '(' + query_.GetSubQuery(sqe.QueryIndex()).GetSQL(stripArgs) + ')', sqe.Condition(), + sqe.Values(), stripArgs); + }, + [&](const SubQueryFieldEntry &sqe) { + if (encodedEntries) { + ser << opNames[op] << ' '; + } + ser << sqe.FieldName() << ' ' << sqe.Condition() << " ("; + SQLEncoder{query_.GetSubQuery(sqe.QueryIndex())}.GetSQL(ser, stripArgs); + ser << ')'; + }, [&](const QueryEntriesBracket &bracket) { if (encodedEntries) { ser << opNames[op] << ' '; @@ -277,64 +365,31 @@ void SQLEncoder::dumpWhereEntries(QueryEntries::const_iterator from, QueryEntrie if (encodedEntries) { ser << opNames[op] << ' '; } - if (entry.condition == CondDWithin) { - ser << "ST_DWithin("; - indexToSql(entry.index, ser); - if (stripArgs) { - ser << ", ?, ?)"; - } else { - assertrx(entry.values.size() == 2); - Point point; - double distance; - if (entry.values[0].Type().Is()) { - point = static_cast(entry.values[0]); - distance = entry.values[1].As(); - } else { - point = static_cast(entry.values[1]); - distance = entry.values[0].As(); - } - ser << ", ST_GeomFromText('POINT(" << point.X() << ' ' << point.Y() << ")'), " << distance << ')'; - } - } else { - indexToSql(entry.index, ser); - ser << ' ' << entry.condition << ' '; - if (entry.condition == CondEmpty || entry.condition == CondAny) { - } else if (stripArgs) { - ser << '?'; - } else { - if (entry.values.size() != 1) ser << '('; - for (auto &v : entry.values) { - if (&v != &entry.values[0]) ser << ','; - if (v.Type().Is()) { - stringToSql(v.As(), ser); - } else { - ser << v.As(); - } - } - if (entry.values.size() != 1) ser << ")"; - } - } + dumpCondWithValues(ser, entry.FieldName(), entry.Condition(), entry.Values(), stripArgs); }, [&](const JoinQueryEntry &jqe) { - if (encodedEntries && query_.joinQueries_[jqe.joinIndex].joinType != JoinType::OrInnerJoin) { + if (encodedEntries && query_.GetJoinQueries()[jqe.joinIndex].joinType != JoinType::OrInnerJoin) { ser << opNames[op] << ' '; } SQLEncoder(query_).DumpSingleJoinQuery(jqe.joinIndex, ser, stripArgs); }, - [&ser](const BetweenFieldsQueryEntry &entry) { - indexToSql(entry.firstIndex, ser); + [&](const BetweenFieldsQueryEntry &entry) { + if (encodedEntries) { + ser << opNames[op] << ' '; + } + indexToSql(entry.LeftFieldName(), ser); ser << ' ' << entry.Condition() << ' '; - indexToSql(entry.secondIndex, ser); + indexToSql(entry.RightFieldName(), ser); }); ++encodedEntries; } } void SQLEncoder::dumpSQLWhere(WrSerializer &ser, bool stripArgs) const { - if (query_.entries.Empty()) return; + if (query_.Entries().Empty()) return; ser << " WHERE "; - dumpWhereEntries(query_.entries.cbegin(), query_.entries.cend(), ser, stripArgs); - dumpEqualPositions(ser, query_.entries.equalPositions); + dumpWhereEntries(query_.Entries().cbegin(), query_.Entries().cend(), ser, stripArgs); + dumpEqualPositions(ser, query_.Entries().equalPositions); } } // namespace reindexer diff --git a/cpp_src/core/query/sql/sqlencoder.h b/cpp_src/core/query/sql/sqlencoder.h index c1b217609..968f387df 100644 --- a/cpp_src/core/query/sql/sqlencoder.h +++ b/cpp_src/core/query/sql/sqlencoder.h @@ -2,7 +2,6 @@ #include #include "core/query/query.h" -#include "core/type_consts.h" /// @namespace reindexer /// The base namespace @@ -12,7 +11,8 @@ class WrSerializer; class SQLEncoder { public: - SQLEncoder(const Query &q); + SQLEncoder(const Query &q) noexcept : SQLEncoder(q, q.Type()) {} + SQLEncoder(const Query &q, QueryType queryType) noexcept : query_(q), realQueryType_(queryType) {} WrSerializer &GetSQL(WrSerializer &ser, bool stripArgs = false) const; @@ -40,8 +40,8 @@ class SQLEncoder { /// Builds a print version of all equal_position() functions in query. /// @param ser - serializer to store SQL string - /// @param parenthesisIndex - index of current parenthesis - void dumpEqualPositions(WrSerializer &ser, const EqualPositions_t &) const; + /// @param equalPositions - equal positions array + void dumpEqualPositions(WrSerializer &ser, const EqualPositions_t &equalPositions) const; /// Builds a print version of all where condition entries. /// @param from - iterator to first entry @@ -52,6 +52,7 @@ class SQLEncoder { void dumpSQLWhere(WrSerializer &ser, bool stripArgs) const; const Query &query_; + const QueryType realQueryType_; }; } // namespace reindexer diff --git a/cpp_src/core/query/sql/sqlparser.cc b/cpp_src/core/query/sql/sqlparser.cc index d0d664ebc..97ee3efc5 100644 --- a/cpp_src/core/query/sql/sqlparser.cc +++ b/cpp_src/core/query/sql/sqlparser.cc @@ -6,6 +6,7 @@ #include "core/queryresults/aggregationresult.h" #include "core/type_consts_helpers.h" #include "sqltokentype.h" +#include "tools/stringstools.h" #include "vendor/double-conversion/double-conversion.h" #include "vendor/gason/gason.h" @@ -13,9 +14,11 @@ namespace reindexer { using namespace std::string_view_literals; -int SQLParser::Parse(std::string_view q) { +Query SQLParser::Parse(std::string_view q) { tokenizer parser(q); - return Parse(parser); + Query query; + SQLParser{query}.Parse(parser); + return query; } bool SQLParser::reachedAutocompleteToken(tokenizer &parser, const token &tok) { @@ -46,20 +49,20 @@ int SQLParser::Parse(tokenizer &parser) { parser.skip_space(); token tok = peekSqlToken(parser, Start); if (tok.text() == "explain"sv) { - query_.explain_ = true; + query_.Explain(); parser.next_token(); tok = peekSqlToken(parser, StartAfterExplain); if (tok.text() == "local"sv) { - query_.local_ = true; + query_.Local(); parser.next_token(); tok = peekSqlToken(parser, StartAfterLocalExplain); } } else if (tok.text() == "local"sv) { - query_.local_ = true; + query_.Local(); parser.next_token(); tok = peekSqlToken(parser, StartAfterLocal); if (tok.text() == "explain"sv) { - query_.explain_ = true; + query_.Explain(); parser.next_token(); tok = peekSqlToken(parser, StartAfterLocalExplain); } @@ -69,7 +72,7 @@ int SQLParser::Parse(tokenizer &parser) { query_.type_ = QuerySelect; parser.next_token(); selectParse(parser); - } else if (query_.local_) { + } else if (query_.IsLocal()) { throw Error(errParams, "Syntax error at or near '%s', %s; only SELECT query could be LOCAL", tok.text(), parser.where()); } else if (tok.text() == "delete"sv) { query_.type_ = QueryDelete; @@ -99,6 +102,7 @@ int SQLParser::selectParse(tokenizer &parser) { // Get filter token tok; bool wasSelectFilter = false; + std::vector selectFilters; while (!parser.end()) { auto nameWithCase = peekSqlToken(parser, SingleSelectFieldSqlToken, false); auto name = parser.next_token(); @@ -107,16 +111,16 @@ int SQLParser::selectParse(tokenizer &parser) { parser.next_token(); tok = peekSqlToken(parser, SingleSelectFieldSqlToken); if (name.text() == "count"sv) { - query_.calcTotal = ModeAccurateTotal; + query_.CalcTotal(ModeAccurateTotal); if (!wasSelectFilter) { - query_.count = 0; + query_.Limit(0); } tok = parser.next_token(); if (tok.text() != "*") throw Error(errParseSQL, "Expected '*', but found '%s' in query, %s", tok.text(), parser.where()); } else if (name.text() == "count_cached"sv) { - query_.calcTotal = ModeCachedTotal; + query_.CalcTotal(ModeCachedTotal); if (!wasSelectFilter) { - query_.count = 0; + query_.Limit(0); } tok = parser.next_token(); if (tok.text() != "*"sv) throw Error(errParseSQL, "Expected '*', but found '%s' in query, %s", tok.text(), parser.where()); @@ -183,28 +187,31 @@ int SQLParser::selectParse(tokenizer &parser) { if (!query_.CanAddSelectFilter()) { throw Error(errConflict, kAggregationWithSelectFieldsMsgError); } - query_.selectFilter_.emplace_back(nameWithCase.text()); - query_.count = QueryEntry::kDefaultLimit; + selectFilters.emplace_back(nameWithCase.text()); + query_.Limit(QueryEntry::kDefaultLimit); wasSelectFilter = true; } else if (name.text() == "*"sv) { if (!query_.CanAddSelectFilter()) { throw Error(errConflict, kAggregationWithSelectFieldsMsgError); } - query_.count = QueryEntry::kDefaultLimit; + query_.Limit(QueryEntry::kDefaultLimit); wasSelectFilter = true; - query_.selectFilter_.clear(); + selectFilters.clear(); } if (tok.text() != ","sv) break; tok = parser.next_token(); } + if (wasSelectFilter) { + query_.Select(std::move(selectFilters)); + } peekSqlToken(parser, FromSqlToken); if (parser.next_token().text() != "from"sv) throw Error(errParams, "Expected 'FROM', but found '%s' in query, %s", tok.text(), parser.where()); peekSqlToken(parser, NamespaceSqlToken); - query_._namespace = std::string(parser.next_token().text()); - ctx_.updateLinkedNs(query_._namespace); + query_.SetNsName(parser.next_token().text()); + ctx_.updateLinkedNs(query_.NsName()); while (!parser.end()) { tok = peekSqlToken(parser, SelectConditionsStart); @@ -216,17 +223,17 @@ int SQLParser::selectParse(tokenizer &parser) { tok = parser.next_token(); if (tok.type != TokenNumber) throw Error(errParseSQL, "Expected number, but found '%s' in query, %s", tok.text(), parser.where()); - query_.count = stoi(tok.text()); + query_.Limit(stoi(tok.text())); } else if (tok.text() == "offset"sv) { parser.next_token(); tok = parser.next_token(); if (tok.type != TokenNumber) throw Error(errParseSQL, "Expected number, but found '%s' in query, %s", tok.text(), parser.where()); - query_.start = stoi(tok.text()); + query_.Offset(stoi(tok.text())); } else if (tok.text() == "order"sv) { parser.next_token(); parseOrderBy(parser, query_.sortingEntries_, query_.forcedSortOrder_); - ctx_.updateLinkedNs(query_._namespace); + ctx_.updateLinkedNs(query_.NsName()); } else if (tok.text() == "join"sv) { parser.next_token(); parseJoin(JoinType::LeftJoin, parser); @@ -243,15 +250,15 @@ int SQLParser::selectParse(tokenizer &parser) { if (parser.next_token().text() != "join") { throw Error(errParseSQL, "Expected JOIN, but found '%s' in query, %s", tok.text(), parser.where()); } - auto jtype = (query_.nextOp_ == OpOr) ? JoinType::OrInnerJoin : JoinType::InnerJoin; - query_.nextOp_ = OpAnd; + auto jtype = (query_.NextOp() == OpOr) ? JoinType::OrInnerJoin : JoinType::InnerJoin; + query_.And(); parseJoin(jtype, parser); } else if (tok.text() == "merge"sv) { parser.next_token(); parseMerge(parser); } else if (tok.text() == "or"sv) { parser.next_token(); - query_.nextOp_ = OpOr; + query_.Or(); } else { break; } @@ -418,8 +425,8 @@ int SQLParser::deleteParse(tokenizer &parser) { throw Error(errParams, "Expected 'FROM', but found '%s' in query, %s", tok.text(), parser.where()); peekSqlToken(parser, NamespaceSqlToken); - query_._namespace = std::string(parser.next_token().text()); - ctx_.updateLinkedNs(query_._namespace); + query_.SetNsName(parser.next_token().text()); + ctx_.updateLinkedNs(query_.NsName()); while (!parser.end()) { tok = peekSqlToken(parser, DeleteConditionsStart); @@ -431,17 +438,17 @@ int SQLParser::deleteParse(tokenizer &parser) { tok = parser.next_token(); if (tok.type != TokenNumber) throw Error(errParseSQL, "Expected number, but found '%s' in query, %s", tok.text(), parser.where()); - query_.count = stoi(tok.text()); + query_.Limit(stoi(tok.text())); } else if (tok.text() == "offset"sv) { parser.next_token(); tok = parser.next_token(); if (tok.type != TokenNumber) throw Error(errParseSQL, "Expected number, but found '%s' in query, %s", tok.text(), parser.where()); - query_.start = stoi(tok.text()); + query_.Offset(stoi(tok.text())); } else if (tok.text() == "order"sv) { parser.next_token(); parseOrderBy(parser, query_.sortingEntries_, query_.forcedSortOrder_); - ctx_.updateLinkedNs(query_._namespace); + ctx_.updateLinkedNs(query_.NsName()); } else break; } @@ -507,7 +514,7 @@ UpdateEntry SQLParser::parseUpdateField(tokenizer &parser) { if (tok.type != TokenName) { throw Error(errParseSQL, "Expected field name but found '%s' in query %s", tok.text(), parser.where()); } - UpdateEntry updateField{{tok.text().data(), tok.text().length()}, {}}; + UpdateEntry updateField{tok.text(), {}}; parser.next_token(); tok = parser.next_token(); @@ -521,7 +528,7 @@ UpdateEntry SQLParser::parseUpdateField(tokenizer &parser) { updateField.Values().MarkArray(); for (;;) { tok = parser.next_token(tokenizer::flags::no_flags); - if (tok.text() == "]") { + if (tok.text() == "]"sv) { if (updateField.Values().empty()) break; throw Error(errParseSQL, "Expected field value, but found ']' in query, %s", parser.where()); } @@ -561,15 +568,15 @@ int SQLParser::updateParse(tokenizer &parser) { parser.next_token(); token tok = peekSqlToken(parser, NamespaceSqlToken); - query_._namespace = std::string(tok.text()); - ctx_.updateLinkedNs(query_._namespace); + query_.SetNsName(tok.text()); + ctx_.updateLinkedNs(query_.NsName()); parser.next_token(); tok = peekSqlToken(parser, UpdateOptionsSqlToken); if (tok.text() == "set"sv) { parser.next_token(); while (!parser.end()) { - query_.updateFields_.emplace_back(parseUpdateField(parser)); + query_.UpdateField(parseUpdateField(parser)); tok = parser.peek_token(); if (tok.text() != ","sv) break; parser.next_token(); @@ -602,12 +609,95 @@ int SQLParser::updateParse(tokenizer &parser) { int SQLParser::truncateParse(tokenizer &parser) { parser.next_token(); token tok = peekSqlToken(parser, NamespaceSqlToken); - query_._namespace = std::string(tok.text()); - ctx_.updateLinkedNs(query_._namespace); + query_.SetNsName(tok.text()); + ctx_.updateLinkedNs(query_.NsName()); parser.next_token(); return 0; } +bool isCondition(std::string_view text) noexcept { + return text == "="sv || text == "=="sv || text == "<>"sv || iequals(text, "is"sv) || text == ">"sv || text == ">="sv || text == "<"sv || + text == "<="sv || iequals(text, "in"sv) || iequals(text, "range"sv) || iequals(text, "like"sv) || iequals(text, "allset"sv); +} + +Query SQLParser::parseSubQuery(tokenizer &parser) { + Query subquery; + SQLParser subparser(subquery); + if (ctx_.autocompleteMode) { + subparser.ctx_.suggestionsPos = ctx_.suggestionsPos; + subparser.ctx_.autocompleteMode = true; + } + // skip select + auto tok = parser.next_token(); + nestedSelectParse(subparser, parser); + tok = parser.next_token(); + if (tok.text() != ")"sv) { + throw Error(errParseSQL, "Expected ')', but found %s, %s", tok.text(), parser.where()); + } + return subquery; +} + +template +void SQLParser::parseWhereCondition(tokenizer &parser, T &&firstArg, OpType op) { + // Operator + CondType condition; + auto tok = peekSqlToken(parser, ConditionSqlToken); + if (tok.text() == "<>"sv) { + condition = CondEq; + if (op == OpAnd) + op = OpNot; + else if (op == OpNot) + op = OpAnd; + else { + throw Error(errParseSQL, "<> condition with OR is not supported, %s", parser.where()); + } + } else { + condition = getCondType(tok.text()); + } + parser.next_token(); + + // Value + if (ctx_.autocompleteMode) peekSqlToken(parser, WhereFieldValueSqlToken, false); + tok = parser.next_token(); + if (iequals(tok.text(), "null"sv) || iequals(tok.text(), "empty"sv)) { + query_.NextOp(op).Where(std::forward(firstArg), CondEmpty, VariantArray{}); + } else if (iequals(tok.text(), "not"sv)) { + tok = peekSqlToken(parser, WhereFieldNegateValueSqlToken, false); + if (!iequals(tok.text(), "null"sv) && !iequals(tok.text(), "empty"sv)) { + throw Error(errParseSQL, "Expected NULL, but found '%s' in query, %s", tok.text(), parser.where()); + } + query_.NextOp(op).Where(std::forward(firstArg), CondAny, VariantArray{}); + tok = parser.next_token(false); + } else if (tok.text() == "("sv) { + if constexpr (!std::is_same_v) { + if (iequals(parser.peek_token().text(), "select"sv) && !isCondition(parser.peek_second_token().text())) { + query_.NextOp(op).Where(std::forward(firstArg), condition, parseSubQuery(parser)); + return; + } + } + VariantArray values; + for (;;) { + tok = parser.next_token(); + if (tok.text() == ")"sv && tok.type == TokenSymbol) break; + values.push_back(token2kv(tok, parser, true)); + tok = parser.next_token(); + if (tok.text() == ")"sv) break; + if (tok.text() != ","sv) + throw Error(errParseSQL, "Expected ')' or ',', but found '%s' in query, %s", tok.text(), parser.where()); + } + query_.NextOp(op).Where(std::forward(firstArg), condition, std::move(values)); + } else if (tok.type != TokenName || iequals(tok.text(), "true"sv) || iequals(tok.text(), "false"sv)) { + query_.NextOp(op).Where(std::forward(firstArg), condition, {token2kv(tok, parser, true)}); + } else { + if constexpr (std::is_same_v) { + throw Error(errParseSQL, "Field cannot be after subquery. (text = '%s' location = %s)", tok.text(), parser.where()); + } else { + // Second field + query_.NextOp(op).WhereBetweenFields(std::forward(firstArg), condition, std::string{tok.text()}); + } + } +} + int SQLParser::parseWhere(tokenizer &parser) { token tok; OpType nextOp = OpAnd; @@ -625,15 +715,21 @@ int SQLParser::parseWhere(tokenizer &parser) { tok = peekSqlToken(parser, WhereFieldSqlToken, false); parser.next_token(false); if (tok.text() == "("sv) { - query_.entries.OpenBracket(nextOp); - ++openBracketsCount; - lastBracketPosition = query_.entries.Size(); tok = peekSqlToken(parser, WhereFieldSqlToken, false); - if (iequals(tok.text(), "not"sv)) { - nextOp = OpNot; - parser.next_token(); - } else { + if (iequals(tok.text(), "select"sv) && !isCondition(parser.peek_second_token().text())) { + parseWhereCondition(parser, parseSubQuery(parser), nextOp); nextOp = OpAnd; + } else { + query_.NextOp(nextOp); + query_.OpenBracket(); + ++openBracketsCount; + lastBracketPosition = query_.Entries().Size(); + if (iequals(tok.text(), "not"sv)) { + nextOp = OpNot; + parser.next_token(); + } else { + nextOp = OpAnd; + } } continue; } @@ -659,62 +755,13 @@ int SQLParser::parseWhere(tokenizer &parser) { throw Error(errParseSQL, "Expected JOIN, but found '%s' in query, %s", tok.text(), parser.where()); } auto jtype = nextOp == OpOr ? JoinType::OrInnerJoin : JoinType::InnerJoin; - query_.nextOp_ = OpAnd; + query_.And(); parseJoin(jtype, parser); } else if (iequals(tok.text(), "st_dwithin"sv)) { parseDWithin(parser, nextOp); nextOp = OpAnd; } else { - // Index name - const std::string index{tok.text()}; - - // Operator - CondType condition; - tok = peekSqlToken(parser, ConditionSqlToken); - if (tok.text() == "<>"sv) { - condition = CondEq; - if (nextOp == OpAnd) { - nextOp = OpNot; - } else if (nextOp == OpNot) { - nextOp = OpAnd; - } else { - throw Error(errParseSQL, "<> condition with OR is not supported, %s", parser.where()); - } - } else { - condition = getCondType(tok.text()); - } - parser.next_token(); - - // Value - if (ctx_.autocompleteMode) peekSqlToken(parser, WhereFieldValueSqlToken, false); - tok = parser.next_token(); - if (iequals(tok.text(), "null"sv) || iequals(tok.text(), "empty"sv)) { - query_.entries.Append(nextOp, QueryEntry{index, CondEmpty, {}}); - } else if (iequals(tok.text(), "not"sv)) { - tok = peekSqlToken(parser, WhereFieldNegateValueSqlToken, false); - if (!iequals(tok.text(), "null"sv) && !iequals(tok.text(), "empty"sv)) { - throw Error(errParseSQL, "Expected NULL, but found '%s' in query, %s", tok.text(), parser.where()); - } - query_.entries.Append(nextOp, QueryEntry{index, CondAny, {}}); - tok = parser.next_token(false); - } else if (tok.text() == "("sv) { - VariantArray values; - for (;;) { - tok = parser.next_token(); - if (tok.text() == ")"sv && tok.type == TokenSymbol) break; - values.push_back(token2kv(tok, parser, true)); - tok = parser.next_token(); - if (tok.text() == ")"sv) break; - if (tok.text() != ","sv) - throw Error(errParseSQL, "Expected ')' or ',', but found '%s' in query, %s", tok.text(), parser.where()); - } - query_.entries.Append(nextOp, QueryEntry{index, condition, std::move(values)}); - } else if (tok.type != TokenName || toLower(tok.text()) == "true" || toLower(tok.text()) == "false") { - query_.entries.Append(nextOp, QueryEntry{index, condition, {token2kv(tok, parser, true)}}); - // Second field - } else { - query_.entries.Append(nextOp, BetweenFieldsQueryEntry{index, condition, std::string{tok.text()}}); - } + parseWhereCondition(parser, std::string{tok.text()}, nextOp); nextOp = OpAnd; } } @@ -726,7 +773,7 @@ int SQLParser::parseWhere(tokenizer &parser) { } while (openBracketsCount > 0 && tok.text() == ")"sv) { - query_.entries.CloseBracket(); + query_.CloseBracket(); --openBracketsCount; parser.next_token(); tok = parser.peek_token(); @@ -751,15 +798,15 @@ int SQLParser::parseWhere(tokenizer &parser) { break; } } - for (auto &&eqPos : equalPositions) { + for (auto &eqPos : equalPositions) { if (eqPos.first == 0) { - query_.entries.equalPositions.emplace_back(std::move(eqPos.second)); + query_.SetEqualPositions(std::move(eqPos.second)); } else { - query_.entries.Get(eqPos.first - 1).equalPositions.emplace_back(std::move(eqPos.second)); + query_.SetEqualPositions(eqPos.first - 1, std::move(eqPos.second)); } } - if (query_.entries.Empty()) { + if (query_.Entries().Empty()) { throw Error(errParseSQL, "Expected condition after 'WHERE'"); } @@ -781,8 +828,8 @@ void SQLParser::parseEqualPositions(tokenizer &parser, std::vectorHoldsOrReferTo() && nameWithCase.text() == it->Value().index) { + for (auto it = query_.Entries().begin_of_current_bracket(); it != query_.Entries().end(); ++it) { + if (it->Is() && nameWithCase.text() == it->Value().FieldName()) { validField = true; break; } @@ -949,17 +996,13 @@ void SQLParser::parseJoin(JoinType type, tokenizer &parser) { throw Error(errParseSQL, "Expected ')', but found '%s', %s", tok.text(), parser.where()); } } else { - jquery._namespace = std::string(tok.text()); - ctx_.updateLinkedNs(jquery._namespace); + jquery.SetNsName(tok.text()); + ctx_.updateLinkedNs(jquery.NsName()); } jquery.joinType = type; - jparser.parseJoinEntries(parser, query_._namespace, jquery); - - if (type != JoinType::LeftJoin) { - query_.entries.Append((type == JoinType::InnerJoin) ? OpAnd : OpOr, JoinQueryEntry(query_.joinQueries_.size())); - } + jparser.parseJoinEntries(parser, query_.NsName(), jquery); - query_.joinQueries_.emplace_back(std::move(jquery)); + query_.Join(std::move(jquery)); } void SQLParser::parseMerge(tokenizer &parser) { @@ -986,7 +1029,7 @@ void SQLParser::parseMerge(tokenizer &parser) { } mquery.joinType = JoinType::Merge; - query_.mergeQueries_.emplace_back(std::move(mquery)); + query_.Merge(std::move(mquery)); } std::string SQLParser::parseJoinedFieldName(tokenizer &parser, std::string &name) { @@ -1025,11 +1068,11 @@ void SQLParser::parseJoinEntries(tokenizer &parser, const std::string &mainNs, J while (!parser.end()) { auto tok = peekSqlToken(parser, OpSqlToken); if (tok.text() == "or"sv) { - jquery.nextOp_ = OpOr; + jquery.Or(); parser.next_token(); tok = parser.peek_token(); } else if (tok.text() == "and"sv) { - jquery.nextOp_ = OpAnd; + jquery.And(); parser.next_token(); tok = parser.peek_token(); } @@ -1039,35 +1082,32 @@ void SQLParser::parseJoinEntries(tokenizer &parser, const std::string &mainNs, J return; } - QueryJoinEntry je; - std::string ns1 = mainNs, ns2 = jquery._namespace; - std::string idx1 = parseJoinedFieldName(parser, ns1); - je.condition_ = getCondType(parser.next_token().text()); - std::string idx2 = parseJoinedFieldName(parser, ns2); - - if (ns1 == mainNs && ns2 == jquery._namespace) { - je.index_ = std::move(idx1); - je.joinIndex_ = std::move(idx2); - } else if (ns2 == mainNs && ns1 == jquery._namespace) { - je.index_ = std::move(idx2); - je.joinIndex_ = std::move(idx1); - je.condition_ = InvertJoinCondition(je.condition_); - je.reverseNamespacesOrder = true; - } else { - throw Error(errParseSQL, "Unexpected tables with ON statement: ('%s' and '%s') but expected ('%s' and '%s'), %s", ns1, ns2, - mainNs, jquery._namespace, parser.where()); + std::string ns1 = mainNs, ns2 = jquery.NsName(); + std::string fld1 = parseJoinedFieldName(parser, ns1); + CondType condition = getCondType(parser.next_token().text()); + std::string fld2 = parseJoinedFieldName(parser, ns2); + bool reverseNamespacesOrder{false}; + + if (ns1 != mainNs || ns2 != jquery.NsName()) { + if (ns2 == mainNs && ns1 == jquery.NsName()) { + std::swap(fld1, fld2); + condition = InvertJoinCondition(condition); + reverseNamespacesOrder = true; + } else { + throw Error(errParseSQL, "Unexpected tables with ON statement: ('%s' and '%s') but expected ('%s' and '%s'), %s", ns1, ns2, + mainNs, jquery.NsName(), parser.where()); + } } - je.op_ = jquery.nextOp_; - jquery.nextOp_ = OpAnd; - jquery.joinEntries_.emplace_back(std::move(je)); + jquery.joinEntries_.emplace_back(jquery.NextOp(), condition, std::move(fld1), std::move(fld2), reverseNamespacesOrder); + jquery.And(); if (!braces) { return; } } } CondType SQLParser::getCondType(std::string_view cond) { - if (cond == "="sv || cond == "=="sv || cond == "is"sv) { + if (cond == "="sv || cond == "=="sv || iequals(cond, "is"sv)) { return CondEq; } else if (cond == ">"sv) { return CondGt; diff --git a/cpp_src/core/query/sql/sqlparser.h b/cpp_src/core/query/sql/sqlparser.h index 2c1d17047..b7f01f4f7 100644 --- a/cpp_src/core/query/sql/sqlparser.h +++ b/cpp_src/core/query/sql/sqlparser.h @@ -13,16 +13,16 @@ class JoinedQuery; struct SortingEntries; class UpdateEntry; using EqualPosition_t = h_vector; + class SQLParser { public: - explicit SQLParser(Query &q) noexcept : query_(q) {} - /// Parses pure sql select query and initializes Query object data members as a result. /// @param q - sql query. - /// @return always returns 0. - int Parse(std::string_view q); + /// @return parsed query + [[nodiscard]] static Query Parse(std::string_view sql); protected: + explicit SQLParser(Query &q) noexcept : query_(q) {} /// Sql parser context struct SqlParsingCtx { struct SuggestionData { @@ -89,6 +89,8 @@ class SQLParser { /// Parse where entries int parseWhere(tokenizer &parser); + template + void parseWhereCondition(tokenizer &, T &&firstArg, OpType); /// Parse order by int parseOrderBy(tokenizer &parser, SortingEntries &, std::vector &forcedSortOrder); @@ -114,6 +116,8 @@ class SQLParser { /// Parse merge entries void parseMerge(tokenizer &parser); + Query parseSubQuery(tokenizer &); + static CondType getCondType(std::string_view cond); SqlParsingCtx ctx_; Query &query_; diff --git a/cpp_src/core/query/sql/sqlsuggester.cc b/cpp_src/core/query/sql/sqlsuggester.cc index ffd299323..5a0b71317 100644 --- a/cpp_src/core/query/sql/sqlsuggester.cc +++ b/cpp_src/core/query/sql/sqlsuggester.cc @@ -1,7 +1,7 @@ #include "sqlsuggester.h" #include "core/namespacedef.h" -#include "core/schema.h" +#include "core/query/query.h" #include "sqltokentype.h" #include @@ -9,27 +9,32 @@ namespace reindexer { -bool checkIfTokenStartsWith(std::string_view src, std::string_view pattern) { +static bool checkIfTokenStartsWith(std::string_view src, std::string_view pattern) { return checkIfStartsWith(src, pattern) && src.length() < pattern.length(); } std::vector SQLSuggester::GetSuggestions(std::string_view q, size_t pos, EnumNamespacesF enumNamespaces, GetSchemaF getSchema) { - ctx_.suggestionsPos = pos; - ctx_.autocompleteMode = true; - enumNamespaces_ = std::move(enumNamespaces); - getSchema_ = std::move(getSchema); + Query query; + SQLSuggester suggester{query}; + suggester.ctx_.suggestionsPos = pos; + suggester.ctx_.autocompleteMode = true; + suggester.enumNamespaces_ = std::move(enumNamespaces); + suggester.getSchema_ = std::move(getSchema); try { - Parse(q); + tokenizer tokens{q}; + (void)(suggester.Parse(tokens)); + // NOLINTBEGIN(bugprone-empty-catch) } catch (const Error &) { } + // NOLINTEND(bugprone-empty-catch) - for (SqlParsingCtx::SuggestionData &item : ctx_.suggestions) { - checkForTokenSuggestions(item); + for (SqlParsingCtx::SuggestionData &item : suggester.ctx_.suggestions) { + suggester.checkForTokenSuggestions(item); } - for (auto &it : ctx_.suggestions) { + for (auto &it : suggester.ctx_.suggestions) { if (!it.variants.empty()) { return it.variants; } diff --git a/cpp_src/core/query/sql/sqlsuggester.h b/cpp_src/core/query/sql/sqlsuggester.h index f0e0feebf..5ece47455 100644 --- a/cpp_src/core/query/sql/sqlsuggester.h +++ b/cpp_src/core/query/sql/sqlsuggester.h @@ -2,9 +2,7 @@ #include #include "core/schema.h" -#include "estl/fast_hash_map.h" #include "sqlparser.h" -#include "tools/stringstools.h" namespace reindexer { @@ -21,10 +19,10 @@ class SQLSuggester : public SQLParser { /// @param pos - pos of cursor in query. /// @param enumNamespaces - functor which enums namespaces to be checked for existing fields. /// @param getSchemaSuggestions - functor which get pointer to namespace's schema - std::vector GetSuggestions(std::string_view q, size_t pos, EnumNamespacesF enumNamespaces, - GetSchemaF getSchemaSuggestions); + [[nodiscard]] static std::vector GetSuggestions(std::string_view q, size_t pos, EnumNamespacesF enumNamespaces, + GetSchemaF getSchemaSuggestions); -protected: +private: /// Finds suggestions for token /// @param ctx - suggestion context. void getSuggestionsForToken(SqlParsingCtx::SuggestionData &ctx); @@ -33,11 +31,11 @@ class SQLSuggester : public SQLParser { void checkForTokenSuggestions(SqlParsingCtx::SuggestionData &data); /// Tries to find token value among accepted tokens. - bool findInPossibleTokens(int type, const std::string &v); + [[nodiscard]] bool findInPossibleTokens(int type, const std::string &v); /// Tries to find token value among indexes. - bool findInPossibleFields(const std::string &tok); + [[nodiscard]] bool findInPossibleFields(const std::string &tok); /// Tries to find among possible namespaces. - bool findInPossibleNamespaces(const std::string &tok); + [[nodiscard]] bool findInPossibleNamespaces(const std::string &tok); /// Gets names of indexes that start with 'token'. void getMatchingFieldsNames(const std::string &token, std::vector &variants); void getMatchingNamespacesNames(const std::string &token, std::vector &variants); diff --git a/cpp_src/core/querycache.h b/cpp_src/core/querycache.h index 4d98bd1d2..a9c0c4c14 100644 --- a/cpp_src/core/querycache.h +++ b/cpp_src/core/querycache.h @@ -8,9 +8,9 @@ namespace reindexer { -struct QueryTotalCountCacheVal { - QueryTotalCountCacheVal() = default; - QueryTotalCountCacheVal(size_t total) noexcept : total_count(total) {} +struct QueryCountCacheVal { + QueryCountCacheVal() = default; + QueryCountCacheVal(size_t total) noexcept : total_count(total) {} size_t Size() const noexcept { return 0; } @@ -49,9 +49,8 @@ struct HashQueryCacheKey { } }; -struct QueryTotalCountCache : LRUCache { - QueryTotalCountCache(size_t sizeLimit = kDefaultCacheSizeLimit, int hitCount = kDefaultHitCountToCache) - : LRUCache(sizeLimit, hitCount) {} -}; +using QueryCountCache = LRUCache; + +; } // namespace reindexer diff --git a/cpp_src/core/queryresults/aggregationresult.h b/cpp_src/core/queryresults/aggregationresult.h index 0f8259a82..9e0c8b1e6 100644 --- a/cpp_src/core/queryresults/aggregationresult.h +++ b/cpp_src/core/queryresults/aggregationresult.h @@ -151,4 +151,4 @@ struct AggregationResult { std::optional value_ = std::nullopt; }; -}; // namespace reindexer +} // namespace reindexer diff --git a/cpp_src/core/queryresults/localqueryresults.cc b/cpp_src/core/queryresults/localqueryresults.cc index d568bacf0..23e5754b6 100644 --- a/cpp_src/core/queryresults/localqueryresults.cc +++ b/cpp_src/core/queryresults/localqueryresults.cc @@ -43,13 +43,19 @@ void LocalQueryResults::RemoveNamespace(const NamespaceImpl *ns) { struct LocalQueryResults::Context { Context() = default; - Context(PayloadType type, TagsMatcher tagsMatcher, const FieldsSet &fieldsFilter, std::shared_ptr schema) - : type_(std::move(type)), tagsMatcher_(std::move(tagsMatcher)), fieldsFilter_(fieldsFilter), schema_(std::move(schema)) {} + Context(PayloadType type, TagsMatcher tagsMatcher, const FieldsSet &fieldsFilter, std::shared_ptr schema, + lsn_t nsIncarnationTag) + : type_(std::move(type)), + tagsMatcher_(std::move(tagsMatcher)), + fieldsFilter_(fieldsFilter), + schema_(std::move(schema)), + nsIncarnationTag_(nsIncarnationTag) {} PayloadType type_; TagsMatcher tagsMatcher_; FieldsSet fieldsFilter_; std::shared_ptr schema_; + lsn_t nsIncarnationTag_; }; static_assert(LocalQueryResults::kSizeofContext >= sizeof(LocalQueryResults::Context), @@ -107,6 +113,16 @@ h_vector LocalQueryResults::GetNamespaces() const { return ret; } +NsShardsIncarnationTags LocalQueryResults::GetIncarnationTags() const { + NsShardsIncarnationTags ret; + auto &shardTags = ret.emplace_back(); + shardTags.tags.reserve(ctxs.size()); + for (auto &ctx : ctxs) { + shardTags.tags.emplace_back(ctx.nsIncarnationTag_); + } + return ret; +} + int LocalQueryResults::GetJoinedNsCtxIndex(int nsid) const noexcept { int ctxIndex = joined_.size(); for (int ns = 0; ns < nsid; ++ns) { @@ -117,7 +133,7 @@ int LocalQueryResults::GetJoinedNsCtxIndex(int nsid) const noexcept { class LocalQueryResults::EncoderDatasourceWithJoins : public IEncoderDatasourceWithJoins { public: - EncoderDatasourceWithJoins(const joins::ItemIterator &joinedItemIt, const ContextsVector &ctxs, int ctxIdx) + EncoderDatasourceWithJoins(const joins::ItemIterator &joinedItemIt, const ContextsVector &ctxs, int ctxIdx) noexcept : joinedItemIt_(joinedItemIt), ctxs_(ctxs), ctxId_(ctxIdx) {} ~EncoderDatasourceWithJoins() override = default; @@ -379,27 +395,17 @@ Item LocalQueryResults::Iterator::GetItem(bool enableHold) { return item; } -LocalQueryResults::Iterator &LocalQueryResults::Iterator::operator++() noexcept { - idx_++; - return *this; -} -LocalQueryResults::Iterator &LocalQueryResults::Iterator::operator+(int val) noexcept { - idx_ += val; - return *this; -} - -bool LocalQueryResults::Iterator::operator!=(const Iterator &other) const noexcept { return idx_ != other.idx_; } -bool LocalQueryResults::Iterator::operator==(const Iterator &other) const noexcept { return idx_ == other.idx_; } - void LocalQueryResults::AddItem(Item &item, bool withData, bool enableHold) { auto ritem = item.impl_; if (item.GetID() != -1) { + auto ns = ritem->GetNamespace(); if (ctxs.empty()) { - ctxs.push_back(Context(ritem->Type(), ritem->tagsMatcher(), FieldsSet(), ritem->GetSchema())); + ctxs.emplace_back(ritem->Type(), ritem->tagsMatcher(), FieldsSet(), ritem->GetSchema(), + ns ? ns->ns_->incarnationTag_ : lsn_t()); } Add(ItemRef(item.GetID(), withData ? (ritem->RealValue().IsFree() ? ritem->Value() : ritem->RealValue()) : PayloadValue())); if (withData && enableHold) { - if (auto ns{ritem->GetNamespace()}; ns) { + if (ns) { Payload{ns->ns_->payloadType_, items_.back().Value()}.CopyStrings(stringsHolder_); } else { assertrx(ctxs.size() == 1); @@ -426,13 +432,16 @@ int LocalQueryResults::getNsNumber(int nsid) const noexcept { return ctxs[nsid].schema_->GetProtobufNsNumber(); } -int LocalQueryResults::getMergedNSCount() const noexcept { return ctxs.size(); } - void LocalQueryResults::addNSContext(const PayloadType &type, const TagsMatcher &tagsMatcher, const FieldsSet &filter, - std::shared_ptr schema) { - if (filter.getTagsPathsLength()) nonCacheableData = true; + std::shared_ptr schema, lsn_t nsIncarnationTag) { + nonCacheableData = nonCacheableData || filter.getTagsPathsLength(); + + ctxs.emplace_back(type, tagsMatcher, filter, std::move(schema), std::move(nsIncarnationTag)); +} - ctxs.push_back(Context(type, tagsMatcher, filter, std::move(schema))); +void LocalQueryResults::addNSContext(const QueryResults &baseQr, size_t nsid, lsn_t nsIncarnationTag) { + addNSContext(baseQr.GetPayloadType(nsid), baseQr.GetTagsMatcher(nsid), baseQr.GetFieldsFilter(nsid), baseQr.GetSchema(nsid), + std::move(nsIncarnationTag)); } LocalQueryResults::NsDataHolder::NsDataHolder(LocalQueryResults::NamespaceImplPtr &&_ns, StringsHolderPtr &&strHldr) noexcept diff --git a/cpp_src/core/queryresults/localqueryresults.h b/cpp_src/core/queryresults/localqueryresults.h index a64994ff6..9b6171cf7 100644 --- a/cpp_src/core/queryresults/localqueryresults.h +++ b/cpp_src/core/queryresults/localqueryresults.h @@ -2,6 +2,7 @@ #include "aggregationresult.h" #include "core/item.h" +#include "core/namespace/incarnationtags.h" #include "core/namespace/stringsholder.h" #include "core/payload/payloadvalue.h" #include "core/rdxcontext.h" @@ -52,10 +53,13 @@ class LocalQueryResults { void Erase(ItemRefVector::iterator begin, ItemRefVector::iterator end); size_t Count() const noexcept { return items_.size(); } size_t TotalCount() const noexcept { return totalCount; } - const std::string &GetExplainResults() const { return explainResults; } - const std::vector &GetAggregationResults() const { return aggregationResults; } + const std::string &GetExplainResults() const & noexcept { return explainResults; } + const std::string &GetExplainResults() const &&; + const std::vector &GetAggregationResults() const & noexcept { return aggregationResults; } + const std::vector &GetAggregationResults() const && = delete; void Clear(); h_vector GetNamespaces() const; + NsShardsIncarnationTags GetIncarnationTags() const; bool IsCacheEnabled() const { return !nonCacheableData; } void SetOutputShardId(int shardId) noexcept { outputShardId = shardId; } CsvOrdering MakeCSVTagOrdering(unsigned limit, unsigned offset) const; @@ -75,11 +79,17 @@ class LocalQueryResults { lsn_t GetLSN() const noexcept { return qr_->items_[idx_].Value().GetLSN(); } bool IsRaw() const noexcept; std::string_view GetRaw() const noexcept; - Iterator &operator++() noexcept; - Iterator &operator+(int delta) noexcept; + Iterator &operator++() noexcept { + idx_++; + return *this; + } + Iterator &operator+(int delta) noexcept { + idx_ += delta; + return *this; + } const Error &Status() const noexcept { return err_; } - bool operator!=(const Iterator &) const noexcept; - bool operator==(const Iterator &) const noexcept; + bool operator!=(const Iterator &other) const noexcept { return idx_ != other.idx_; } + bool operator==(const Iterator &other) const noexcept { return idx_ == other.idx_; } Iterator &operator*() noexcept { return *this; } const LocalQueryResults *qr_; @@ -101,7 +111,8 @@ class LocalQueryResults { struct Context; // precalc context size - static constexpr int kSizeofContext = 264; // sizeof(PayloadType) + sizeof(TagsMatcher) + sizeof(FieldsSet) + sizeof(shared_ptr); + // sizeof(PayloadType) + sizeof(TagsMatcher) + sizeof(FieldsSet) + sizeof(shared_ptr) + sizeof(int64); + static constexpr int kSizeofContext = 272; // Order of storing contexts for namespaces: // [0] - main NS context @@ -111,7 +122,8 @@ class LocalQueryResults { ContextsVector ctxs; void addNSContext(const PayloadType &type, const TagsMatcher &tagsMatcher, const FieldsSet &fieldsFilter, - std::shared_ptr schema); + std::shared_ptr schema, lsn_t nsIncarnationTag); + void addNSContext(const QueryResults &baseQr, size_t nsid, lsn_t nsIncarnationTag); const TagsMatcher &getTagsMatcher(int nsid) const noexcept; const PayloadType &getPayloadType(int nsid) const noexcept; const FieldsSet &getFieldsFilter(int nsid) const noexcept; @@ -119,7 +131,7 @@ class LocalQueryResults { PayloadType &getPayloadType(int nsid) noexcept; std::shared_ptr getSchema(int nsid) const noexcept; int getNsNumber(int nsid) const noexcept; - int getMergedNSCount() const noexcept; + int getMergedNSCount() const noexcept { return ctxs.size(); } ItemRefVector &Items() noexcept { return items_; } const ItemRefVector &Items() const noexcept { return items_; } int GetJoinedNsCtxIndex(int nsid) const noexcept; diff --git a/cpp_src/core/queryresults/queryresults.cc b/cpp_src/core/queryresults/queryresults.cc index eb10d928a..a1ef6452e 100644 --- a/cpp_src/core/queryresults/queryresults.cc +++ b/cpp_src/core/queryresults/queryresults.cc @@ -257,25 +257,7 @@ void QueryResults::RebuildMergedData() { void QueryResults::Clear() { *this = QueryResults(); } -int QueryResults::GetMergedNSCount() const noexcept { - switch (type_) { - case Type::None: { - return 0; - } - case Type::Local: { - return local_->qr.getMergedNSCount(); - } - case Type::SingleRemote: { - return remote_[0].qr.GetMergedNSCount(); - } - case Type::MultipleRemote: - case Type::Mixed: - default: - return 1; // No joined/merged nss in distributed qr - } -} - -const std::vector& QueryResults::GetAggregationResults() { +const std::vector& QueryResults::GetAggregationResults() & { switch (type_) { case Type::None: { static std::vector kEmpty; @@ -450,10 +432,10 @@ void QueryResults::SetQuery(const Query* q) { if (q) { QueryData data; data.isWalQuery = q->IsWALQuery(); - data.joinedSize = uint16_t(q->joinQueries_.size()); - data.mergedJoinedSizes.reserve(q->mergeQueries_.size()); - for (const auto& mq : q->mergeQueries_) { - data.mergedJoinedSizes.emplace_back(mq.joinQueries_.size()); + data.joinedSize = uint16_t(q->GetJoinQueries().size()); + data.mergedJoinedSizes.reserve(q->GetMergeQueries().size()); + for (const auto& mq : q->GetMergeQueries()) { + data.mergedJoinedSizes.emplace_back(mq.GetJoinQueries().size()); } qData_.emplace(std::move(data)); } else { @@ -921,7 +903,7 @@ class QueryResults::Comparator { if (expr.ByField()) { int index = IndexValueType::SetByJsonPath; std::string field; - if (ns.getIndexByName(se.expression, index) && index < ns.indexes_.firstCompositePos() && + if (ns.tryGetIndexByName(se.expression, index) && index < ns.indexes_.firstCompositePos() && ns.indexes_[index]->Opts().IsSparse()) { const auto& fields = ns.indexes_[index]->Fields(); assertrx(fields.getJsonPathsLength() == 1); @@ -1014,8 +996,8 @@ void QueryResults::SetOrdering(const Query& q, const NamespaceImpl& ns, const Rd Comparator comparator{*this, q, ns}; lock.unlock(); orderedQrs_ = std::make_unique>(std::move(comparator)); - limit = q.count; - offset = q.start; + limit = q.Limit(); + offset = q.Offset(); } } diff --git a/cpp_src/core/queryresults/queryresults.h b/cpp_src/core/queryresults/queryresults.h index 42ce467c5..b2cccb9c4 100644 --- a/cpp_src/core/queryresults/queryresults.h +++ b/cpp_src/core/queryresults/queryresults.h @@ -4,6 +4,7 @@ #include #include "client/queryresults.h" #include "core/itemimplrawdata.h" +#include "core/namespace/incarnationtags.h" #include "localqueryresults.h" namespace reindexer_server { @@ -130,7 +131,7 @@ class QueryResults { return local_->qr; } int Flags() const noexcept { return flags_; } - const std::string &GetExplainResults() { + const std::string &GetExplainResults() & { switch (type_) { case Type::Local: return local_->qr.GetExplainResults(); @@ -155,11 +156,90 @@ class QueryResults { } } } - int GetMergedNSCount() const noexcept; - const std::vector &GetAggregationResults(); + const std::string &GetExplainResults() && = delete; + int GetMergedNSCount() const noexcept { + switch (type_) { + case Type::None: { + return 0; + } + case Type::Local: { + return local_->qr.getMergedNSCount(); + } + case Type::SingleRemote: { + return remote_[0].qr.GetMergedNSCount(); + } + case Type::MultipleRemote: + case Type::Mixed: + default: + return 1; // No joined/merged nss in distributed qr + } + } + const std::vector &GetAggregationResults() &; + const std::vector &GetAggregationResults() && = delete; h_vector GetNamespaces() const; + NsShardsIncarnationTags GetIncarnationTags() const { + NsShardsIncarnationTags ret; + switch (type_) { + case Type::None: + return ret; + case Type::Local: { + auto localTags = local_->qr.GetIncarnationTags(); + if (localTags.empty()) { + return ret; + } + if (localTags.size() != 1) { + throw Error(errLogic, "Unexpected shards count in the local query results"); + } + localTags[0].shardId = local_->shardID; + ret.emplace_back(std::move(localTags[0])); + return ret; + } + case Type::SingleRemote: { + auto &remote = remote_[0]; + auto &remoteTags = remote.qr.GetIncarnationTags(); + if (remoteTags.empty()) { + return ret; + } + if (remoteTags.size() != 1) { + throw Error(errLogic, "Unexpected shards count in the remote query results"); + } + auto &tags = ret.emplace_back(remoteTags[0]); + tags.shardId = remote.shardID; + return ret; + } + case Type::Mixed: { + auto localTags = local_->qr.GetIncarnationTags(); + if (!localTags.empty()) { + if (localTags.size() != 1) { + throw Error(errLogic, "Unexpected shards count in the local query results"); + } + localTags[0].shardId = local_->shardID; + ret.emplace_back(std::move(localTags[0])); + } + } + [[fallthrough]]; + case Type::MultipleRemote: + for (auto &r : remote_) { + auto &remoteTags = r.qr.GetIncarnationTags(); + if (remoteTags.empty()) { + continue; + } + if (remoteTags.size() != 1) { + throw Error(errLogic, "Unexpected shards count in the remote query results"); + } + auto &tags = ret.emplace_back(remoteTags[0]); + tags.shardId = r.shardID; + } + return ret; + } + throw Error(errLogic, "Unknown query results type"); + } bool IsCacheEnabled() const noexcept; int64_t GetShardingConfigVersion() const noexcept { return shardingConfigVersion_; } + void SetShardingConfigVersion(int64_t v) noexcept { + assertrx_dbg(shardingConfigVersion_ == ShardingSourceId::NotSet); // Do not set version multiple times + shardingConfigVersion_ = v; + } bool IsLocal() const noexcept { return type_ == Type::Local; } bool HasProxiedResults() const noexcept { return type_ == Type::SingleRemote || type_ == Type::Mixed || type_ == Type::MultipleRemote; } bool IsDistributed() const noexcept { return type_ == Type::Mixed || type_ == Type::MultipleRemote; } @@ -435,7 +515,7 @@ class QueryResults { h_vector mergedJoinedSizes; }; - int64_t shardingConfigVersion_ = -1; + int64_t shardingConfigVersion_ = ShardingSourceId::NotSet; std::unique_ptr mergedData_; // Merged data of distributed query results std::unique_ptr> local_; std::deque> remote_; diff --git a/cpp_src/core/queryresults/tableviewbuilder.h b/cpp_src/core/queryresults/tableviewbuilder.h index 017f98d5d..cb1bac11c 100644 --- a/cpp_src/core/queryresults/tableviewbuilder.h +++ b/cpp_src/core/queryresults/tableviewbuilder.h @@ -2,16 +2,14 @@ #include #include +#include #include #include #include #include #include "estl/span.h" -#include "tools/errors.h" #include "tools/terminalutils.h" -#include - namespace reindexer { struct ColumnData { diff --git a/cpp_src/core/rdxcontext.h b/cpp_src/core/rdxcontext.h index dec13bc3f..bbc115af4 100644 --- a/cpp_src/core/rdxcontext.h +++ b/cpp_src/core/rdxcontext.h @@ -20,23 +20,24 @@ struct IRdxCancelContext { virtual ~IRdxCancelContext() = default; }; +constexpr std::string_view kDefaultTimeoutError = "Context timeout"; +constexpr std::string_view kDefaultCancelError = "Context was canceled"; + template void ThrowOnCancel(const Context& ctx, std::string_view errMsg = std::string_view()) { if (!ctx.IsCancelable()) return; - using namespace std::string_view_literals; - auto cancel = ctx.CheckCancel(); + const auto cancel = ctx.CheckCancel(); switch (cancel) { case CancelType::Explicit: - throw Error(errCanceled, errMsg.empty() ? "Request was canceled by context"sv : errMsg); + throw Error(errCanceled, errMsg.empty() ? kDefaultCancelError : errMsg); case CancelType::Timeout: - throw Error(errTimeout, errMsg.empty() ? "Request was canceled on timeout"sv : errMsg); + throw Error(errTimeout, errMsg.empty() ? kDefaultTimeoutError : errMsg); case CancelType::None: return; - default: - assertrx(false); - throw Error(errCanceled, errMsg.empty() ? "Request was canceled by unknown reason"sv : errMsg); } + assertrx(false); + throw Error(errCanceled, errMsg.empty() ? kDefaultCancelError : errMsg); } class RdxDeadlineContext : public IRdxCancelContext { @@ -242,12 +243,11 @@ class InternalRdxContext { InternalRdxContext WithShardId(unsigned int id, bool parallel) const noexcept { return InternalRdxContext(cmpl_, deadlineCtx_, activityTracer_, user_, connectionId_, lsn_, emmiterServerId_, id, parallel); } - InternalRdxContext WithContextParams(milliseconds timeout, lsn_t lsn, int emmiterServerId, unsigned int shardId, - bool distributed) const { + InternalRdxContext WithContextParams(milliseconds timeout, lsn_t lsn, int emmiterServerId, int shardId, bool distributed) const { return InternalRdxContext(cmpl_, RdxDeadlineContext(timeout, deadlineCtx_.parent()), activityTracer_, user_, connectionId_, lsn, emmiterServerId, shardId, distributed); } - InternalRdxContext WithContextParams(milliseconds timeout, lsn_t lsn, int emmiterServerId, unsigned int shardId, bool distributed, + InternalRdxContext WithContextParams(milliseconds timeout, lsn_t lsn, int emmiterServerId, int shardId, bool distributed, std::string_view activityTracer, std::string&& user, int connectionId) const { return activityTracer.empty() ? InternalRdxContext(cmpl_, RdxDeadlineContext(timeout, deadlineCtx_.parent()), activityTracer_, user_, connectionId_, @@ -257,6 +257,15 @@ class InternalRdxContext { : std::string(activityTracer_).append("/").append(activityTracer), std::move(user), connectionId, lsn, emmiterServerId, shardId, distributed); } + InternalRdxContext WithContextParams(milliseconds timeout, std::string_view activityTracer, std::string&& user) const { + return activityTracer.empty() + ? InternalRdxContext(cmpl_, RdxDeadlineContext(timeout, deadlineCtx_.parent()), activityTracer_, user_, connectionId_, + lsn_, emmiterServerId_, shardId_, shardingParallelExecution_) + : InternalRdxContext(cmpl_, RdxDeadlineContext(timeout, deadlineCtx_.parent()), + activityTracer_.empty() ? std::string(activityTracer) + : std::string(activityTracer_).append("/").append(activityTracer), + std::move(user), connectionId_, lsn_, emmiterServerId_, shardId_, shardingParallelExecution_); + } void SetActivityTracer(std::string&& activityTracer, std::string&& user, int connectionId = kNoConnectionId) noexcept { activityTracer_ = std::move(activityTracer); user_ = std::move(user); diff --git a/cpp_src/core/reindexer.cc b/cpp_src/core/reindexer.cc index 0eb8d0379..7e262ba8f 100644 --- a/cpp_src/core/reindexer.cc +++ b/cpp_src/core/reindexer.cc @@ -45,10 +45,6 @@ bool Reindexer::NeedTraceActivity() const noexcept { return impl_->NeedTraceActi Error Reindexer::Connect(const std::string& dsn, ConnectOpts opts) { return impl_->Connect(dsn, opts); } -Error Reindexer::EnableStorage(const std::string& storagePath, bool skipPlaceholderCheck) { - const auto rdxCtx = impl_->CreateRdxContext(ctx_, [&](WrSerializer& s) { s << "ENABLE STORAGE "sv << storagePath; }); - return impl_->EnableStorage(storagePath, skipPlaceholderCheck, rdxCtx); -} Error Reindexer::AddNamespace(const NamespaceDef& nsDef, const NsReplicationOpts& replOpts) { if (!validateUserNsName(nsDef.name)) { return Error(errParams, "Namespace name contains invalid character. Only alphas, digits,'_','-', are allowed"); @@ -265,4 +261,11 @@ Error Reindexer::DumpIndex(std::ostream& os, std::string_view nsName, std::strin return impl_->ShardingControlRequest(request, impl_->CreateRdxContext(ctx_, [&](WrSerializer& s) { s << "SHARDING CONTROL REQUEST"; })); } +// REINDEX_WITH_V3_FOLLOWERS +Error Reindexer::SubscribeUpdates(IUpdatesObserver* observer, const UpdatesFilters& filters, SubscriptionOpts opts) { + return impl_->SubscribeUpdates(observer, filters, opts); +} +Error Reindexer::UnsubscribeUpdates(IUpdatesObserver* observer) { return impl_->UnsubscribeUpdates(observer); } +// REINDEX_WITH_V3_FOLLOWERS + } // namespace reindexer diff --git a/cpp_src/core/reindexer.h b/cpp_src/core/reindexer.h index b6d7e89f6..2d22a37c6 100644 --- a/cpp_src/core/reindexer.h +++ b/cpp_src/core/reindexer.h @@ -22,6 +22,10 @@ class Snapshot; struct SnapshotOpts; struct ClusterControlRequestData; +// REINDEX_WITH_V3_FOLLOWERS +class IUpdatesObserver; +class UpdatesFilters; + namespace cluster { struct NodeData; struct RaftInfo; @@ -64,11 +68,6 @@ class Reindexer { /// ConnectOpts::OpenNamespaces() - true: Need to open all the namespaces; false: Don't open namespaces Error Connect(const std::string &dsn, ConnectOpts opts = ConnectOpts()); - /// Enable storage. Must be called before InitSystemNamespaces - /// @param storagePath - file system path to database storage - /// @param skipPlaceholderCheck - If set, then reindexer will not check folder for placeholder - Error EnableStorage(const std::string &storagePath, bool skipPlaceholderCheck = false); - /// Open or create namespace /// @param nsName - Name of namespace /// @param opts - Storage options. Can be one of
@@ -333,7 +332,7 @@ class Reindexer { /// @param emmiterServerId - server ID of the emmiter node (required for synchronously replicated requests) /// @param shardId - expected shard ID for this node (non empty for proxied sharding requests) /// @param distributed - 'true' means, that we are executing distributed sharding query part - Reindexer WithContextParams(milliseconds timeout, lsn_t lsn, int emmiterServerId, unsigned int shardId, bool distributed) const { + Reindexer WithContextParams(milliseconds timeout, lsn_t lsn, int emmiterServerId, int shardId, bool distributed) const { return Reindexer(impl_, ctx_.WithContextParams(timeout, lsn, emmiterServerId, shardId, distributed)); } /// Allows to set multiple context params at once @@ -345,11 +344,18 @@ class Reindexer { /// @param activityTracer - name of activity tracer /// @param user - user identifying information /// @param connectionId - unique identifier for the connection - Reindexer WithContextParams(milliseconds timeout, lsn_t lsn, int emmiterServerId, unsigned int shardId, bool distributed, + Reindexer WithContextParams(milliseconds timeout, lsn_t lsn, int emmiterServerId, int shardId, bool distributed, std::string_view activityTracer, std::string user, int connectionId) const { return Reindexer(impl_, ctx_.WithContextParams(timeout, lsn, emmiterServerId, shardId, distributed, activityTracer, std::move(user), connectionId)); } + /// Allows to set multiple context params at once + /// @param timeout - Execution timeout + /// @param activityTracer - name of activity tracer + /// @param user - user identifying information + Reindexer WithContextParams(milliseconds timeout, std::string_view activityTracer, std::string user) const { + return Reindexer(impl_, ctx_.WithContextParams(timeout, activityTracer, std::move(user))); + } /// Set activityTracer to current DB /// @param activityTracer - name of activity tracer @@ -371,6 +377,19 @@ class Reindexer { Error DumpIndex(std::ostream &os, std::string_view nsName, std::string_view index); + /// REINDEX_WITH_V3_FOLLOWERS + /// THIS METHOD IS TEMPORARY AND WILL BE REMOVED + /// Subscribe to updates of database + /// @param observer - Observer interface, which will receive updates + /// @param filters - Subscription filters set + /// @param opts - Subscription options (allows to either add new filters or reset them) + Error SubscribeUpdates(IUpdatesObserver *observer, const UpdatesFilters &filters, SubscriptionOpts opts = SubscriptionOpts()); + /// THIS METHOD IS TEMPORARY AND WILL BE REMOVED + /// Unsubscribe from updates of database + /// Cancelation context doesn't affect this call + /// @param observer - Observer interface, which will be unsubscribed updates + Error UnsubscribeUpdates(IUpdatesObserver *observer); + private: Reindexer(ShardingProxy *impl, InternalRdxContext &&ctx) noexcept : impl_(impl), owner_(false), ctx_(std::move(ctx)) {} diff --git a/cpp_src/core/reindexerimpl.cc b/cpp_src/core/reindexer_impl/reindexerimpl.cc similarity index 81% rename from cpp_src/core/reindexerimpl.cc rename to cpp_src/core/reindexer_impl/reindexerimpl.cc index dccb240fe..83106906f 100644 --- a/cpp_src/core/reindexerimpl.cc +++ b/cpp_src/core/reindexer_impl/reindexerimpl.cc @@ -1,24 +1,25 @@ -#include "core/reindexerimpl.h" +#include "reindexerimpl.h" #include #include #include -#include "cjson/jsonbuilder.h" #include "cluster/clustercontrolrequest.h" #include "cluster/clusterizator.h" -#include "core/cjson/jsondecoder.h" +#include "core/cjson/jsonbuilder.h" #include "core/cjson/protobufschemabuilder.h" +#include "core/defnsconfigs.h" #include "core/iclientsstats.h" #include "core/index/index.h" #include "core/itemimpl.h" #include "core/nsselecter/crashqueryreporter.h" +#include "core/nsselecter/nsselecter.h" +#include "core/nsselecter/querypreprocessor.h" #include "core/query/sql/sqlsuggester.h" +#include "core/queryresults/joinresults.h" #include "core/selectfunc/selectfunc.h" #include "core/type_consts_helpers.h" -#include "defnsconfigs.h" -#include "estl/contexted_locks.h" #include "estl/defines.h" -#include "queryresults/joinresults.h" +#include "rx_selector.h" #include "server/outputparameters.h" #include "tools/alloc_ext/tc_malloc_extension.h" #include "tools/catch_and_return.h" @@ -125,13 +126,13 @@ ReindexerImpl::StatsLocker::StatsLocker() { ReindexerImpl::StatsLocker::StatsLockT ReindexerImpl::StatsLocker::LockIfRequired(std::string_view sysNsName, const RdxContext& ctx) { auto found = mtxMap_.find(sysNsName); if (found != mtxMap_.end()) { - return StatsLockT(found->second, &ctx); + return StatsLockT(found->second, ctx); } // Do not create any lock, if namespace does not preset in the map return StatsLockT(); } -Error ReindexerImpl::EnableStorage(const std::string& storagePath, bool skipPlaceholderCheck, const RdxContext& ctx) { +Error ReindexerImpl::enableStorage(const std::string& storagePath) { if (!storagePath_.empty()) { return Error(errParams, "Storage already enabled"); } @@ -154,7 +155,7 @@ Error ReindexerImpl::EnableStorage(const std::string& storagePath, bool skipPlac if (entry.name == kConfigNamespace) isHaveConfig = true; } - if (!isEmpty && !skipPlaceholderCheck) { + if (!isEmpty) { std::string content; int res = fs::ReadFile(fs::JoinPath(storagePath, kStoragePlaceholderFilename), content); if (res > 0) { @@ -197,7 +198,7 @@ Error ReindexerImpl::EnableStorage(const std::string& storagePath, bool skipPlac Error res; if (isHaveConfig) { - res = openNamespace(kConfigNamespace, true, StorageOpts().Enabled().CreateIfMissing(), {}, ctx); + res = openNamespace(kConfigNamespace, true, StorageOpts().Enabled().CreateIfMissing(), {}, RdxContext()); } for (auto& watcher : configWatchers_) { watcher.SetDirectory(storagePath_); @@ -229,8 +230,6 @@ Error ReindexerImpl::Connect(const std::string& dsn, ConnectOpts opts) { path = dsn.substr(10); } - std::vector foundNs; - switch (opts.StorageType()) { case kStorageTypeOptLevelDB: storageType_ = StorageType::LevelDB; @@ -243,9 +242,10 @@ Error ReindexerImpl::Connect(const std::string& dsn, ConnectOpts opts) { autorepairEnabled_ = opts.IsAutorepair(); replicationEnabled_ = !opts.IsReplicationDisabled(); - bool enableStorage = (path.length() > 0 && path != "/"); - if (enableStorage) { - auto err = EnableStorage(path); + std::vector foundNs; + const bool storageEnable = (path.length() > 0 && path != "/"); + if (storageEnable) { + auto err = enableStorage(path); if (!err.ok()) return err; if (fs::ReadDir(path, foundNs) < 0) { return Error(errParams, "Can't read database dir %s", path); @@ -255,9 +255,10 @@ Error ReindexerImpl::Connect(const std::string& dsn, ConnectOpts opts) { Error err = InitSystemNamespaces(); if (!err.ok()) return err; - if (enableStorage && opts.IsOpenNamespaces()) { - std::sort(foundNs.begin(), foundNs.end(), - [](const fs::DirEntry& ld, const fs::DirEntry& rd) { return ld.internalFilesCount > rd.internalFilesCount; }); + if (storageEnable && opts.IsOpenNamespaces()) { + boost::sort::pdqsort_branchless(foundNs.begin(), foundNs.end(), [](const fs::DirEntry& ld, const fs::DirEntry& rd) noexcept { + return ld.internalFilesCount > rd.internalFilesCount; + }); const size_t maxLoadWorkers = ConcurrentNamespaceLoaders(); std::unique_ptr thrs(new std::thread[maxLoadWorkers]); std::atomic_flag hasNsErrors = ATOMIC_FLAG_INIT; @@ -269,7 +270,7 @@ Error ReindexerImpl::Connect(const std::string& dsn, ConnectOpts opts) { idx = nsIdx.fetch_add(1, std::memory_order_relaxed)) { auto& de = foundNs[idx]; if (de.isDir && validateObjectName(de.name, true)) { - if (de.name[0] == '@') { + if (de.name[0] == kTmpNsPrefix) { const std::string tmpPath = fs::JoinPath(storagePath_, de.name); logPrintf(LogWarning, "Dropping tmp namespace '%s'", de.name); if (fs::RmDirAll(tmpPath) < 0) { @@ -279,9 +280,9 @@ Error ReindexerImpl::Connect(const std::string& dsn, ConnectOpts opts) { } continue; } - auto status = openNamespace(de.name, true, StorageOpts().Enabled(), {}, RdxContext()); + RdxContext dummyCtx; + auto status = openNamespace(de.name, true, StorageOpts().Enabled(), {}, dummyCtx); if (status.ok()) { - RdxContext dummyCtx; auto ns = getNamespace(de.name, dummyCtx); auto replState = ns->GetReplStateV2(dummyCtx); int64_t maxVer = maxNsVersion.load(); @@ -305,6 +306,23 @@ Error ReindexerImpl::Connect(const std::string& dsn, ConnectOpts opts) { if (!opts.IsAllowNamespaceErrors() && hasNsErrors.test_and_set(std::memory_order_relaxed)) { return Error(errNotValid, "Namespaces load error"); } + + RdxContext dummyCtx; + auto nss = getNamespaces(dummyCtx); + for (auto& ns : nss) { + const auto replState = ns.second->GetReplState(dummyCtx); + if (replState.nsVersion.isEmpty()) { + // Do not set version for the v3 follower namespaces to guarantee force sync for them + if (!replState.wasV3ReplicatedNS) { + const auto nsVersion = nsVersion_.GetNext(); + logPrintf(LogTrace, "%s: Ns version was empty. Generating new one: %d", ns.first, nsVersion); + ns.second->SetNsVersion(nsVersion, dummyCtx); + } else { + logPrintf(LogTrace, "%s: Ns version was empty (namespaces was replicated in v3). Keeping empty value", ns.first); + } + } + } + logPrintf(LogTrace, "%s: All of the namespaces were opened", storagePath_); } @@ -357,8 +375,13 @@ Error ReindexerImpl::AddNamespace(const NamespaceDef& nsDef, std::optional(nsDef.name, replOpts.has_value() ? replOpts->tmStateToken : std::optional(), - clusterizator_.get(), bgDeleter_); + *clusterizator_, bgDeleter_, observers_); +#else // REINDEX_WITH_V3_FOLLOWERS + ns = std::make_shared(nsDef.name, replOpts.has_value() ? replOpts->tmStateToken : std::optional(), + *clusterizator_, bgDeleter_); +#endif // REINDEX_WITH_V3_FOLLOWERS if (nsDef.isTemporary) { ns->awaitMainNs(rdxCtx)->setTemporary(); } @@ -393,6 +416,9 @@ Error ReindexerImpl::AddNamespace(const NamespaceDef& nsDef, std::optionalReplicate( {UpdateRecord::Type::AddNamespace, nsDef.name, version, rdxCtx.EmmiterServerId(), std::move(def), stateToken}, [&wlck] { @@ -468,7 +494,6 @@ Error ReindexerImpl::closeNamespace(std::string_view nsName, const RdxContext& c auto wlck = nsLock_.DataWLock(ctx); auto nsIt = namespaces_.find(nsName); - if (nsIt == namespaces_.end()) { return Error(errNotFound, "Namespace '%s' does not exist", nsName); } @@ -495,6 +520,11 @@ Error ReindexerImpl::closeNamespace(std::string_view nsName, const RdxContext& c namespaces_.erase(nsIt); if (!isTemporary) { +#ifdef REINDEX_WITH_V3_FOLLOWERS + if (dropStorage) { + observers_.OnWALUpdate(LSNPair(), nsName, WALRecord(WalNamespaceDrop)); + } +#endif // REINDEX_WITH_V3_FOLLOWERS auto err = clusterizator_->Replicate( {dropStorage ? UpdateRecord::Type::DropNamespace : UpdateRecord::Type::CloseNamespace, std::string(nsName), lsn_t(0, 0), lsn_t(0, 0), ctx.EmmiterServerId()}, @@ -537,8 +567,13 @@ Error ReindexerImpl::openNamespace(std::string_view name, bool skipNameCheck, co } NamespaceDef nsDef(std::string(name), storageOpts); assertrx(clusterizator_); +#ifdef REINDEX_WITH_V3_FOLLOWERS auto ns = std::make_shared(nsDef.name, replOpts.has_value() ? replOpts->tmStateToken : std::optional(), - clusterizator_.get(), bgDeleter_); + *clusterizator_, bgDeleter_, observers_); +#else // REINDEX_WITH_V3_FOLLOWERS + auto ns = std::make_shared(nsDef.name, replOpts.has_value() ? replOpts->tmStateToken : std::optional(), + *clusterizator_, bgDeleter_); +#endif // REINDEX_WITH_V3_FOLLOWERS if (storageOpts.IsEnabled() && !storagePath_.empty()) { auto opts = storageOpts; ns->EnableStorage(storagePath_, opts.Autorepair(autorepairEnabled_), storageType_, rdxCtx); @@ -563,6 +598,11 @@ Error ReindexerImpl::openNamespace(std::string_view name, bool skipNameCheck, co const lsn_t version = setNsVersion(ns, replOpts, rdxCtx); namespaces_.insert({nsDef.name, std::move(ns)}); +#ifdef REINDEX_WITH_V3_FOLLOWERS + if (!nsDef.isTemporary) { + observers_.OnWALUpdate(LSNPair(), nsDef.name, WALRecord(WalNamespaceAdd)); + } +#endif // REINDEX_WITH_V3_FOLLOWERS auto err = clusterizator_->Replicate( {UpdateRecord::Type::AddNamespace, std::string(name), version, rdxCtx.EmmiterServerId(), std::move(nsDef), stateToken}, [&wlck] { @@ -626,11 +666,30 @@ bool ReindexerImpl::NamespaceIsInClusterConfig(std::string_view nsName) { return clusterizator_ && clusterizator_->NamespaceIsInClusterConfig(nsName); } +Error ReindexerImpl::SubscribeUpdates([[maybe_unused]] IUpdatesObserver* observer, [[maybe_unused]] const UpdatesFilters& filters, + [[maybe_unused]] SubscriptionOpts opts) { +#ifdef REINDEX_WITH_V3_FOLLOWERS + return observers_.Add(observer, filters, opts); +#else // REINDEX_WITH_V3_FOLLOWERS + return Error(errForbidden, "Reindexer was built without v3 followers compatibility"); +#endif // REINDEX_WITH_V3_FOLLOWERS +} + +Error ReindexerImpl::UnsubscribeUpdates([[maybe_unused]] IUpdatesObserver* observer) { +#ifdef REINDEX_WITH_V3_FOLLOWERS + return observers_.Delete(observer); +#else // REINDEX_WITH_V3_FOLLOWERS + return Error(errForbidden, "Reindexer was built without v3 followers compatibility"); +#endif // REINDEX_WITH_V3_FOLLOWERS +} + Error ReindexerImpl::renameNamespace(std::string_view srcNsName, const std::string& dstNsName, bool fromReplication, bool skipResync, const RdxContext& rdxCtx) { Namespace::Ptr dstNs, srcNs; try { - if (std::string_view(dstNsName) == srcNsName) return errOK; + if (std::string_view(dstNsName) == srcNsName.data()) { + return {}; + } if (isSystemNamespaceNameStrict(srcNsName)) { return Error(errParams, "Can't rename system namespace (%s)", srcNsName); } @@ -641,6 +700,22 @@ Error ReindexerImpl::renameNamespace(std::string_view srcNsName, const std::stri return Error(errParams, "Namespace name contains invalid character. Only alphas, digits,'_','-', are allowed (%s)", dstNsName); } + { + // Perform namespace flushes to minimize chances of the flush under lock + auto rlock = nsLock_.RLock(rdxCtx); + auto srcIt = namespaces_.find(srcNsName); + srcNs = (srcIt != namespaces_.end()) ? srcIt->second : Namespace::Ptr(); + rlock.unlock(); + + if (srcNs) { + auto err = srcNs->awaitMainNs(rdxCtx)->FlushStorage(rdxCtx); + if (!err.ok()) { + return Error(err.code(), "Unable to flush storage before rename: %s", err.what()); + } + srcNs.reset(); + } + } + rdxCtx.WithNoWaitSync(fromReplication); auto wlck = nsLock_.DataWLock(rdxCtx); @@ -701,6 +776,17 @@ Error ReindexerImpl::renameNamespace(std::string_view srcNsName, const std::stri } else { srcNs->Rename(dstNsName, storagePath_, replicateCb, rdxCtx); } +#ifdef REINDEX_WITH_V3_FOLLOWERS + if (needWalUpdate) { + observers_.OnWALUpdate(LSNPair(), srcNsName, WALRecord(WalNamespaceRename, dstNsName)); + } else if (!skipResync) { + WrSerializer ser; + auto nsDef = srcNs->GetDefinition(rdxCtx); + nsDef.GetJSON(ser); + ser.PutBool(true); + observers_.OnWALUpdate(LSNPair(), dstNsName, WALRecord(WalForceSync, ser.Slice())); + } +#endif // REINDEX_WITH_V3_FOLLOWERS } catch (...) { auto actualName = srcNs->GetName(rdxCtx); if (actualName != dstNsName) { @@ -712,7 +798,7 @@ Error ReindexerImpl::renameNamespace(std::string_view srcNsName, const std::stri } catch (const Error& err) { return err; } - return errOK; + return {}; } Error ReindexerImpl::readClusterConfigFile() { @@ -729,13 +815,6 @@ Error ReindexerImpl::readClusterConfigFile() { if (clusterConfig_.compare_exchange_strong(nullptr, confPtr.get())) { confPtr.release(); // NOLINT(bugprone-unused-return-value) Moved to clusterConfig_ ptr clusterizator_->Configure(*clusterConfig_); - if (clusterizator_->IsExpectingClusterStartup()) { - logPrintf(LogTrace, "%s: Starting clusterizator right after config read", storagePath_); - err = clusterizator_->StartClusterRepl(); - if (!err.ok()) { - logPrintf(LogError, "Error on cluster start: %s", err.what()); - } - } } } else { logPrintf(LogError, "Error parsing cluster config YML: %s", err.what()); @@ -866,7 +945,8 @@ Error ReindexerImpl::Update(std::string_view nsName, Item& item, LocalQueryResul Error ReindexerImpl::Update(const Query& q, LocalQueryResults& result, const RdxContext& rdxCtx) { try { - auto ns = getNamespace(q._namespace, rdxCtx); + q.VerifyForUpdate(); + auto ns = getNamespace(q.NsName(), rdxCtx); ns->Update(q, result, rdxCtx); if (ns->IsSystem(rdxCtx)) { const std::string kNsName = ns->GetName(rdxCtx); @@ -936,7 +1016,8 @@ Error ReindexerImpl::Delete(std::string_view nsName, Item& item, LocalQueryResul } Error ReindexerImpl::Delete(const Query& q, LocalQueryResults& result, const RdxContext& ctx) { - const std::string_view nsName = q.Namespace(); + q.VerifyForUpdate(); + const std::string_view nsName = q.NsName(); APPLY_NS_FUNCTION2(false, Delete, q, result); } @@ -954,9 +1035,9 @@ struct ItemRefLess { Error ReindexerImpl::Select(const Query& q, LocalQueryResults& result, const RdxContext& rdxCtx) { try { - NsLocker locks(rdxCtx); + RxSelector::NsLocker locks(rdxCtx); - auto mainNsWrp = getNamespace(q._namespace, rdxCtx); + auto mainNsWrp = getNamespace(q.NsName(), rdxCtx); auto mainNs = q.IsWALQuery() ? mainNsWrp->awaitMainNs(rdxCtx) : mainNsWrp->getMainNs(); const auto queriesPerfStatsEnabled = configProvider_.QueriesPerfStatsEnabled(); @@ -982,7 +1063,7 @@ Error ReindexerImpl::Select(const Query& q, LocalQueryResults& result, const Rdx } : std::function{}; - const bool isSystemNsRequest = isSystemNamespaceNameFast(q._namespace); + const bool isSystemNsRequest = isSystemNamespaceNameFast(q.NsName()); QueryStatCalculator statCalculator( std::move(hitter), std::chrono::microseconds(queriesThresholdUS), queriesPerfStatsEnabled || configProvider_.GetSelectLoggingParams().thresholdUs >= 0, @@ -991,13 +1072,13 @@ Error ReindexerImpl::Select(const Query& q, LocalQueryResults& result, const Rdx StatsLocker::StatsLockT statsSelectLck; if (isSystemNsRequest) { - statsSelectLck = syncSystemNamespaces(q._namespace, detectFilterNsNames(q), rdxCtx); + statsSelectLck = syncSystemNamespaces(q.NsName(), detectFilterNsNames(q), rdxCtx); } // Lookup and lock namespaces_ mainNs->updateSelectTime(); locks.Add(mainNs); - q.WalkNested(false, true, [this, &locks, &rdxCtx](const Query& q) { - auto nsWrp = getNamespace(q._namespace, rdxCtx); + q.WalkNested(false, true, true, [this, &locks, &rdxCtx](const Query& q) { + auto nsWrp = getNamespace(q.NsName(), rdxCtx); auto ns = q.IsWALQuery() ? nsWrp->awaitMainNs(rdxCtx) : nsWrp->getMainNs(); ns->updateSelectTime(); locks.Add(ns); @@ -1014,7 +1095,7 @@ Error ReindexerImpl::Select(const Query& q, LocalQueryResults& result, const Rdx const auto ward = rdxCtx.BeforeSimpleState(Activity::InProgress); SelectFunctionsHolder func; - doSelect(q, result, locks, func, rdxCtx, statCalculator); + RxSelector::DoSelect(q, result, locks, func, rdxCtx, statCalculator); func.Process(result); } catch (const Error& err) { if (rdxCtx.Compl()) rdxCtx.Compl()(err); @@ -1024,324 +1105,6 @@ Error ReindexerImpl::Select(const Query& q, LocalQueryResults& result, const Rdx return Error(); } -struct ReindexerImpl::QueryResultsContext { - QueryResultsContext() = default; - QueryResultsContext(PayloadType type, TagsMatcher tagsMatcher, const FieldsSet& fieldsFilter, std::shared_ptr schema) - : type_(std::move(type)), tagsMatcher_(std::move(tagsMatcher)), fieldsFilter_(fieldsFilter), schema_(std::move(schema)) {} - - PayloadType type_; - TagsMatcher tagsMatcher_; - FieldsSet fieldsFilter_; - std::shared_ptr schema_; -}; - -[[nodiscard]] static bool byJoinedField(std::string_view sortExpr, std::string_view joinedNs) { - static const fast_hash_set allowedSymbolsInIndexName{ - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', - 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', - 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_', '.', '+'}; - std::string_view::size_type i = 0; - const auto s = sortExpr.size(); - while (i < s && isspace(sortExpr[i])) ++i; - bool inQuotes = false; - if (i < s && sortExpr[i] == '"') { - ++i; - inQuotes = true; - } - while (i < s && isspace(sortExpr[i])) ++i; - std::string_view::size_type j = 0, s2 = joinedNs.size(); - for (; j < s2 && i < s; ++i, ++j) { - if (sortExpr[i] != joinedNs[j]) return false; - } - if (i >= s || sortExpr[i] != '.') return false; - for (++i; i < s; ++i) { - if (allowedSymbolsInIndexName.find(sortExpr[i]) == allowedSymbolsInIndexName.end()) { - if (isspace(sortExpr[i])) break; - if (inQuotes && sortExpr[i] == '"') { - inQuotes = false; - ++i; - break; - } - return false; - } - } - while (i < s && isspace(sortExpr[i])) ++i; - if (inQuotes && i < s && sortExpr[i] == '"') ++i; - while (i < s && isspace(sortExpr[i])) ++i; - return i == s; -} - -bool ReindexerImpl::isPreResultValuesModeOptimizationAvailable(const Query& jItemQ, const NamespaceImpl::Ptr& jns, const Query& mainQ) { - bool result = true; - jItemQ.entries.ExecuteAppropriateForEach( - Skip{}, - [&jns, &result](const QueryEntry& qe) { - if (qe.idxNo >= 0) { - assertrx(jns->indexes_.size() > static_cast(qe.idxNo)); - const IndexType indexType = jns->indexes_[qe.idxNo]->Type(); - if (IsComposite(indexType) || IsFullText(indexType)) result = false; - } - }, - [&jns, &result](const BetweenFieldsQueryEntry& qe) { - if (qe.firstIdxNo >= 0) { - assertrx(jns->indexes_.size() > static_cast(qe.firstIdxNo)); - const IndexType indexType = jns->indexes_[qe.firstIdxNo]->Type(); - if (IsComposite(indexType) || IsFullText(indexType)) result = false; - } - if (qe.secondIdxNo >= 0) { - assertrx(jns->indexes_.size() > static_cast(qe.secondIdxNo)); - if (IsComposite(jns->indexes_[qe.secondIdxNo]->Type())) result = false; - } - }); - if (!result) return false; - for (const auto& se : mainQ.sortingEntries_) { - if (byJoinedField(se.expression, jItemQ._namespace)) return false; // TODO maybe allow #1410 - } - return true; -} - -template -JoinedSelectors ReindexerImpl::prepareJoinedSelectors(const Query& q, LocalQueryResults& result, NsLocker& locks, - SelectFunctionsHolder& func, std::vector& queryResultsContexts, - const RdxContext& rdxCtx) { - JoinedSelectors joinedSelectors; - if (q.joinQueries_.empty()) return joinedSelectors; - auto ns = locks.Get(q._namespace); - assertrx(ns); - - // For each joined queries - uint32_t joinedSelectorsCount = uint32_t(q.joinQueries_.size()); - for (auto& jq : q.joinQueries_) { - if rx_unlikely (isSystemNamespaceNameFast(jq._namespace)) { - throw Error(errParams, "Queries to system namespaces ('%s') are not supported inside JOIN statement", jq._namespace); - } - if (rx_unlikely(!jq.joinQueries_.empty())) { - throw Error(errParams, "JOINs nested into the other JOINs are not supported"); - } - if (rx_unlikely(!jq.mergeQueries_.empty())) { - throw Error(errParams, "MERGEs nested into the JOINs are not supported"); - } - - // Get common results from joined namespaces_ - auto jns = locks.Get(jq._namespace); - assertrx(jns); - - // Do join for each item in main result - Query jItemQ(jq._namespace); - jItemQ.explain_ = q.explain_; - jItemQ.Debug(jq.debugLevel).Limit(jq.count); - jItemQ.Strict(q.strictMode); - for (size_t i = 0; i < jq.sortingEntries_.size(); ++i) { - jItemQ.Sort(jq.sortingEntries_[i].expression, jq.sortingEntries_[i].desc); - } - - jItemQ.entries.Reserve(jq.joinEntries_.size()); - - // Construct join conditions - for (auto& je : jq.joinEntries_) { - int joinIdx = IndexValueType::NotSet; - if (!jns->getIndexByName(je.joinIndex_, joinIdx)) { - joinIdx = IndexValueType::SetByJsonPath; - } - QueryEntry qe(InvertJoinCondition(je.condition_), je.joinIndex_, joinIdx); - if (!ns->getIndexByName(je.index_, const_cast(je).idxNo)) { - const_cast(je).idxNo = IndexValueType::SetByJsonPath; - } - jItemQ.entries.Append(je.op_, std::move(qe)); - } - - Query jjq(jq); - JoinPreResult::Ptr preResult = std::make_shared(); - uint32_t joinedFieldIdx = uint32_t(joinedSelectors.size()); - JoinCacheRes joinRes; - jns->getFromJoinCache(jq, joinRes); - jjq.explain_ = q.explain_; - jjq.Strict(q.strictMode); - if (!jjq.entries.Empty() && !joinRes.haveData) { - LocalQueryResults jr; - jjq.Limit(QueryEntry::kDefaultLimit); - SelectCtx ctx(jjq, &q); - ctx.preResult = preResult; - ctx.preResult->executionMode = JoinPreResult::ModeBuild; - ctx.preResult->enableStoredValues = isPreResultValuesModeOptimizationAvailable(jItemQ, jns, q); - ctx.functions = &func; - ctx.requiresCrashTracking = true; - jns->Select(jr, ctx, rdxCtx); - assertrx(ctx.preResult->executionMode == JoinPreResult::ModeExecute); - } - if (joinRes.haveData) { - preResult = joinRes.it.val.preResult; - } else if (joinRes.needPut) { - jns->putToJoinCache(joinRes, preResult); - } - - queryResultsContexts.emplace_back(jns->payloadType_, jns->tagsMatcher_, FieldsSet(jns->tagsMatcher_, jq.selectFilter_), - jns->schema_); - - result.AddNamespace(jns, true); - if (preResult->dataMode == JoinPreResult::ModeValues) { - jItemQ.entries.ExecuteAppropriateForEach( - Skip{}, - [&jns](QueryEntry& qe) { - if (qe.idxNo != IndexValueType::SetByJsonPath) { - assertrx(qe.idxNo >= 0 && static_cast(qe.idxNo) < jns->indexes_.size()); - if (jns->indexes_[qe.idxNo]->Opts().IsSparse()) qe.idxNo = IndexValueType::SetByJsonPath; - } - }, - [&jns](BetweenFieldsQueryEntry& qe) { - if (qe.firstIdxNo != IndexValueType::SetByJsonPath) { - assertrx(qe.firstIdxNo >= 0 && static_cast(qe.firstIdxNo) < jns->indexes_.size()); - if (jns->indexes_[qe.firstIdxNo]->Opts().IsSparse()) qe.firstIdxNo = IndexValueType::SetByJsonPath; - } - if (qe.secondIdxNo != IndexValueType::SetByJsonPath) { - assertrx(qe.secondIdxNo >= 0 && static_cast(qe.secondIdxNo) < jns->indexes_.size()); - if (jns->indexes_[qe.secondIdxNo]->Opts().IsSparse()) qe.secondIdxNo = IndexValueType::SetByJsonPath; - } - }); - preResult->values.PreselectAllowed(static_cast(jns->Config().maxPreselectSize) >= preResult->values.size()); - if (!preResult->values.Locked()) preResult->values.Lock(); // If not from cache - locks.Delete(jns); - jns.reset(); - } - joinedSelectors.emplace_back(jq.joinType, ns, std::move(jns), std::move(joinRes), std::move(jItemQ), result, jq, preResult, - joinedFieldIdx, func, joinedSelectorsCount, false, rdxCtx); - ThrowOnCancel(rdxCtx); - } - return joinedSelectors; -} - -template -void ReindexerImpl::doSelect(const Query& q, LocalQueryResults& result, NsLocker& locks, SelectFunctionsHolder& func, - const RdxContext& ctx, QueryStatCalculator& queryStatCalculator) { - auto ns = locks.Get(q._namespace); - if rx_unlikely (!ns) { - throw Error(errParams, "Namespace '%s' does not exist", q._namespace); - } - std::vector joinQueryResultsContexts; - bool thereAreJoins = !q.joinQueries_.empty(); - if (!thereAreJoins) { - for (const Query& mq : q.mergeQueries_) { - if (!mq.joinQueries_.empty()) { - thereAreJoins = true; - break; - } - } - } - - JoinedSelectors mainJoinedSelectors; - ExplainCalc::Duration preselectTimeTotal{0}; - if (thereAreJoins) { - const auto preselectStartTime = ExplainCalc::Clock::now(); - mainJoinedSelectors = prepareJoinedSelectors(q, result, locks, func, joinQueryResultsContexts, ctx); - result.joined_.resize(1 + q.mergeQueries_.size()); - preselectTimeTotal = ExplainCalc::Clock::now() - preselectStartTime; - } - IsFTQuery isFtQuery{IsFTQuery::NotSet}; - { - SelectCtx selCtx(q, nullptr); - selCtx.joinedSelectors = mainJoinedSelectors.size() ? &mainJoinedSelectors : nullptr; - selCtx.preResultTimeTotal = preselectTimeTotal; - selCtx.contextCollectingMode = true; - selCtx.functions = &func; - selCtx.nsid = 0; - if (!q.mergeQueries_.empty()) { - selCtx.isMergeQuery = IsMergeQuery::Yes; - if rx_unlikely (!q.sortingEntries_.empty()) { - throw Error{errNotValid, "Sorting in merge query is not implemented yet"}; // TODO #1449 - } - for (const auto& a : q.aggregations_) { - switch (a.Type()) { - case AggCount: - case AggCountCached: - case AggSum: - case AggMin: - case AggMax: - continue; - case AggAvg: - case AggFacet: - case AggDistinct: - case AggUnknown: - throw Error{errNotValid, "Aggregation '%s' in merge query is not implemented yet", - AggTypeToStr(a.Type())}; // TODO #1506 - } - } - } - selCtx.requiresCrashTracking = true; - ns->Select(result, selCtx, ctx); - result.AddNamespace(ns, true); - isFtQuery = selCtx.isFtQuery; - if (selCtx.explain.IsEnabled()) { - queryStatCalculator.AddExplain(selCtx.explain); - } - } - // should be destroyed after results.lockResults() - std::vector mergeJoinedSelectors; - if (!q.mergeQueries_.empty()) { - mergeJoinedSelectors.reserve(q.mergeQueries_.size()); - uint8_t counter = 0; - - auto hasUnsupportedAggreagations = [](const std::vector& aggVector, AggType& t) -> bool { - for (const auto& a : aggVector) { - if (a.Type() != AggCount || a.Type() != AggCountCached) { - t = a.Type(); - return true; - } - } - t = AggUnknown; - return false; - }; - AggType errType; - if (rx_unlikely((q.HasLimit() || q.HasOffset()) && hasUnsupportedAggreagations(q.aggregations_, errType))) { - throw Error(errParams, "Limit and offset are not supported for aggregations '%s'", AggTypeToStr(errType)); - } - for (auto& mq : q.mergeQueries_) { - if rx_unlikely (isSystemNamespaceNameFast(mq._namespace)) { - throw Error(errParams, "Queries to system namespaces ('%s') are not supported inside MERGE statement", mq._namespace); - } - if rx_unlikely (!mq.sortingEntries_.empty()) { - throw Error(errParams, "Sorting in inner merge query is not allowed"); - } - if rx_unlikely (!mq.aggregations_.empty() || mq.calcTotal != ModeNoTotal) { - throw Error(errParams, "Aggregations in inner merge query is not allowed"); - } - if rx_unlikely (mq.HasLimit() || mq.HasOffset()) { - throw Error(errParams, "Limit and offset in inner merge query is not allowed"); - } - if rx_unlikely (!mq.mergeQueries_.empty()) { - throw Error(errParams, "MERGEs nested into the MERGEs are not supported"); - } - - auto mns = locks.Get(mq._namespace); - assertrx(mns); - SelectCtx mctx(mq, &q); - mctx.nsid = ++counter; - mctx.isMergeQuery = IsMergeQuery::Yes; - mctx.isFtQuery = isFtQuery; - mctx.functions = &func; - mctx.contextCollectingMode = true; - mergeJoinedSelectors.emplace_back(prepareJoinedSelectors(mq, result, locks, func, joinQueryResultsContexts, ctx)); - mctx.joinedSelectors = mergeJoinedSelectors.back().size() ? &mergeJoinedSelectors.back() : nullptr; - mctx.requiresCrashTracking = true; - mns->Select(result, mctx, ctx); - result.AddNamespace(std::move(mns), true); - } - ItemRefVector& itemRefVec = result.Items(); - if (static_cast(q.start) >= itemRefVec.size()) { - result.Erase(itemRefVec.begin(), itemRefVec.end()); - return; - } - std::sort(itemRefVec.begin(), itemRefVec.end(), ItemRefLess()); - if (q.start > QueryEntry::kDefaultOffset) { - result.Erase(itemRefVec.begin(), itemRefVec.begin() + q.start); - } - if (itemRefVec.size() > q.count) { - result.Erase(itemRefVec.begin() + q.count, itemRefVec.end()); - } - } - // Adding context to LocalQueryResults - for (const auto& jctx : joinQueryResultsContexts) result.addNSContext(jctx.type_, jctx.tagsMatcher_, jctx.fieldsFilter_, jctx.schema_); -} - Error ReindexerImpl::Commit(std::string_view /*_namespace*/) { try { // getNamespace(_namespace)->FlushStorage(); @@ -1354,7 +1117,6 @@ Error ReindexerImpl::Commit(std::string_view /*_namespace*/) { } std::set ReindexerImpl::getFTIndexes(std::string_view nsName) { - InternalRdxContext ctx; const RdxContext rdxCtx; auto rlck = nsLock_.RLock(rdxCtx); auto it = namespaces_.find(nsName); @@ -1366,7 +1128,6 @@ std::set ReindexerImpl::getFTIndexes(std::string_view nsName) { } PayloadType ReindexerImpl::getPayloadType(std::string_view nsName) { - InternalRdxContext ctx; const RdxContext rdxCtx; auto rlck = nsLock_.RLock(rdxCtx); auto it = namespaces_.find(nsName); @@ -1380,7 +1141,6 @@ PayloadType ReindexerImpl::getPayloadType(std::string_view nsName) { Namespace::Ptr ReindexerImpl::getNamespace(std::string_view nsName, const RdxContext& ctx) { auto rlck = nsLock_.RLock(ctx); auto nsIt = namespaces_.find(nsName); - if (nsIt == namespaces_.end()) { throw Error(errNotFound, "Namespace '%s' does not exist", nsName); } @@ -1491,20 +1251,26 @@ Error ReindexerImpl::EnumNamespaces(std::vector& defs, EnumNamespa if (namespaces_.find(d.name) != namespaces_.end()) continue; } assertrx(clusterizator_); - std::unique_ptr tmpNs{new NamespaceImpl(d.name, {}, clusterizator_.get())}; +#ifdef REINDEX_WITH_V3_FOLLOWERS + std::unique_ptr tmpNs{new NamespaceImpl(d.name, {}, *clusterizator_, observers_)}; +#else // REINDEX_WITH_V3_FOLLOWERS + std::unique_ptr tmpNs{new NamespaceImpl(d.name, {}, *clusterizator_)}; +#endif // REINDEX_WITH_V3_FOLLOWERS try { tmpNs->EnableStorage(storagePath_, StorageOpts(), storageType_, rdxCtx); if (opts.IsHideTemporary() && tmpNs->IsTemporary(rdxCtx)) { continue; } defs.push_back(tmpNs->GetDefinition(rdxCtx)); - } catch (reindexer::Error) { + // NOLINTBEGIN(bugprone-empty-catch) + } catch (const Error&) { } + // NOLINTEND(bugprone-empty-catch) } } } - } catch (reindexer::Error err) { - return err.code(); + } catch (const Error& err) { + return err; } return errOK; } @@ -1844,12 +1610,15 @@ void ReindexerImpl::updateToSystemNamespace(std::string_view nsName, Item& item, const auto& actionNode = configJson[kActionConfigType]; handleConfigAction(actionNode, namespaces, ctx); - if (replicationEnabled_ && clusterizator_->IsExpectingAsyncReplStartup()) { - if (Error err = clusterizator_->StartAsyncRepl()) throw err; - } - if (replicationEnabled_ && !dbDestroyed_ && clusterizator_->IsExpectingClusterStartup()) { - if (Error err = clusterizator_->StartClusterRepl()) throw err; + if (replicationEnabled_ && !dbDestroyed_) { + if (clusterizator_->IsExpectingAsyncReplStartup()) { + if (Error err = clusterizator_->StartAsyncRepl()) throw err; + } + if (clusterizator_->IsExpectingClusterStartup()) { + if (Error err = clusterizator_->StartClusterRepl()) throw err; + } } + if (Error err = configProvider_.GetConfigParseErrors()) throw err; } catch (gason::Exception& e) { throw Error(errParseJson, "JSON parsing error: %s", e.what()); @@ -1919,7 +1688,9 @@ void ReindexerImpl::handleConfigAction(const gason::JsonNode& action, const std: } } - if (const auto it = proxyCallbacks_.find(command); it != proxyCallbacks_.end()) it->second(action, ctx); + if (const auto it = proxyCallbacks_.find({command, CallbackT::Type::User}); it != proxyCallbacks_.end()) { + it->second(action, CallbackT::EmptyT{}, ctx); + } } } @@ -1952,10 +1723,7 @@ void ReindexerImpl::updateConfFile(const ConfigT& newConf, std::string_view file auto err = watcher.RewriteFile(std::string(ser.Slice()), [&newConf](const std::string& content) { ConfigT config; Error err = config.FromYAML(content); - if (err.ok()) { - return config == newConf; - } - return false; + return err.ok() && (config == newConf); }); if (!err.ok()) { throw err; @@ -1972,15 +1740,15 @@ ReindexerImpl::FilterNsNamesT ReindexerImpl::detectFilterNsNames(const Query& q) }; h_vector notBrackets; - const auto& entries = q.entries; + const auto& entries = q.Entries(); for (uint32_t i = 0, sz = entries.Size(); i < sz; ++i) { const auto op = entries.GetOperation(i); if (op == OpOr) { return std::nullopt; } - if (entries.HoldsOrReferTo(i)) { + if (entries.Is(i)) { auto& qe = entries.Get(i); - if (qe.index == kNsNameField) { + if (qe.FieldName() == kNsNameField) { if (op == OpNot) { return std::nullopt; } @@ -1988,31 +1756,31 @@ ReindexerImpl::FilterNsNamesT ReindexerImpl::detectFilterNsNames(const Query& q) [i](const BracketRange& br) noexcept { return i >= br.begin && i < br.end; }) != notBrackets.end()) { return std::nullopt; } - if (qe.condition != CondSet && qe.condition != CondEq) { + if (qe.Condition() != CondSet && qe.Condition() != CondEq) { return std::nullopt; } if (res.has_value()) { return std::nullopt; } res.emplace(); - res->reserve(qe.values.size()); - for (auto& v : qe.values) { + res->reserve(qe.Values().size()); + for (auto& v : qe.Values()) { if (!v.Type().Is()) { return std::nullopt; } res->emplace_back(v.As()); } } - } else if (entries.HoldsOrReferTo(i)) { + } else if (entries.Is(i)) { auto& qe = entries.Get(i); - if (qe.firstIndex == kNsNameField || qe.secondIndex == kNsNameField) { + if (qe.LeftFieldName() == kNsNameField || qe.RightFieldName() == kNsNameField) { return std::nullopt; } } else if (op == OpNot && entries.IsSubTree(i)) { notBrackets.emplace_back(BracketRange{.begin = i, .end = uint32_t(entries.Size(i))}); } } - for (auto& jq : q.joinQueries_) { + for (auto& jq : q.GetJoinQueries()) { if (jq.joinType == OrInnerJoin) { return std::nullopt; } @@ -2177,11 +1945,9 @@ void ReindexerImpl::onProfiligConfigLoad() { Error ReindexerImpl::GetSqlSuggestions(std::string_view sqlQuery, int pos, std::vector& suggestions, const RdxContext& rdxCtx) { - Query query; - SQLSuggester suggester(query); std::vector nses; - suggestions = suggester.GetSuggestions( + suggestions = SQLSuggester::GetSuggestions( sqlQuery, pos, [&, this](EnumNamespacesOpts opts) { EnumNamespaces(nses, opts, rdxCtx); @@ -2339,7 +2105,7 @@ Error ReindexerImpl::ApplySnapshotChunk(std::string_view nsName, const SnapshotC return errOK; } -[[nodiscard]] bool ReindexerImpl::isSystemNamespaceNameStrict(std::string_view name) noexcept { +bool ReindexerImpl::isSystemNamespaceNameStrict(std::string_view name) noexcept { return std::find_if(kSystemNsDefs.begin(), kSystemNsDefs.end(), [name](const NamespaceDef& nsDef) { return iequals(nsDef.name, name); }) != kSystemNsDefs.end(); } @@ -2403,7 +2169,7 @@ static auto makeUpdateRecord(Tuple&& t, std::index_sequence) { } template -[[nodiscard]] Error ReindexerImpl::shardingConfigReplAction(const RdxContext& ctx, PreReplFunc func, Args&&... args) noexcept { +Error ReindexerImpl::shardingConfigReplAction(const RdxContext& ctx, PreReplFunc func, Args&&... args) noexcept { try { auto wlck = nsLock_.DataWLock(ctx); if (!clusterizator_) { @@ -2422,8 +2188,7 @@ template } template -[[nodiscard]] Error ReindexerImpl::shardingConfigReplAction(const RdxContext& ctx, cluster::UpdateRecord::Type type, - Args&&... args) noexcept { +Error ReindexerImpl::shardingConfigReplAction(const RdxContext& ctx, cluster::UpdateRecord::Type type, Args&&... args) noexcept { return shardingConfigReplAction( ctx, [&ctx, &type](Args&&... aargs) { return std::make_tuple(type, ctx.EmmiterServerId(), std::forward(aargs)...); }, std::forward(args)...); @@ -2458,15 +2223,15 @@ Error ReindexerImpl::applyShardingCfgCandidate(int64_t sourceId, const RdxContex return shardingConfigReplAction(ctx, UpdateRecord::Type::ApplyShardingConfig, sourceId); } -[[nodiscard]] Error ReindexerImpl::resetOldShardingConfig(int64_t sourceId, const RdxContext& ctx) noexcept { +Error ReindexerImpl::resetOldShardingConfig(int64_t sourceId, const RdxContext& ctx) noexcept { return shardingConfigReplAction(ctx, UpdateRecord::Type::ResetOldShardingConfig, sourceId); } -[[nodiscard]] Error ReindexerImpl::resetShardingConfigCandidate(int64_t sourceId, const RdxContext& ctx) noexcept { +Error ReindexerImpl::resetShardingConfigCandidate(int64_t sourceId, const RdxContext& ctx) noexcept { return shardingConfigReplAction(ctx, UpdateRecord::Type::ResetCandidateConfig, sourceId); } -[[nodiscard]] Error ReindexerImpl::rollbackShardingConfigCandidate(int64_t sourceId, const RdxContext& ctx) noexcept { +Error ReindexerImpl::rollbackShardingConfigCandidate(int64_t sourceId, const RdxContext& ctx) noexcept { return shardingConfigReplAction(ctx, UpdateRecord::Type::RollbackCandidateConfig, sourceId); } diff --git a/cpp_src/core/reindexerimpl.h b/cpp_src/core/reindexer_impl/reindexerimpl.h similarity index 82% rename from cpp_src/core/reindexerimpl.h rename to cpp_src/core/reindexer_impl/reindexerimpl.h index 1f1af1ff2..121999567 100644 --- a/cpp_src/core/reindexerimpl.h +++ b/cpp_src/core/reindexer_impl/reindexerimpl.h @@ -6,28 +6,33 @@ #include #include "cluster/config.h" +#include "core/dbconfig.h" #include "core/namespace/namespace.h" -#include "core/nsselecter/nsselecter.h" +#include "core/querystat.h" #include "core/rdxcontext.h" -#include "dbconfig.h" +#include "core/reindexerconfig.h" +#include "core/transaction/transaction.h" #include "estl/atomic_unique_ptr.h" #include "estl/fast_hash_map.h" #include "estl/h_vector.h" -#include "estl/smart_lock.h" #include "net/ev/ev.h" -#include "querystat.h" -#include "reindexerconfig.h" #include "tools/errors.h" #include "tools/filecontentwatcher.h" #include "tools/nsversioncounter.h" #include "tools/tcmallocheapwathcher.h" -#include "transaction/transaction.h" + +#include "replv3/updatesobserver.h" namespace reindexer { class IClientsStats; struct ClusterControlRequestData; +// REINDEX_WITH_V3_FOLLOWERS +class IUpdatesObserver; +class UpdatesFilters; +// REINDEX_WITH_V3_FOLLOWERS + namespace cluster { struct NodeData; class Clusterizator; @@ -43,12 +48,6 @@ struct RaftInfo; class ReindexerImpl { using Mutex = MarkedMutex; using StatsSelectMutex = MarkedMutex; - struct NsLockerItem { - NsLockerItem(NamespaceImpl::Ptr ins = {}) : ns(std::move(ins)), count(1) {} - NamespaceImpl::Ptr ns; - NamespaceImpl::Locker::RLockT nsLck; - unsigned count = 1; - }; template Error applyNsFunction(std::string_view nsName, const RdxContext &ctx, Arg arg, Args &&...args); template @@ -57,15 +56,33 @@ class ReindexerImpl { public: using Completion = std::function; - using CallbackFT = std::function; - using CallbackMap = fast_hash_map; + struct CallbackT { + enum class Type { System, User }; + struct EmptyT {}; + struct SourceIdT { + int64_t sourceId; + }; + using ExtrasT = std::variant; + using Value = std::function; + struct Key { + std::string_view key; + Type type; + + bool operator==(const Key &other) const noexcept { return key == other.key && type == other.type; } + bool operator<(const Key &other) const noexcept { return key < other.key && type < other.type; } + }; + struct Hash { + std::size_t operator()(Key key) const noexcept { return std::hash{}(key.key); } + }; + }; + + using CallbackMap = fast_hash_map; ReindexerImpl(ReindexerConfig cfg, ActivityContainer &activities, CallbackMap &&proxyCallbacks); ~ReindexerImpl(); Error Connect(const std::string &dsn, ConnectOpts opts = ConnectOpts()); - Error EnableStorage(const std::string &storagePath, bool skipPlaceholderCheck = false, const RdxContext &ctx = RdxContext()); Error OpenNamespace(std::string_view nsName, const StorageOpts &opts = StorageOpts().Enabled().CreateIfMissing(), const NsReplicationOpts &replOpts = NsReplicationOpts(), const RdxContext &ctx = RdxContext()); Error AddNamespace(const NamespaceDef &nsDef, std::optional replOpts = NsReplicationOpts{}, @@ -123,66 +140,13 @@ class ReindexerImpl { Error DumpIndex(std::ostream &os, std::string_view nsName, std::string_view index, const RdxContext &ctx); bool NamespaceIsInClusterConfig(std::string_view nsName); -protected: - using FilterNsNamesT = std::optional>; - - template - class NsLocker : private h_vector { - public: - NsLocker(const Context &context) : context_(context) {} - ~NsLocker() { - // Unlock first - for (auto it = rbegin(); it != rend(); ++it) { - // Some of the namespaces may be in unlocked statet in case of the exception during Lock() call - if (it->nsLck.owns_lock()) { - it->nsLck.unlock(); - } else { - assertrx(!locked_); - } - } - // Clean (ns may releases, if locker holds last ref) - } - - void Add(NamespaceImpl::Ptr ns) { - assertrx(!locked_); - for (auto it = begin(); it != end(); ++it) { - if (it->ns.get() == ns.get()) { - ++(it->count); - return; - } - } - - emplace_back(std::move(ns)); - return; - } - void Delete(const NamespaceImpl::Ptr &ns) { - for (auto it = begin(); it != end(); ++it) { - if (it->ns.get() == ns.get()) { - if (!--(it->count)) erase(it); - return; - } - } - assertrx(0); - } - void Lock() { - std::sort(begin(), end(), [](const NsLockerItem &lhs, const NsLockerItem &rhs) { return lhs.ns.get() < rhs.ns.get(); }); - for (auto it = begin(); it != end(); ++it) { - it->nsLck = it->ns->rLock(context_); - } - locked_ = true; - } + // REINDEX_WITH_V3_FOLLOWERS + Error SubscribeUpdates(IUpdatesObserver *observer, const UpdatesFilters &filters, SubscriptionOpts opts); + Error UnsubscribeUpdates(IUpdatesObserver *observer); + // REINDEX_WITH_V3_FOLLOWERS - NamespaceImpl::Ptr Get(const std::string &name) { - for (auto it = begin(); it != end(); it++) { - if (iequals(it->ns->name_, name)) return it->ns; - } - return nullptr; - } - - protected: - bool locked_ = false; - const Context &context_; - }; +private: + using FilterNsNamesT = std::optional>; class BackgroundThread { public: @@ -225,7 +189,7 @@ class ReindexerImpl { public: class NsWLock { public: - NsWLock(Mutex &mtx, const RdxContext &ctx, bool isCL) : impl_(mtx, &ctx), isClusterLck_(isCL) {} + NsWLock(Mutex &mtx, const RdxContext &ctx, bool isCL) : impl_(mtx, ctx), isClusterLck_(isCL) {} void lock() { impl_.lock(); } void unlock() { impl_.unlock(); } bool owns_lock() const { return impl_.owns_lock(); } @@ -238,9 +202,9 @@ class ReindexerImpl { typedef contexted_shared_lock RLockT; typedef NsWLock WLockT; - Locker(cluster::INsDataReplicator &clusterizator, ReindexerImpl &owner) : clusterizator_(clusterizator), owner_(owner) {} + Locker(cluster::INsDataReplicator &clusterizator, ReindexerImpl &owner) noexcept : clusterizator_(clusterizator), owner_(owner) {} - RLockT RLock(const RdxContext &ctx) const { return RLockT(mtx_, &ctx); } + RLockT RLock(const RdxContext &ctx) const { return RLockT(mtx_, ctx); } WLockT DataWLock(const RdxContext &ctx) const { const bool requireSync = !ctx.NoWaitSync() && ctx.GetOriginLSN().isEmpty(); WLockT lck(mtx_, ctx, true); @@ -264,15 +228,6 @@ class ReindexerImpl { ReindexerImpl &owner_; }; - template - void doSelect(const Query &q, LocalQueryResults &result, NsLocker &locks, SelectFunctionsHolder &func, const RdxContext &ctx, - QueryStatCalculator &queryStatCalculator); - struct QueryResultsContext; - template - JoinedSelectors prepareJoinedSelectors(const Query &q, LocalQueryResults &result, NsLocker &locks, SelectFunctionsHolder &func, - std::vector &, const RdxContext &ctx); - static bool isPreResultValuesModeOptimizationAvailable(const Query &jItemQ, const NamespaceImpl::Ptr &jns, const Query &mainQ); - Error insertDontUpdateSystemNS(std::string_view nsName, Item &item, const RdxContext &ctx); FilterNsNamesT detectFilterNsNames(const Query &q); [[nodiscard]] StatsLocker::StatsLockT syncSystemNamespaces(std::string_view sysNsName, const FilterNsNamesT &, const RdxContext &ctx); @@ -314,6 +269,7 @@ class ReindexerImpl { void checkClusterRole(std::string_view nsName, lsn_t originLsn) const; void setClusterizationStatus(ClusterizationStatus &&status, const RdxContext &ctx); std::string generateTemporaryNamespaceName(std::string_view baseName); + Error enableStorage(const std::string &storagePath); [[nodiscard]] Error saveShardingCfgCandidate(std::string_view config, int64_t sourceId, const RdxContext &ctx) noexcept; [[nodiscard]] Error applyShardingCfgCandidate(int64_t sourceId, const RdxContext &ctx) noexcept; @@ -360,7 +316,7 @@ class ReindexerImpl { private: mutable spinlock m; - intrusive_ptr> config; + intrusive_ptr> config = nullptr; } shardingConfig_; std::deque configWatchers_; @@ -374,8 +330,8 @@ class ReindexerImpl { ActivityContainer &activities_; StorageType storageType_; - bool autorepairEnabled_ = false; - bool replicationEnabled_ = true; + std::atomic autorepairEnabled_ = {false}; + std::atomic replicationEnabled_ = {true}; std::atomic connected_ = {false}; ReindexerConfig config_; @@ -384,6 +340,10 @@ class ReindexerImpl { const CallbackMap proxyCallbacks_; +#ifdef REINDEX_WITH_V3_FOLLOWERS + UpdatesObservers observers_; +#endif // REINDEX_WITH_V3_FOLLOWERS + friend class Replicator; friend class cluster::DataReplicator; friend class cluster::ReplThread; diff --git a/cpp_src/core/reindexer_impl/rx_selector.cc b/cpp_src/core/reindexer_impl/rx_selector.cc new file mode 100644 index 000000000..420c2ce8e --- /dev/null +++ b/cpp_src/core/reindexer_impl/rx_selector.cc @@ -0,0 +1,508 @@ +#include "rx_selector.h" +#include "core/nsselecter/nsselecter.h" +#include "core/nsselecter/querypreprocessor.h" +#include "core/queryresults/joinresults.h" +#include "tools/logger.h" + +namespace reindexer { + +struct ItemRefLess { + bool operator()(const ItemRef& lhs, const ItemRef& rhs) const noexcept { + if (lhs.Proc() == rhs.Proc()) { + if (lhs.Nsid() == rhs.Nsid()) { + return lhs.Id() < rhs.Id(); + } + return lhs.Nsid() < rhs.Nsid(); + } + return lhs.Proc() > rhs.Proc(); + } +}; + +struct RxSelector::QueryResultsContext { + QueryResultsContext() = default; + QueryResultsContext(PayloadType type, TagsMatcher tagsMatcher, const FieldsSet& fieldsFilter, std::shared_ptr schema, + lsn_t nsIncarnationTag) + : type_(std::move(type)), + tagsMatcher_(std::move(tagsMatcher)), + fieldsFilter_(fieldsFilter), + schema_(std::move(schema)), + nsIncarnationTag_(std::move(nsIncarnationTag)) {} + + PayloadType type_; + TagsMatcher tagsMatcher_; + FieldsSet fieldsFilter_; + std::shared_ptr schema_; + lsn_t nsIncarnationTag_; +}; + +template +void RxSelector::DoSelect(const Query& q, LocalQueryResults& result, NsLocker& locks, SelectFunctionsHolder& func, const RdxContext& ctx, + QueryStatCalculator& queryStatCalculator) { + auto ns = locks.Get(q.NsName()); + if rx_unlikely (!ns) { + throw Error(errParams, "Namespace '%s' does not exist", q.NsName()); + } + std::vector queryResultsHolder; + std::optional queryCopy; + if (!q.GetSubQueries().empty()) { + if (q.GetDebugLevel() >= LogInfo || ns->config_.logLevel >= LogInfo) { + logPrintf(LogInfo, "Query before subqueries substitution: %s", q.GetSQL()); + } + queryCopy.emplace(q); + preselectSubQueries(*queryCopy, queryResultsHolder, locks, func, ctx); + } + const Query& query = queryCopy ? *queryCopy : q; + std::vector joinQueryResultsContexts; + bool thereAreJoins = !query.GetJoinQueries().empty(); + if (!thereAreJoins) { + for (const Query& mq : query.GetMergeQueries()) { + if (!mq.GetJoinQueries().empty()) { + thereAreJoins = true; + break; + } + } + } + + JoinedSelectors mainJoinedSelectors; + ExplainCalc::Duration preselectTimeTotal{0}; + if (thereAreJoins) { + const auto preselectStartTime = ExplainCalc::Clock::now(); + mainJoinedSelectors = prepareJoinedSelectors(query, result, locks, func, joinQueryResultsContexts, ctx); + result.joined_.resize(1 + query.GetMergeQueries().size()); + preselectTimeTotal = ExplainCalc::Clock::now() - preselectStartTime; + } + IsFTQuery isFtQuery{IsFTQuery::NotSet}; + { + SelectCtx selCtx(query, nullptr); + selCtx.joinedSelectors = mainJoinedSelectors.size() ? &mainJoinedSelectors : nullptr; + selCtx.preResultTimeTotal = preselectTimeTotal; + selCtx.contextCollectingMode = true; + selCtx.functions = &func; + selCtx.nsid = 0; + if (!query.GetMergeQueries().empty()) { + selCtx.isMergeQuery = IsMergeQuery::Yes; + if rx_unlikely (!query.sortingEntries_.empty()) { + throw Error{errNotValid, "Sorting in merge query is not implemented yet"}; // TODO #1449 + } + for (const auto& a : query.aggregations_) { + switch (a.Type()) { + case AggCount: + case AggCountCached: + case AggSum: + case AggMin: + case AggMax: + continue; + case AggAvg: + case AggFacet: + case AggDistinct: + case AggUnknown: + throw Error{errNotValid, "Aggregation '%s' in merge query is not implemented yet", + AggTypeToStr(a.Type())}; // TODO #1506 + } + } + } + selCtx.requiresCrashTracking = true; + ns->Select(result, selCtx, ctx); + result.AddNamespace(ns, true); + isFtQuery = selCtx.isFtQuery; + if (selCtx.explain.IsEnabled()) { + queryStatCalculator.AddExplain(selCtx.explain); + } + } + // should be destroyed after results.lockResults() + std::vector mergeJoinedSelectors; + if (!query.GetMergeQueries().empty()) { + mergeJoinedSelectors.reserve(query.GetMergeQueries().size()); + uint8_t counter = 0; + + auto hasUnsupportedAggregations = [](const std::vector& aggVector, AggType& t) -> bool { + for (const auto& a : aggVector) { + if (a.Type() != AggCount || a.Type() != AggCountCached) { + t = a.Type(); + return true; + } + } + t = AggUnknown; + return false; + }; + AggType errType; + if (rx_unlikely((query.HasLimit() || query.HasOffset()) && hasUnsupportedAggregations(query.aggregations_, errType))) { + throw Error(errParams, "Limit and offset are not supported for aggregations '%s'", AggTypeToStr(errType)); + } + for (const JoinedQuery& mq : query.GetMergeQueries()) { + if rx_unlikely (isSystemNamespaceNameFast(mq.NsName())) { + throw Error(errParams, "Queries to system namespaces ('%s') are not supported inside MERGE statement", mq.NsName()); + } + if rx_unlikely (!mq.sortingEntries_.empty()) { + throw Error(errParams, "Sorting in inner merge query is not allowed"); + } + if rx_unlikely (!mq.aggregations_.empty() || mq.HasCalcTotal()) { + throw Error(errParams, "Aggregations in inner merge query are not allowed"); + } + if rx_unlikely (mq.HasLimit() || mq.HasOffset()) { + throw Error(errParams, "Limit and offset in inner merge query is not allowed"); + } + if rx_unlikely (!mq.GetMergeQueries().empty()) { + throw Error(errParams, "MERGEs nested into the MERGEs are not supported"); + } + std::optional mQueryCopy; + if (!mq.GetSubQueries().empty()) { + mQueryCopy.emplace(mq); + preselectSubQueries(*mQueryCopy, queryResultsHolder, locks, func, ctx); + } + const JoinedQuery& mQuery = mQueryCopy ? *mQueryCopy : mq; + + auto mns = locks.Get(mQuery.NsName()); + assertrx_throw(mns); + SelectCtx mctx(mQuery, &query); + mctx.nsid = ++counter; + mctx.isMergeQuery = IsMergeQuery::Yes; + mctx.isFtQuery = isFtQuery; + mctx.functions = &func; + mctx.contextCollectingMode = true; + mergeJoinedSelectors.emplace_back(prepareJoinedSelectors(mQuery, result, locks, func, joinQueryResultsContexts, ctx)); + mctx.joinedSelectors = mergeJoinedSelectors.back().size() ? &mergeJoinedSelectors.back() : nullptr; + mctx.requiresCrashTracking = true; + mns->Select(result, mctx, ctx); + result.AddNamespace(mns, true); + } + ItemRefVector& itemRefVec = result.Items(); + if (query.Offset() >= itemRefVec.size()) { + result.Erase(itemRefVec.begin(), itemRefVec.end()); + return; + } + boost::sort::pdqsort(itemRefVec.begin(), itemRefVec.end(), ItemRefLess()); + if (query.HasOffset()) { + result.Erase(itemRefVec.begin(), itemRefVec.begin() + query.Offset()); + } + if (itemRefVec.size() > query.Limit()) { + result.Erase(itemRefVec.begin() + query.Limit(), itemRefVec.end()); + } + } + // Adding context to QueryResults + for (const auto& jctx : joinQueryResultsContexts) { + result.addNSContext(jctx.type_, jctx.tagsMatcher_, jctx.fieldsFilter_, jctx.schema_, jctx.nsIncarnationTag_); + } +} + +[[nodiscard]] static bool byJoinedField(std::string_view sortExpr, std::string_view joinedNs) { + static const fast_hash_set allowedSymbolsInIndexName{ + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_', '.', '+'}; + std::string_view::size_type i = 0; + const auto s = sortExpr.size(); + while (i < s && isspace(sortExpr[i])) ++i; + bool inQuotes = false; + if (i < s && sortExpr[i] == '"') { + ++i; + inQuotes = true; + } + while (i < s && isspace(sortExpr[i])) ++i; + std::string_view::size_type j = 0, s2 = joinedNs.size(); + for (; j < s2 && i < s; ++i, ++j) { + if (sortExpr[i] != joinedNs[j]) return false; + } + if (i >= s || sortExpr[i] != '.') return false; + for (++i; i < s; ++i) { + if (allowedSymbolsInIndexName.find(sortExpr[i]) == allowedSymbolsInIndexName.end()) { + if (isspace(sortExpr[i])) break; + if (inQuotes && sortExpr[i] == '"') { + inQuotes = false; + ++i; + break; + } + return false; + } + } + while (i < s && isspace(sortExpr[i])) ++i; + if (inQuotes && i < s && sortExpr[i] == '"') ++i; + while (i < s && isspace(sortExpr[i])) ++i; + return i == s; +} + +bool RxSelector::isPreResultValuesModeOptimizationAvailable(const Query& jItemQ, const NamespaceImpl::Ptr& jns, const Query& mainQ) { + bool result = true; + jItemQ.Entries().ExecuteAppropriateForEach([](const SubQueryEntry&) { assertrx_throw(0); }, + [](const SubQueryFieldEntry&) { assertrx_throw(0); }, + Skip{}, + [&jns, &result](const QueryEntry& qe) { + if (qe.IsFieldIndexed()) { + assertrx_throw(jns->indexes_.size() > static_cast(qe.IndexNo())); + const IndexType indexType = jns->indexes_[qe.IndexNo()]->Type(); + if (IsComposite(indexType) || IsFullText(indexType)) result = false; + } + }, + [&jns, &result](const BetweenFieldsQueryEntry& qe) { + if (qe.IsLeftFieldIndexed()) { + assertrx_throw(jns->indexes_.size() > static_cast(qe.LeftIdxNo())); + const IndexType indexType = jns->indexes_[qe.LeftIdxNo()]->Type(); + if (IsComposite(indexType) || IsFullText(indexType)) result = false; + } + if (qe.IsRightFieldIndexed()) { + assertrx_throw(jns->indexes_.size() > static_cast(qe.RightIdxNo())); + if (IsComposite(jns->indexes_[qe.RightIdxNo()]->Type())) result = false; + } + }); + if (!result) return false; + for (const auto& se : mainQ.sortingEntries_) { + if (byJoinedField(se.expression, jItemQ.NsName())) return false; // TODO maybe allow #1410 + } + return true; +} + +template +bool RxSelector::selectSubQuery(const Query& subQuery, const Query& mainQuery, NsLocker& locks, SelectFunctionsHolder& func, + const RdxContext& rdxCtx) { + auto ns = locks.Get(subQuery.NsName()); + assertrx_throw(ns); + + SelectCtx sctx{subQuery, &mainQuery}; + sctx.nsid = 0; + sctx.requiresCrashTracking = true; + sctx.reqMatchedOnceFlag = true; + sctx.contextCollectingMode = true; + sctx.functions = &func; + + LocalQueryResults result; + ns->Select(result, sctx, rdxCtx); + locks.Delete(ns); + return sctx.matchedAtLeastOnce; +} + +template +VariantArray RxSelector::selectSubQuery(const Query& subQuery, const Query& mainQuery, NsLocker& locks, LocalQueryResults& qr, + SelectFunctionsHolder& func, const RdxContext& rdxCtx) { + NamespaceImpl::Ptr ns = locks.Get(subQuery.NsName()); + assertrx_throw(ns); + + SelectCtx sctx{subQuery, &mainQuery}; + sctx.nsid = 0; + sctx.requiresCrashTracking = true; + sctx.contextCollectingMode = true; + sctx.functions = &func; + + ns->Select(qr, sctx, rdxCtx); + VariantArray result, buf; + if (qr.GetAggregationResults().empty()) { + assertrx_throw(!subQuery.SelectFilters().empty()); + const std::string_view field = subQuery.SelectFilters()[0]; + result.reserve(qr.Count()); + if (int idxNo = -1; ns->getIndexByNameOrJsonPath(field, idxNo) && !ns->indexes_[idxNo]->Opts().IsSparse()) { + if (idxNo < ns->indexes_.firstCompositePos()) { + for (const auto& it : qr) { + if (!it.Status().ok()) { + throw it.Status(); + } + ConstPayload{ns->payloadType_, ns->items_[it.GetItemRef().Id()]}.Get(idxNo, buf); + for (Variant& v : buf) { + result.emplace_back(std::move(v)); + } + } + } else { + const auto fields = ns->indexes_[idxNo]->Fields(); + std::vector fieldsTypes; +#ifndef NDEBUG + const bool ftIdx = IsFullText(ns->indexes_[idxNo]->Type()); +#endif + for (const auto f : ns->indexes_[idxNo]->Fields()) { + if (f == IndexValueType::SetByJsonPath) { + // not indexed fields allowed only in ft composite indexes + assertrx_throw(ftIdx); + fieldsTypes.push_back(KeyValueType::String{}); + } else { + assertrx_throw(f <= ns->indexes_.firstCompositePos()); + fieldsTypes.push_back(ns->indexes_[f]->SelectKeyType()); + } + } + for (const auto& it : qr) { + if (!it.Status().ok()) { + throw it.Status(); + } + result.emplace_back(ConstPayload{ns->payloadType_, ns->items_[it.GetItemRef().Id()]}.GetComposite(fields, fieldsTypes)); + } + } + } else { + if (idxNo < 0) { + switch (mainQuery.GetStrictMode()) { + case StrictModeIndexes: + throw Error(errParams, + "Current query strict mode allows aggregate index fields only. There are no indexes with name '%s' in " + "namespace '%s'", + field, subQuery.NsName()); + case StrictModeNames: + if (ns->tagsMatcher_.path2tag(field).empty()) { + throw Error(errParams, + "Current query strict mode allows aggregate existing fields only. There are no fields with name " + "'%s' in namespace '%s'", + field, subQuery.NsName()); + } + break; + case StrictModeNone: + case StrictModeNotSet: + break; + } + } + for (const auto& it : qr) { + if (!it.Status().ok()) { + throw it.Status(); + } + ConstPayload{ns->payloadType_, ns->items_[it.GetItemRef().Id()]}.GetByJsonPath(field, ns->tagsMatcher_, buf, + KeyValueType::Undefined{}); + for (Variant& v : buf) { + result.emplace_back(std::move(v)); + } + } + } + } else { + const auto v = qr.GetAggregationResults()[0].GetValue(); + if (v.has_value()) { + result.emplace_back(*v); + } + } + locks.Delete(ns); + return result; +} + +template +JoinedSelectors RxSelector::prepareJoinedSelectors(const Query& q, LocalQueryResults& result, NsLocker& locks, + SelectFunctionsHolder& func, std::vector& queryResultsContexts, + const RdxContext& rdxCtx) { + JoinedSelectors joinedSelectors; + if (q.GetJoinQueries().empty()) return joinedSelectors; + auto ns = locks.Get(q.NsName()); + assertrx_throw(ns); + + // For each joined queries + uint32_t joinedSelectorsCount = uint32_t(q.GetJoinQueries().size()); + for (auto& jq : q.GetJoinQueries()) { + if rx_unlikely (isSystemNamespaceNameFast(jq.NsName())) { + throw Error(errParams, "Queries to system namespaces ('%s') are not supported inside JOIN statement", jq.NsName()); + } + if rx_unlikely (!jq.GetJoinQueries().empty()) { + throw Error(errParams, "JOINs nested into the other JOINs are not supported"); + } + if rx_unlikely (!jq.GetMergeQueries().empty()) { + throw Error(errParams, "MERGEs nested into the JOINs are not supported"); + } + if rx_unlikely (!jq.GetSubQueries().empty()) { + throw Error(errParams, "Subquery in the JOINs are not supported"); + } + if rx_unlikely (!jq.aggregations_.empty()) { + throw Error(errParams, "Aggregations are not allowed in joined subqueries"); + } + if rx_unlikely (jq.HasCalcTotal()) { + throw Error(errParams, "Count()/count_cached() are not allowed in joined subqueries"); + } + + // Get common results from joined namespaces_ + auto jns = locks.Get(jq.NsName()); + assertrx_throw(jns); + + // Do join for each item in main result + Query jItemQ(jq.NsName()); + jItemQ.Explain(q.GetExplain()); + jItemQ.Debug(jq.GetDebugLevel()).Limit(jq.Limit()); + jItemQ.Strict(q.GetStrictMode()); + for (size_t i = 0; i < jq.sortingEntries_.size(); ++i) { + jItemQ.Sort(jq.sortingEntries_[i].expression, jq.sortingEntries_[i].desc); + } + + jItemQ.ReserveQueryEntries(jq.joinEntries_.size()); + + // Construct join conditions + for (auto& je : jq.joinEntries_) { + QueryPreprocessor::SetQueryField(const_cast(je).LeftFieldData(), *ns); + QueryPreprocessor::SetQueryField(const_cast(je).RightFieldData(), *jns); + jItemQ.AppendQueryEntry(je.Operation(), QueryField(je.RightFieldData()), InvertJoinCondition(je.Condition()), + QueryEntry::IgnoreEmptyValues{}); + } + + Query jjq(static_cast(jq)); + JoinPreResult::Ptr preResult = std::make_shared(); + uint32_t joinedFieldIdx = uint32_t(joinedSelectors.size()); + JoinCacheRes joinRes; + jns->getFromJoinCache(jq, joinRes); + if (!jjq.Entries().Empty() && !joinRes.haveData) { + LocalQueryResults jr; + jjq.Limit(QueryEntry::kDefaultLimit); + SelectCtx ctx(jjq, &q); + ctx.preResult = preResult; + ctx.preResult->executionMode = JoinPreResult::ModeBuild; + ctx.preResult->enableStoredValues = isPreResultValuesModeOptimizationAvailable(jItemQ, jns, q); + ctx.functions = &func; + ctx.requiresCrashTracking = true; + jns->Select(jr, ctx, rdxCtx); + assertrx_throw(ctx.preResult->executionMode == JoinPreResult::ModeExecute); + } + if (joinRes.haveData) { + preResult = joinRes.it.val.preResult; + } else if (joinRes.needPut) { + jns->putToJoinCache(joinRes, preResult); + } + + queryResultsContexts.emplace_back(jns->payloadType_, jns->tagsMatcher_, FieldsSet(jns->tagsMatcher_, jq.SelectFilters()), + jns->schema_, jns->incarnationTag_); + + result.AddNamespace(jns, true); + if (preResult->dataMode == JoinPreResult::ModeValues) { + preResult->values.PreselectAllowed(static_cast(jns->Config().maxPreselectSize) >= preResult->values.size()); + if (!preResult->values.Locked()) preResult->values.Lock(); // If not from cache + locks.Delete(jns); + jns.reset(); + } + joinedSelectors.emplace_back(jq.joinType, ns, std::move(jns), std::move(joinRes), std::move(jItemQ), result, jq, preResult, + joinedFieldIdx, func, joinedSelectorsCount, false, rdxCtx); + ThrowOnCancel(rdxCtx); + } + return joinedSelectors; +} + +template +void RxSelector::preselectSubQueries(Query& mainQuery, std::vector& queryResultsHolder, NsLocker& locks, + SelectFunctionsHolder& func, const RdxContext& ctx) { + for (size_t i = 0, s = mainQuery.Entries().Size(); i < s; ++i) { + mainQuery.Entries().InvokeAppropriate( + i, Skip{}, + [&](const SubQueryEntry& sqe) { + try { + const CondType cond = sqe.Condition(); + if (cond == CondAny || cond == CondEmpty) { + if (selectSubQuery(mainQuery.GetSubQuery(sqe.QueryIndex()), mainQuery, locks, func, ctx) == (cond == CondAny)) { + mainQuery.SetEntry(i); + } else { + mainQuery.SetEntry(i); + } + } else { + LocalQueryResults qr; + const auto values = selectSubQuery(mainQuery.GetSubQuery(sqe.QueryIndex()), mainQuery, locks, qr, func, ctx); + if (QueryEntries::CheckIfSatisfyCondition(values, sqe.Condition(), sqe.Values())) { + mainQuery.SetEntry(i); + } else { + mainQuery.SetEntry(i); + } + } + } catch (const Error& err) { + throw Error(err.code(), "Error during preprocessing of subquery '" + mainQuery.GetSubQuery(sqe.QueryIndex()).GetSQL() + + "': " + err.what()); + } + }, + [&](const SubQueryFieldEntry& sqe) { + try { + queryResultsHolder.resize(queryResultsHolder.size() + 1); + mainQuery.SetEntry( + i, std::move(mainQuery.GetUpdatableEntry(i)).FieldName(), sqe.Condition(), + selectSubQuery(mainQuery.GetSubQuery(sqe.QueryIndex()), mainQuery, locks, queryResultsHolder.back(), func, ctx)); + } catch (const Error& err) { + throw Error(err.code(), "Error during preprocessing of subquery '" + mainQuery.GetSubQuery(sqe.QueryIndex()).GetSQL() + + "': " + err.what()); + } + }); + } +} + +template void RxSelector::DoSelect>( + const Query&, LocalQueryResults&, NsLocker&, SelectFunctionsHolder&, const RdxContext&, + QueryStatCalculator, long_actions::Logger>&); + +} // namespace reindexer diff --git a/cpp_src/core/reindexer_impl/rx_selector.h b/cpp_src/core/reindexer_impl/rx_selector.h new file mode 100644 index 000000000..5a2923d97 --- /dev/null +++ b/cpp_src/core/reindexer_impl/rx_selector.h @@ -0,0 +1,99 @@ +#pragma once + +#include "core/namespace/namespaceimpl.h" +#include "core/querystat.h" + +namespace reindexer { + +class SelectFunctionsHolder; + +class RxSelector { + struct NsLockerItem { + NsLockerItem(NamespaceImpl::Ptr ins = {}) noexcept : ns(std::move(ins)), count(1) {} + NamespaceImpl::Ptr ns; + NamespaceImpl::Locker::RLockT nsLck; + unsigned count = 1; + }; + +public: + template + class NsLocker : private h_vector { + public: + NsLocker(const Context &context) : context_(context) {} + ~NsLocker() { + // Unlock first + for (auto it = rbegin(); it != rend(); ++it) { + // Some of the namespaces may be in unlocked statet in case of the exception during Lock() call + if (it->nsLck.owns_lock()) { + it->nsLck.unlock(); + } else { + assertrx(!locked_); + } + } + // Clean (ns may releases, if locker holds last ref) + } + + void Add(NamespaceImpl::Ptr ns) { + assertrx(!locked_); + for (auto it = begin(); it != end(); ++it) { + if (it->ns.get() == ns.get()) { + ++(it->count); + return; + } + } + + emplace_back(std::move(ns)); + return; + } + void Delete(const NamespaceImpl::Ptr &ns) { + for (auto it = begin(); it != end(); ++it) { + if (it->ns.get() == ns.get()) { + if (!--(it->count)) erase(it); + return; + } + } + assertrx(0); + } + void Lock() { + boost::sort::pdqsort_branchless( + begin(), end(), [](const NsLockerItem &lhs, const NsLockerItem &rhs) noexcept { return lhs.ns.get() < rhs.ns.get(); }); + for (auto it = begin(), e = end(); it != e; ++it) { + it->nsLck = it->ns->rLock(context_); + } + locked_ = true; + } + + NamespaceImpl::Ptr Get(const std::string &name) { + for (auto it = begin(); it != end(); it++) { + if (iequals(it->ns->name_, name)) return it->ns; + } + return nullptr; + } + + protected: + bool locked_ = false; + const Context &context_; + }; + + template + static void DoSelect(const Query &q, LocalQueryResults &result, NsLocker &locks, SelectFunctionsHolder &func, const RdxContext &ctx, + QueryStatCalculator &queryStatCalculator); + +private: + struct QueryResultsContext; + template + static JoinedSelectors prepareJoinedSelectors(const Query &q, LocalQueryResults &result, NsLocker &locks, + SelectFunctionsHolder &func, std::vector &, const RdxContext &ctx); + template + static void preselectSubQueries(Query &mainQuery, std::vector &queryResultsHolder, NsLocker &, + SelectFunctionsHolder &, const RdxContext &); + template + [[nodiscard]] static bool selectSubQuery(const Query &subQuery, const Query &mainQuery, NsLocker &, SelectFunctionsHolder &, + const RdxContext &); + template + [[nodiscard]] static VariantArray selectSubQuery(const Query &subQuery, const Query &mainQuery, NsLocker &, LocalQueryResults &, + SelectFunctionsHolder &, const RdxContext &); + static bool isPreResultValuesModeOptimizationAvailable(const Query &jItemQ, const NamespaceImpl::Ptr &jns, const Query &mainQ); +}; + +} // namespace reindexer diff --git a/cpp_src/core/schema.cc b/cpp_src/core/schema.cc index 79682d12f..b47f6c171 100644 --- a/cpp_src/core/schema.cc +++ b/cpp_src/core/schema.cc @@ -277,8 +277,6 @@ Error Schema::FromJSON(std::string_view json) { void Schema::GetJSON(WrSerializer& ser) const { ser << originalJson_; } -std::string_view Schema::GetJSON() const noexcept { return originalJson_; } - KeyValueType Schema::GetFieldType(const TagsPath& fieldPath, bool& isArray) const { return paths_.fieldsTypes_.GetField(fieldPath, isArray); } @@ -298,7 +296,6 @@ Error Schema::GetProtobufSchema(WrSerializer& schema) const { std::string Schema::AppendProtobufNumber(std::string_view j, int protobufNsNumber) { std::string json(j); if (protobufNsNumber != -1 && j != "{}") { - // TODO: fix it auto pos = json.find_last_of('}'); if (pos != std::string::npos) { json.erase(pos); diff --git a/cpp_src/core/schema.h b/cpp_src/core/schema.h index e25e57f05..7a166401a 100644 --- a/cpp_src/core/schema.h +++ b/cpp_src/core/schema.h @@ -133,7 +133,7 @@ class Schema { Error FromJSON(std::string_view json); void GetJSON(WrSerializer&) const; - std::string_view GetJSON() const noexcept; + std::string_view GetJSON() const noexcept { return originalJson_; } Error BuildProtobufSchema(TagsMatcher& tm, PayloadType& pt); Error GetProtobufSchema(WrSerializer& schema) const; int GetProtobufNsNumber() const noexcept { return protobufNsNumber_; } diff --git a/cpp_src/core/selectfunc/nsselectfuncinterface.cc b/cpp_src/core/selectfunc/nsselectfuncinterface.cc index cd4d38e21..e5e470865 100644 --- a/cpp_src/core/selectfunc/nsselectfuncinterface.cc +++ b/cpp_src/core/selectfunc/nsselectfuncinterface.cc @@ -3,14 +3,16 @@ #include "core/namespace/namespaceimpl.h" namespace reindexer { -const std::string& NsSelectFuncInterface::GetName() const { return nm_.name_; }; -int NsSelectFuncInterface::getIndexByName(const std::string& index) const { return nm_.getIndexByName(index); } -bool NsSelectFuncInterface::getIndexByName(const std::string& name, int& index) const { return nm_.getIndexByName(name, index); } -int NsSelectFuncInterface::getIndexesCount() const { return nm_.indexes_.size(); } +const std::string& NsSelectFuncInterface::GetName() const noexcept { return nm_.name_; }; +int NsSelectFuncInterface::getIndexByName(std::string_view index) const noexcept { return nm_.getIndexByName(index); } +bool NsSelectFuncInterface::getIndexByName(std::string_view name, int& index) const noexcept { return nm_.tryGetIndexByName(name, index); } +int NsSelectFuncInterface::getIndexesCount() const noexcept { return nm_.indexes_.size(); } -const std::string& NsSelectFuncInterface::getIndexName(int id) const { return nm_.indexes_[id]->Name(); } -IndexType NsSelectFuncInterface::getIndexType(int id) const { return nm_.indexes_[id]->Type(); } -const FieldsSet& NsSelectFuncInterface::getIndexFields(int id) const { return nm_.indexes_[id]->Fields(); } -TagsPath NsSelectFuncInterface::getTagsPathForField(const std::string& jsonPath) const { return nm_.tagsMatcher_.path2tag(jsonPath); } +const std::string& NsSelectFuncInterface::getIndexName(int id) const noexcept { return nm_.indexes_[id]->Name(); } +IndexType NsSelectFuncInterface::getIndexType(int id) const noexcept { return nm_.indexes_[id]->Type(); } +const FieldsSet& NsSelectFuncInterface::getIndexFields(int id) const noexcept { return nm_.indexes_[id]->Fields(); } +TagsPath NsSelectFuncInterface::getTagsPathForField(std::string_view jsonPath) const noexcept { + return nm_.tagsMatcher_.path2tag(jsonPath); +} } // namespace reindexer diff --git a/cpp_src/core/selectfunc/nsselectfuncinterface.h b/cpp_src/core/selectfunc/nsselectfuncinterface.h index b20aa5079..7bab47f88 100644 --- a/cpp_src/core/selectfunc/nsselectfuncinterface.h +++ b/cpp_src/core/selectfunc/nsselectfuncinterface.h @@ -1,7 +1,6 @@ #pragma once #include #include -#include namespace reindexer { class NamespaceImpl; @@ -10,15 +9,15 @@ class FieldsSet; class NsSelectFuncInterface { public: - NsSelectFuncInterface(const NamespaceImpl& nm) : nm_(nm) {} - const std::string& GetName() const; - int getIndexByName(const std::string& index) const; - bool getIndexByName(const std::string& name, int& index) const; - int getIndexesCount() const; - const std::string& getIndexName(int id) const; - IndexType getIndexType(int id) const; - const FieldsSet& getIndexFields(int id) const; - TagsPath getTagsPathForField(const std::string& jsonPath) const; + explicit NsSelectFuncInterface(const NamespaceImpl& nm) noexcept : nm_(nm) {} + const std::string& GetName() const noexcept; + int getIndexByName(std::string_view index) const noexcept; + bool getIndexByName(std::string_view name, int& index) const noexcept; + int getIndexesCount() const noexcept; + const std::string& getIndexName(int id) const noexcept; + IndexType getIndexType(int id) const noexcept; + const FieldsSet& getIndexFields(int id) const noexcept; + TagsPath getTagsPathForField(std::string_view jsonPath) const noexcept; private: const NamespaceImpl& nm_; diff --git a/cpp_src/core/selectfunc/selectfunc.cc b/cpp_src/core/selectfunc/selectfunc.cc index 0ae2977e9..a716b0d06 100644 --- a/cpp_src/core/selectfunc/selectfunc.cc +++ b/cpp_src/core/selectfunc/selectfunc.cc @@ -9,38 +9,36 @@ namespace reindexer { -static inline void ltrim(std::string &s) { +inline void ltrim(std::string &s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) { return !std::isspace(ch); })); } // trim from end (in place) -static inline void rtrim(std::string &s) { +inline void rtrim(std::string &s) { s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return !std::isspace(ch); }).base(), s.end()); } // trim from both ends (in place) -static inline void trim(std::string &s) { +inline void trim(std::string &s) { ltrim(s); rtrim(s); } -SelectFunction::Ptr SelectFunctionsHolder::AddNamespace(const Query &q, const NamespaceImpl &nm, bool force) { +SelectFunction::Ptr SelectFunctionsHolder::AddNamespace(const Query &q, const NamespaceImpl &nm, uint32_t nsid, bool force) { if (q.selectFunctions_.empty() && !force) { return nullptr; } else if (!q.selectFunctions_.empty()) { force_only_ = false; } - if (!querys_) { - querys_.reset(new fast_hash_map); + if (queries_.size() <= nsid) { + queries_.resize(nsid + 1); } - - NsSelectFuncInterface nm_interface(nm); - SelectFunction::Ptr func = std::make_shared(q, nm_interface); - return querys_->emplace(nm_interface.GetName(), func).first->second; + queries_[nsid] = std::make_shared(q, NsSelectFuncInterface(nm)); + return queries_[nsid]; } -SelectFunction::SelectFunction(const Query &q, NsSelectFuncInterface &nm) : nm_(nm), currCjsonFieldIdx_(nm.getIndexesCount()) { +SelectFunction::SelectFunction(const Query &q, NsSelectFuncInterface &&nm) : nm_(std::move(nm)), currCjsonFieldIdx_(nm_.getIndexesCount()) { functions_.reserve(q.selectFunctions_.size()); for (auto &func : q.selectFunctions_) { SelectFuncParser parser; @@ -145,7 +143,7 @@ BaseFunctionCtx::Ptr SelectFunction::CreateCtx(int indexNo) { } BaseFunctionCtx::Ptr ctx; - IndexType indexType = nm_.getIndexType(indexNo); + const IndexType indexType = nm_.getIndexType(indexNo); if (IsComposite(indexType)) { int fieldNo = 0; @@ -163,22 +161,33 @@ BaseFunctionCtx::Ptr SelectFunction::CreateCtx(int indexNo) { auto it = functions_.find(indexNo); if (it != functions_.end()) { it->second.fieldNo = 0; - ctx = createCtx(it->second, ctx, nm_.getIndexType(indexNo)); + ctx = createCtx(it->second, ctx, indexType); } } - if (!ctx && IsFullText(nm_.getIndexType(indexNo))) { + if (!ctx && IsFullText(indexType)) { return createFuncForProc(indexNo); } return ctx; } void SelectFunctionsHolder::Process(LocalQueryResults &res) { - if (!querys_ || querys_->empty() || force_only_) return; + if (queries_.empty() || force_only_) return; + bool hasFuncs = false; + for (auto &q : queries_) { + if (q) { + hasFuncs = true; + break; + } + } + if (!hasFuncs) return; + bool changed = false; - for (size_t i = 0; i < res.Count(); ++i) { - auto &pl_type = res.getPayloadType(res.Items()[i].Nsid()); - auto it = querys_->find(pl_type.Name()); - if (it != querys_->end()) { - if (it->second->ProcessItem(res.Items()[i], pl_type, res.stringsHolder_)) changed = true; + for (auto &item : res.Items()) { + const auto nsid = item.Nsid(); + if (queries_.size() <= nsid) { + continue; + } + if (auto &funcPtr = queries_[nsid]; funcPtr && funcPtr->ProcessItem(item, res.getPayloadType(nsid), res.stringsHolder_)) { + changed = true; } } res.nonCacheableData = changed; @@ -186,8 +195,8 @@ void SelectFunctionsHolder::Process(LocalQueryResults &res) { bool SelectFunction::ProcessItem(ItemRef &res, PayloadType &pl_type, std::vector &stringsHolder) { bool changed = false; for (auto &func : functions_) { - if (!func.second.ctx) continue; - if (std::visit([&](auto &f) -> bool { return f.Process(res, pl_type, func.second, stringsHolder); }, func.second.func)) { + if (func.second.ctx && + std::visit([&](auto &f) -> bool { return f.Process(res, pl_type, func.second, stringsHolder); }, func.second.func)) { changed = true; } } diff --git a/cpp_src/core/selectfunc/selectfunc.h b/cpp_src/core/selectfunc/selectfunc.h index 56602b09c..d5f09da96 100644 --- a/cpp_src/core/selectfunc/selectfunc.h +++ b/cpp_src/core/selectfunc/selectfunc.h @@ -12,7 +12,7 @@ class NamespaceImpl; class SelectFunction { public: typedef std::shared_ptr Ptr; - SelectFunction(const Query& q, NsSelectFuncInterface& nm); + SelectFunction(const Query& q, NsSelectFuncInterface&& nm); /// Processes selected item to apply sql function. /// @param res - ItemRef containing payload value. @@ -48,18 +48,21 @@ class SelectFunctionsHolder { /// Creates SelectFunction object for a query. /// @param q - query that contains sql function. /// @param nm - namespace of the query to be executed. + /// @param nsid - unique ns ID in the query. /// @param force - forces to create SelectFunction object /// even if the list of sql-functions in this query is empty. /// @return pointer to SelectFunction object or nullptr in case of error. - SelectFunction::Ptr AddNamespace(const Query& q, const NamespaceImpl& nm, bool force); + SelectFunction::Ptr AddNamespace(const Query& q, const NamespaceImpl& nm, uint32_t nsid, bool force); /// Processing of results of an executed query. /// @param res - results of query execution. void Process(LocalQueryResults& res); private: + using MapT = h_vector; + /// Indicates if object is empty and was created wuth flag force = true. bool force_only_ = true; /// Container of sql functions for every namespace. - std::unique_ptr> querys_; + MapT queries_; }; } // namespace reindexer diff --git a/cpp_src/core/selectkeyresult.h b/cpp_src/core/selectkeyresult.h index 17a597eb0..55f83498b 100644 --- a/cpp_src/core/selectkeyresult.h +++ b/cpp_src/core/selectkeyresult.h @@ -199,18 +199,20 @@ class SelectKeyResult : public h_vector { size_t actualSize = 0; ids.resize(idsCount); auto rit = ids.begin(); - for (auto it = begin(); it != end(); ++it) { + for (auto it = begin(), endIt = end(); it != endIt; ++it) { if (it->isRange_) { throw Error(errLogic, "Unable to merge 'range' idset ('generic sort mode')"); } if (it->useBtree_) { - actualSize += it->set_->size(); + const auto sz = it->set_->size(); + actualSize += sz; std::copy(it->set_->begin(), it->set_->end(), rit); - rit += it->set_->size(); + rit += sz; } else { - actualSize += it->ids_.size(); + const auto sz = it->ids_.size(); + actualSize += sz; std::copy(it->ids_.begin(), it->ids_.end(), rit); - rit += it->ids_.size(); + rit += sz; } } assertrx(idsCount == actualSize); @@ -218,7 +220,7 @@ class SelectKeyResult : public h_vector { } else { mergedIds = make_intrusive>(); mergedIds->reserve(idsCount); - for (auto it = begin(); it != end(); ++it) { + for (auto it = begin(), endIt = end(); it != endIt; ++it) { if (it->isRange_) { throw Error(errLogic, "Unable to merge 'range' idset ('merge sort mode')"); } @@ -231,7 +233,7 @@ class SelectKeyResult : public h_vector { for (;;) { const int min = mergedIds->size() ? mergedIds->back() : INT_MIN; int curMin = INT_MAX; - for (auto it = begin(); it != end(); it++) { + for (auto it = begin(), endIt = end(); it != endIt; ++it) { if (it->useBtree_) { const auto end = it->set_->end(); for (; it->itset_ != end && *it->itset_ <= min; ++it->itset_) { @@ -250,8 +252,8 @@ class SelectKeyResult : public h_vector { mergedIds->shrink_to_fit(); } clear(); - emplace_back(SingleSelectKeyResult(mergedIds)); deferedExplicitSort = false; + emplace_back(mergedIds); return mergedIds; } }; diff --git a/cpp_src/core/shardingproxy.cc b/cpp_src/core/shardingproxy.cc index 5212551f3..21a201155 100644 --- a/cpp_src/core/shardingproxy.cc +++ b/cpp_src/core/shardingproxy.cc @@ -18,16 +18,17 @@ void ShardingProxy::ShutdownCluster() { } } -auto ShardingProxy::isWithSharding(const Query &q, const RdxContext &ctx, int &actualShardId) const { +auto ShardingProxy::isWithSharding(const Query &q, const RdxContext &ctx, int &actualShardId, int64_t &cfgSourceId) const { using ret_type = std::optional; - if (q.local_) { + if (q.IsLocal()) { if (q.Type() != QuerySelect) { throw Error{errParams, "Only SELECT query could be LOCAL"}; } return ret_type{}; } - if (q.count == 0 && q.calcTotal == ModeNoTotal && !q.joinQueries_.size() && !q.mergeQueries_.size()) { + if (q.Limit() == 0 && q.CalcTotal() == ModeNoTotal && q.GetJoinQueries().empty() && q.GetMergeQueries().empty() && + q.GetSubQueries().empty()) { return ret_type{}; // Special case for tagsmatchers selects } if (q.IsWALQuery()) { @@ -44,6 +45,7 @@ auto ShardingProxy::isWithSharding(const Query &q, const RdxContext &ctx, int &a } actualShardId = lockedShardingRouter->ActualShardId(); + cfgSourceId = lockedShardingRouter->SourceId(); if (!isWithSharding(ctx)) { return ret_type{}; @@ -79,16 +81,36 @@ bool ShardingProxy::isWithSharding(const RdxContext &ctx) const noexcept { int(ctx.ShardId()) == ShardingKeyType::NotSetShard; } -ShardingProxy::ShardingProxy(ReindexerConfig cfg) - : impl_(std::move(cfg), activities_, {{"apply_sharding_config", [this](const gason::JsonNode &action, const RdxContext &ctx) { - auto localy = action["locally"]; - Error (ShardingProxy::*method)(const gason::JsonNode &, const RdxContext &) noexcept = - &ShardingProxy::handleNewShardingConfigLocaly; - auto err = std::invoke(localy.empty() ? &ShardingProxy::handleNewShardingConfig : method, - this, action["config"], ctx); +using ImplCallBackT = ReindexerImpl::CallbackT; - if (!err.ok()) throw err; - }}}) {} +ShardingProxy::ShardingProxy(ReindexerConfig cfg) + : impl_(std::move(cfg), activities_, + {{{"apply_sharding_config", ImplCallBackT::Type::User}, + [this](const gason::JsonNode &action, const ImplCallBackT::ExtrasT &, const RdxContext &ctx) { + auto &locallyNode = action["locally"]; + const bool locally = !locallyNode.empty() && locallyNode.As(); + if (locally) { + const std::optional externalSourceId = + action["source_id"].empty() ? std::optional() : action["source_id"].As(); + Error (ShardingProxy::*method)(const gason::JsonNode &, std::optional, const RdxContext &) noexcept = + &ShardingProxy::handleNewShardingConfigLocally; + auto err = std::invoke(method, this, action["config"], externalSourceId, ctx); + if (!err.ok()) throw err; + } else { + auto err = std::invoke(&ShardingProxy::handleNewShardingConfig, this, action["config"], ctx); + if (!err.ok()) throw err; + } + }}, + {{"leader_config_process", ImplCallBackT::Type::System}, + [this](const gason::JsonNode &config, const ImplCallBackT::ExtrasT &extras, const RdxContext &ctx) { + auto *sourceIdPtr = std::get_if(&extras); + std::optional externalSourceId; + if (sourceIdPtr) { + externalSourceId = sourceIdPtr->sourceId; + } + auto err = handleNewShardingConfigLocally<>(config, externalSourceId, ctx); + if (!err.ok()) throw err; + }}}) {} Error ShardingProxy::Connect(const std::string &dsn, ConnectOpts opts) { try { @@ -116,7 +138,7 @@ Error ShardingProxy::Connect(const std::string &dsn, ConnectOpts opts) { } template -[[nodiscard]] Error ShardingProxy::resetShardingConfigs(int64_t sourceId, const RdxContext &ctx) noexcept { +Error ShardingProxy::resetShardingConfigs(int64_t sourceId, const RdxContext &ctx) noexcept { try { // To avoid connection errors, it is necessary to copy the connections from the current ShardingConfig // since ParallelExecutor consistently calls the functions that disconnect shards @@ -178,7 +200,19 @@ void ShardingProxy::obtainConfigForResetRouting(std::optional(system_clock().now().time_since_epoch()).count(); + sourceId &= ~cluster::ShardingConfig::serverIdMask; + int64_t serverId = impl_.GetServerID(); + if (serverId < 0 || serverId >= 1000) { + throw Error(errParams, "Incorrect serverId(%d) for sharding config sourceId generation.", serverId); + } + sourceId |= (serverId << cluster::ShardingConfig::serverIdPos); + return sourceId; +} + +Error ShardingProxy::handleNewShardingConfig(const gason::JsonNode &configJSON, const RdxContext &ctx) noexcept { try { if (stringifyJson(configJSON).empty()) return Error(errParams, "New sharding config is empty"); @@ -196,7 +230,7 @@ void ShardingProxy::obtainConfigForResetRouting(std::optional::max()); + auto sourceId = generateSourceId(); ParallelExecutor execNodes(router.ActualShardId()); @@ -234,15 +268,17 @@ void ShardingProxy::obtainConfigForResetRouting(std::optional(sourceId, ctx).what()); + } return Error(); } CATCH_AND_RETURN } -[[nodiscard]] Error ShardingProxy::handleNewShardingConfigLocaly(const gason::JsonNode &configJSON, const RdxContext &ctx) noexcept { +Error ShardingProxy::handleNewShardingConfigLocally(const gason::JsonNode &configJSON, std::optional externalSourceId, + const RdxContext &ctx) noexcept { try { if (configJSON.empty()) { if (auto lockedConfigCandidate = configCandidate_.SharedLock(ctx); lockedConfigCandidate.Config()) @@ -258,20 +294,27 @@ void ShardingProxy::obtainConfigForResetRouting(std::optional(configJSON, ctx); + return handleNewShardingConfigLocally<>(configJSON, externalSourceId, ctx); } CATCH_AND_RETURN } template -[[nodiscard]] Error ShardingProxy::handleNewShardingConfigLocaly(const ConfigType &rawConfig, const RdxContext &ctx) noexcept { +Error ShardingProxy::handleNewShardingConfigLocally(const ConfigType &rawConfig, std::optional externalSourceId, + const RdxContext &ctx) noexcept { try { cluster::ShardingConfig config; auto err = config.FromJSON(rawConfig); if (!err.ok()) return err; - int64_t sourceId = tools::RandomGenerator::gets64(int64_t{0}, std::numeric_limits::max()); - logPrintf(LogInfo, "Start attempt applying sharding config locally. Source - %d", sourceId); + int64_t sourceId; + if (externalSourceId.has_value()) { + sourceId = externalSourceId.value(); + logPrintf(LogInfo, "Start attempt applying sharding config locally. Forced source id - %d", sourceId); + } else { + sourceId = generateSourceId(); + logPrintf(LogInfo, "Start attempt applying sharding config locally. Generated source id - %d", sourceId); + } saveShardingCfgCandidateImpl(std::move(config), sourceId, ctx); applyNewShardingConfig({sourceId}, ctx); @@ -302,26 +345,27 @@ bool ShardingProxy::needProxyWithinCluster(const RdxContext &ctx) { } namespace { -using ShardingReqType = sharding::ShardingControlRequestData::Type; -using EnumsOrder = meta::Values2Types; - -using ClusterProxyMethods = - meta::Map>; - -using RequestEnum2Types = - meta::Map>; +using ReqT = sharding::ShardingControlRequestData::Type; +using ClusterProxyMethods = meta::Map; + +using RequestEnum2Types = meta::Map< + RDX_META_PAIR(ReqT::SaveCandidate, sharding::SaveConfigCommand), RDX_META_PAIR(ReqT::ResetOldSharding, sharding::ResetConfigCommand), + RDX_META_PAIR(ReqT::ApplyNew, sharding::ApplyConfigCommand), RDX_META_PAIR(ReqT::ResetCandidate, sharding::ResetConfigCommand), + RDX_META_PAIR(ReqT::RollbackCandidate, sharding::ResetConfigCommand)>; } // namespace struct ShardingProxy::ShardingProxyMethods { - using Map = meta::Map, - &ShardingProxy::applyNewShardingConfig, &ShardingProxy::resetConfigCandidate, - &ShardingProxy::resetOrRollbackShardingConfig>>; + using Map = + meta::Map), + RDX_META_PAIR(ReqT::ApplyNew, &ShardingProxy::applyNewShardingConfig), + RDX_META_PAIR(ReqT::ResetCandidate, &ShardingProxy::resetConfigCandidate), + RDX_META_PAIR(ReqT::RollbackCandidate, &ShardingProxy::resetOrRollbackShardingConfig)>; }; template @@ -344,8 +388,7 @@ void ShardingProxy::shardingControlRequestAction(const Request &request, const R (this->*ShardingProxyMethods::Map::GetValue())(std::move(data), ctx); } -[[nodiscard]] Error ShardingProxy::ShardingControlRequest(const sharding::ShardingControlRequestData &request, - const RdxContext &ctx) noexcept { +Error ShardingProxy::ShardingControlRequest(const sharding::ShardingControlRequestData &request, const RdxContext &ctx) noexcept { try { using Type = sharding::ShardingControlRequestData::Type; @@ -372,12 +415,12 @@ void ShardingProxy::shardingControlRequestAction(const Request &request, const R } case Type::ApplyLeaderConfig: { assertrx(!ctx.GetOriginLSN().isEmpty()); - const auto &data = std::get(request.data); - return data.config.empty() ? handleNewShardingConfigLocaly(gason::JsonNode::EmptyNode(), ctx) - : handleNewShardingConfigLocaly(data.config, ctx); + const auto &data = std::get(request.data); + return data.config.empty() ? handleNewShardingConfigLocally(gason::JsonNode::EmptyNode(), std::optional(), ctx) + : handleNewShardingConfigLocally<>(data.config, data.sourceId, ctx); } default: - throw Error(errLogic, "Unsupported sharding request command"); + throw Error(errLogic, "Unsupported sharding request command: %d", int(request.type)); } } CATCH_AND_RETURN @@ -408,12 +451,12 @@ void ShardingProxy::saveShardingCfgCandidateImpl(cluster::ShardingConfig config, "Config candidate is busy already (when trying to save a new config). Received Source - %d. Current sourceId - %d", sourceId, lockedConfigCandidate.SourceId()); } + config.sourceId = sourceId; lockedConfigCandidate.EnableReseter(false); if (lockedConfigCandidate.Reseter().joinable()) lockedConfigCandidate.Reseter().join(); lockedConfigCandidate.EnableReseter(); - impl_.SaveNewShardingConfigFile(config); lockedConfigCandidate.SourceId() = sourceId; lockedConfigCandidate.Config() = std::move(config); @@ -511,6 +554,8 @@ void ShardingProxy::applyNewShardingConfig(const sharding::ApplyConfigCommand &d auto lockedShardingRouter = shardingRouter_.UniqueLock(ctx); + impl_.SaveNewShardingConfigFile(*config); + auto err = impl_.ResetShardingConfig(std::move(*config)); // TODO: after allowing actions to upsert #config namespace, make ApplyNewShardingConfig returned void, allow except here // if (!err.ok()) return err; @@ -809,10 +854,12 @@ Error ShardingProxy::Update(const Query &query, QueryResults &result, const RdxC auto updateFn = [this](const Query &q, LocalQueryResults &qr, const RdxContext &ctx) { return impl_.Update(q, qr, ctx); }; int actualShardId = ShardingKeyType::NotSharded; + int64_t shardingVersion = -1; result.SetQuery(&query); - if (auto lckRouterOpt = isWithSharding(query, ctx, actualShardId)) { + if (auto lckRouterOpt = isWithSharding(query, ctx, actualShardId, shardingVersion)) { return executeQueryOnShard(*lckRouterOpt, query, result, 0, ctx, std::move(updateFn)); } + result.SetShardingConfigVersion(shardingVersion); result.AddQr(LocalQueryResults{}, actualShardId); return updateFn(query, result.ToLocalQr(false), ctx); } catch (Error &e) { @@ -884,9 +931,11 @@ Error ShardingProxy::Delete(const Query &query, QueryResults &result, const RdxC result.SetQuery(&query); int actualShardId = ShardingKeyType::NotSharded; - if (auto lckRouterOpt = isWithSharding(query, ctx, actualShardId)) { + int64_t shardingVersion = -1; + if (auto lckRouterOpt = isWithSharding(query, ctx, actualShardId, shardingVersion)) { return executeQueryOnShard(*lckRouterOpt, query, result, 0, ctx, std::move(deleteFn)); } + result.SetShardingConfigVersion(shardingVersion); result.AddQr(LocalQueryResults{}, actualShardId); return deleteFn(query, result.ToLocalQr(false), ctx); } catch (Error &e) { @@ -896,8 +945,7 @@ Error ShardingProxy::Delete(const Query &query, QueryResults &result, const RdxC Error ShardingProxy::Select(std::string_view sql, QueryResults &result, unsigned proxyFetchLimit, const RdxContext &ctx) { try { - Query query; - query.FromSQL(sql); + const Query query = Query::FromSQL(sql); switch (query.type_) { case QuerySelect: { return Select(query, result, proxyFetchLimit, ctx); @@ -909,7 +957,7 @@ Error ShardingProxy::Select(std::string_view sql, QueryResults &result, unsigned return Update(query, result, ctx); } case QueryTruncate: { - return TruncateNamespace(query.Namespace(), ctx); + return TruncateNamespace(query.NsName(), ctx); } default: return Error(errLogic, "Incorrect sql type %d", query.type_); @@ -927,11 +975,13 @@ Error ShardingProxy::Select(const Query &query, QueryResults &result, unsigned p result.SetQuery(&query); int actualShardId = ShardingKeyType::NotSharded; - if (auto lckRouterOpt = isWithSharding(query, ctx, actualShardId)) { + int64_t shardingVersion = -1; + if (auto lckRouterOpt = isWithSharding(query, ctx, actualShardId, shardingVersion)) { return executeQueryOnShard( *lckRouterOpt, query, result, proxyFetchLimit, ctx, [this](const Query &q, LocalQueryResults &qr, const RdxContext &ctx) { return impl_.Select(q, qr, ctx); }); } + result.SetShardingConfigVersion(shardingVersion); result.AddQr(LocalQueryResults{}, actualShardId); return impl_.Select(query, result.ToLocalQr(false), ctx); } catch (const Error &err) { @@ -1042,16 +1092,21 @@ Error ShardingProxy::EnumMeta(std::string_view nsName, std::vector template bool ShardingProxy::isSharderQuery(const Query &q, const ShardingRouterLock &shLockShardingRouter) const { - if (isSharded(q.Namespace(), shLockShardingRouter)) { + if (isSharded(q.NsName(), shLockShardingRouter)) { return true; } - for (const auto &jq : q.joinQueries_) { - if (isSharded(jq.Namespace(), shLockShardingRouter)) { + for (const auto &jq : q.GetJoinQueries()) { + if (isSharded(jq.NsName(), shLockShardingRouter)) { return true; } } - for (const auto &mq : q.mergeQueries_) { - if (isSharded(mq.Namespace(), shLockShardingRouter)) { + for (const auto &mq : q.GetMergeQueries()) { + if (isSharded(mq.NsName(), shLockShardingRouter)) { + return true; + } + } + for (const auto &sq : q.GetSubQueries()) { + if (isSharded(sq.NsName(), shLockShardingRouter)) { return true; } } @@ -1220,6 +1275,7 @@ Error ShardingProxy::modifyItemOnShard(LockedRouter &lockedShardingRouter, const if (!status.ok()) { return status; } + result.SetShardingConfigVersion(lockedShardingRouter->SourceId()); if (connection) { client::Item clientItem = toClientItem(nsName, connection.get(), item); client::QueryResults qrClient(result.Flags(), 0); @@ -1240,11 +1296,7 @@ Error ShardingProxy::modifyItemOnShard(LockedRouter &lockedShardingRouter, const return status; } result.AddQr(LocalQueryResults(), actualShardId); - status = localFn(nsName, item, result.ToLocalQr(false)); - if (!status.ok()) { - return status; - } - return status; + return localFn(nsName, item, result.ToLocalQr(false)); } template @@ -1270,6 +1322,8 @@ Error ShardingProxy::executeQueryOnShard(LockedRouter &lockedShardingRouter, con return Error(errLogic, "Delete request can be executed on one node only."); } + const auto shardingVersion = lockedShardingRouter->SourceId(); + result.SetShardingConfigVersion(shardingVersion); const bool isDistributedQuery = connections.size() > 1; if (!isDistributedQuery) { assert(connections.size() == 1); @@ -1287,6 +1341,11 @@ Error ShardingProxy::executeQueryOnShard(LockedRouter &lockedShardingRouter, con client::QueryResults qrClient(result.Flags(), proxyFetchLimit, client::LazyQueryResultsMode{}); status = executeQueryOnClient(connection, query, qrClient, [](size_t, size_t) {}); if (status.ok()) { + if (qrClient.GetShardingConfigVersion() != shardingVersion) { + return Error(errLogic, + "Proxied query: local and remote sharding versions (config source IDs) are different: %d vs %d", + shardingVersion, qrClient.GetShardingConfigVersion()); + } result.AddQr(std::move(qrClient), connections[0].ShardId(), true); } @@ -1300,22 +1359,23 @@ Error ShardingProxy::executeQueryOnShard(LockedRouter &lockedShardingRouter, con } } return status; - } else if (query.count == UINT_MAX && query.start == 0 && query.sortingEntries_.empty()) { + } else if (query.Limit() == QueryEntry::kDefaultLimit && query.Offset() == QueryEntry::kDefaultOffset && + query.sortingEntries_.empty()) { ParallelExecutor exec(actualShardId); return exec.ExecSelect(query, result, connections, ctx, std::forward(localAction)); } else { - unsigned limit = query.count; - unsigned offset = query.start; + unsigned limit = query.Limit(); + unsigned offset = query.Offset(); Query distributedQuery(query); if (!distributedQuery.sortingEntries_.empty()) { - const auto ns = impl_.GetNamespacePtr(distributedQuery.Namespace(), ctx)->getMainNs(); + const auto ns = impl_.GetNamespacePtr(distributedQuery.NsName(), ctx)->getMainNs(); result.SetOrdering(distributedQuery, *ns, ctx); } - if (distributedQuery.count != UINT_MAX && !distributedQuery.sortingEntries_.empty()) { - distributedQuery.Limit(distributedQuery.start + distributedQuery.count); + if (distributedQuery.Limit() != QueryEntry::kDefaultLimit && !distributedQuery.sortingEntries_.empty()) { + distributedQuery.Limit(distributedQuery.Offset() + distributedQuery.Limit()); } - if (distributedQuery.start != 0) { + if (distributedQuery.Offset() != QueryEntry::kDefaultOffset) { if (distributedQuery.sortingEntries_.empty()) { distributedQuery.ReqTotal(); } else { @@ -1348,6 +1408,12 @@ Error ShardingProxy::executeQueryOnShard(LockedRouter &lockedShardingRouter, con calculateNewLimitOfsset(count, totalCount, limit, offset); }); if (status.ok()) { + if (qrClient.GetShardingConfigVersion() != shardingVersion) { + return Error( + errLogic, + "Distributed query: local and remote sharding versions (config source IDs) are different: %d vs %d", + shardingVersion, qrClient.GetShardingConfigVersion()); + } result.AddQr(std::move(qrClient), connections[i].ShardId(), (i + 1) == connections.size()); } } else { @@ -1367,7 +1433,7 @@ Error ShardingProxy::executeQueryOnShard(LockedRouter &lockedShardingRouter, con return status; } } - if (distributedQuery.calcTotal == ModeNoTotal && limit == 0 && (i + 1) != connections.size()) { + if (distributedQuery.CalcTotal() == ModeNoTotal && limit == 0 && (i + 1) != connections.size()) { result.RebuildMergedData(); break; } diff --git a/cpp_src/core/shardingproxy.h b/cpp_src/core/shardingproxy.h index 3302d6021..8314562ce 100644 --- a/cpp_src/core/shardingproxy.h +++ b/cpp_src/core/shardingproxy.h @@ -72,9 +72,6 @@ class ShardingProxy { return impl_.SetClusterizationStatus(nsName, status, ctx); } bool NeedTraceActivity() const noexcept { return impl_.NeedTraceActivity(); } - Error EnableStorage(const std::string &storagePath, bool skipPlaceholderCheck, const RdxContext &ctx) { - return impl_.EnableStorage(storagePath, skipPlaceholderCheck, ctx); - } Error InitSystemNamespaces() { return impl_.InitSystemNamespaces(); } Error GetSnapshot(std::string_view nsName, const SnapshotOpts &opts, Snapshot &snapshot, const RdxContext &ctx) { return impl_.GetSnapshot(nsName, opts, snapshot, ctx); @@ -128,6 +125,13 @@ class ShardingProxy { [[nodiscard]] Error ShardingControlRequest(const sharding::ShardingControlRequestData &request, const RdxContext &ctx) noexcept; + // REINDEX_WITH_V3_FOLLOWERS + Error SubscribeUpdates(IUpdatesObserver *observer, const UpdatesFilters &filters, SubscriptionOpts opts) { + return impl_.SubscribeUpdates(observer, filters, opts); + } + Error UnsubscribeUpdates(IUpdatesObserver *observer) { return impl_.UnsubscribeUpdates(observer); } + // REINDEX_WITH_V3_FOLLOWERS + private: using ItemModifyFT = Error (client::Reindexer::*)(std::string_view, client::Item &); using ItemModifyQrFT = Error (client::Reindexer::*)(std::string_view, client::Item &, client::QueryResults &); @@ -137,7 +141,7 @@ class ShardingProxy { return ser; } - auto isWithSharding(const Query &q, const RdxContext &ctx, int &actualShardId) const; + auto isWithSharding(const Query &q, const RdxContext &ctx, int &actualShardId, int64_t &cfgSourceId) const; auto isWithSharding(std::string_view nsName, const RdxContext &ctx) const; bool isWithSharding(const RdxContext &ctx) const noexcept; @@ -171,9 +175,11 @@ class ShardingProxy { const CalucalteFT &limitOffsetCalc); [[nodiscard]] Error handleNewShardingConfig(const gason::JsonNode &config, const RdxContext &ctx) noexcept; - [[nodiscard]] Error handleNewShardingConfigLocaly(const gason::JsonNode &config, const RdxContext &ctx) noexcept; + [[nodiscard]] Error handleNewShardingConfigLocally(const gason::JsonNode &config, std::optional externalSourceId, + const RdxContext &ctx) noexcept; template - [[nodiscard]] Error handleNewShardingConfigLocaly(const ConfigType &rawConfig, const RdxContext &ctx) noexcept; + [[nodiscard]] Error handleNewShardingConfigLocally(const ConfigType &rawConfig, std::optional externalSourceId, + const RdxContext &ctx) noexcept; void saveShardingCfgCandidate(const sharding::SaveConfigCommand &requestData, const RdxContext &ctx); void saveShardingCfgCandidateImpl(cluster::ShardingConfig config, int64_t sourceId, const RdxContext &ctx); @@ -198,6 +204,8 @@ class ShardingProxy { void checkNamespacesEmpty(const cluster::ShardingConfig &config, const RdxContext &ctx); void checkSyncCluster(const cluster::ShardingConfig &config); + int64_t generateSourceId() const; + ClusterProxy impl_; using Mutex = MarkedMutex; @@ -230,10 +238,10 @@ class ShardingProxy { }; public: - auto SharedLock(const RdxContext &ctx) const { return ShardingRouterTSWrapper{RLocker(mtx_, &ctx), locatorService_}; } + auto SharedLock(const RdxContext &ctx) const { return ShardingRouterTSWrapper{RLocker(mtx_, ctx), locatorService_}; } auto SharedLock() const { return ShardingRouterTSWrapper{shared_lock(mtx_), locatorService_}; } - auto UniqueLock(const RdxContext &ctx) { return ShardingRouterTSWrapper{WLocker(mtx_, &ctx), locatorService_}; } + auto UniqueLock(const RdxContext &ctx) { return ShardingRouterTSWrapper{WLocker(mtx_, ctx), locatorService_}; } auto UniqueLock() { return ShardingRouterTSWrapper{std::unique_lock(mtx_), locatorService_}; } auto SharedPtr(const RdxContext &ctx) const; @@ -263,8 +271,8 @@ class ShardingProxy { }; public: - auto SharedLock(const RdxContext &ctx) const { return ConfigCandidateTSWrapper{RLocker(mtx_, &ctx), *this}; } - auto UniqueLock(const RdxContext &ctx) { return ConfigCandidateTSWrapper{WLocker(mtx_, &ctx), *this}; } + auto SharedLock(const RdxContext &ctx) const { return ConfigCandidateTSWrapper{RLocker(mtx_, ctx), *this}; } + auto UniqueLock(const RdxContext &ctx) { return ConfigCandidateTSWrapper{WLocker(mtx_, ctx), *this}; } auto SharedLock() const { return ConfigCandidateTSWrapper{shared_lock(mtx_), *this}; } auto UniqueLock() { return ConfigCandidateTSWrapper{std::unique_lock(mtx_), *this}; } diff --git a/cpp_src/core/sorting/sortexpression.cc b/cpp_src/core/sorting/sortexpression.cc index 77ca0fad0..dfc1bac10 100644 --- a/cpp_src/core/sorting/sortexpression.cc +++ b/cpp_src/core/sorting/sortexpression.cc @@ -84,12 +84,12 @@ VariantArray SortExpression::GetJoinedFieldValues(IdType rowId, const joins::Nam bool SortExpression::ByField() const noexcept { static constexpr SortExpressionOperation noOperation; - return Size() == 1 && container_[0].HoldsOrReferTo() && GetOperation(0) == noOperation; + return Size() == 1 && container_[0].Is() && GetOperation(0) == noOperation; } bool SortExpression::ByJoinedField() const noexcept { static constexpr SortExpressionOperation noOperation; - return Size() == 1 && container_[0].HoldsOrReferTo() && GetOperation(0) == noOperation; + return Size() == 1 && container_[0].Is() && GetOperation(0) == noOperation; } SortExprFuncs::JoinedIndex& SortExpression::GetJoinedIndex() noexcept { diff --git a/cpp_src/core/storage/leveldblogger.cc b/cpp_src/core/storage/leveldblogger.cc new file mode 100644 index 000000000..eba6dc16c --- /dev/null +++ b/cpp_src/core/storage/leveldblogger.cc @@ -0,0 +1,22 @@ +#include "leveldblogger.h" + +#include +#include + +// Using separate cc-file to be able to compile it with different options. +// Static LevelDB v1.23 is built with -fno-rtti by default and to inherit NoOpLogger from leveldb's logger, this file must be built with +// -fno-rtti to + +namespace reindexer { +namespace datastorage { + +class NoOpLogger : public leveldb::Logger { + void Logv(const char* /*format*/, va_list /*ap*/) override final {} +}; + +static NoOpLogger dummyLevelDBLogger; + +void SetDummyLogger(leveldb::Options& options) { options.info_log = &dummyLevelDBLogger; } + +} // namespace datastorage +} // namespace reindexer diff --git a/cpp_src/core/storage/leveldblogger.h b/cpp_src/core/storage/leveldblogger.h new file mode 100644 index 000000000..9f060daa6 --- /dev/null +++ b/cpp_src/core/storage/leveldblogger.h @@ -0,0 +1,13 @@ +#pragma once + +namespace leveldb { +struct Options; +} + +namespace reindexer { +namespace datastorage { + +void SetDummyLogger(leveldb::Options& options); + +} +} // namespace reindexer diff --git a/cpp_src/core/storage/leveldbstorage.cc b/cpp_src/core/storage/leveldbstorage.cc index aff80e990..1d18a392a 100644 --- a/cpp_src/core/storage/leveldbstorage.cc +++ b/cpp_src/core/storage/leveldbstorage.cc @@ -5,7 +5,9 @@ #include #include #include +#include "leveldblogger.h" #include "tools/assertrx.h" +#include "tools/fsops.h" namespace reindexer { namespace datastorage { @@ -125,6 +127,7 @@ Error LevelDbStorage::doOpen(const std::string& path, const StorageOpts& opts) { leveldb::Options options; options.create_if_missing = opts.IsCreateIfMissing(); options.max_open_files = 50; + SetDummyLogger(options); leveldb::DB* db = nullptr; leveldb::Status status = leveldb::DB::Open(options, path, &db); @@ -144,7 +147,11 @@ void LevelDbStorage::doDestroy(const std::string& path) { db_.reset(); leveldb::Status status = leveldb::DestroyDB(path.c_str(), options); if (!status.ok()) { - printf("Cannot destroy DB: %s, %s\n", path.c_str(), status.ToString().c_str()); + fprintf(stderr, "Cannot destroy LevelDB's storage: %s, %s. Trying to remove files by the backup mechanism...\n", path.c_str(), + status.ToString().c_str()); + if (fs::RmDirAll(path) != 0) { + fprintf(stderr, "Unable to remove LevelDB's storage: %s, %s", path.c_str(), strerror(errno)); + } } } diff --git a/cpp_src/core/storage/leveldbstorage.h b/cpp_src/core/storage/leveldbstorage.h index 291396452..472c1d696 100644 --- a/cpp_src/core/storage/leveldbstorage.h +++ b/cpp_src/core/storage/leveldbstorage.h @@ -2,6 +2,7 @@ #ifdef REINDEX_WITH_LEVELDB +#include #include #include #include "basestorage.h" diff --git a/cpp_src/core/storage/rocksdbstorage.cc b/cpp_src/core/storage/rocksdbstorage.cc index 95bd8e8fb..2f83909bf 100644 --- a/cpp_src/core/storage/rocksdbstorage.cc +++ b/cpp_src/core/storage/rocksdbstorage.cc @@ -6,6 +6,7 @@ #include #include #include +#include "tools/fsops.h" namespace reindexer { namespace datastorage { @@ -143,8 +144,10 @@ void RocksDbStorage::doDestroy(const std::string& path) { options.create_if_missing = true; db_.reset(); rocksdb::Status status = rocksdb::DestroyDB(path.c_str(), options); - if (!status.ok()) { - printf("Cannot destroy DB: %s, %s\n", path.c_str(), status.ToString().c_str()); + fprintf(stderr, "Cannot destroy RocksDB's storage: %s, %s. Trying to remove files by the backup mechanism...\n", path.c_str(), + status.ToString().c_str()); + if (fs::RmDirAll(path) != 0) { + fprintf(stderr, "Unable to remove RocksDB's storage: %s, %s", path.c_str(), strerror(errno)); } } diff --git a/cpp_src/core/transaction/localtransaction.h b/cpp_src/core/transaction/localtransaction.h index 993159291..d35236f39 100644 --- a/cpp_src/core/transaction/localtransaction.h +++ b/cpp_src/core/transaction/localtransaction.h @@ -24,11 +24,11 @@ class LocalTransaction { } std::vector &GetSteps() noexcept { assertrx(tx_); - return tx_->steps_; + return tx_->steps; } const std::vector &GetSteps() const noexcept { assertrx(tx_); - return tx_->steps_; + return tx_->steps; } Transaction::TimepointT GetStartTime() const noexcept { assertrx(data_); @@ -43,6 +43,14 @@ class LocalTransaction { return data_->nsName; } Error Status() const noexcept { return err_; } + void ValidatePK(const FieldsSet &pkFields) { + assertrx(data_); + if (tx_ && tx_->HasDeleteItemSteps() && rx_unlikely(pkFields != data_->GetPKFileds())) { + throw Error(errNotValid, + "Transaction has Delete-calls and it's PK metadata is outdated (probably PK has been change during the transaction " + "creation)"); + } + } private: LocalTransaction(std::unique_ptr &&d, std::unique_ptr &&tx, Error &&e) diff --git a/cpp_src/core/transaction/proxiedtransaction.cc b/cpp_src/core/transaction/proxiedtransaction.cc index 74e04b4f4..df25a16ce 100644 --- a/cpp_src/core/transaction/proxiedtransaction.cc +++ b/cpp_src/core/transaction/proxiedtransaction.cc @@ -17,8 +17,8 @@ Error ProxiedTransaction::Modify(Item &&item, ItemModifyMode mode, lsn_t lsn) { std::unique_lock lck(mtx_); if (itemCache_.isValid) { itemFromCache = true; - clientItem = client::Item(new client::ItemImpl(itemCache_.pt, itemCache_.tm, nullptr, - std::chrono::milliseconds())); + clientItem = client::Item( + new client::ItemImpl(itemCache_.pt, itemCache_.tm, nullptr, std::chrono::milliseconds())); } else { lck.unlock(); clientItem = tx_.NewItem(); diff --git a/cpp_src/core/transaction/transactionimpl.cc b/cpp_src/core/transaction/transactionimpl.cc index b6c8c7e87..103b9257f 100644 --- a/cpp_src/core/transaction/transactionimpl.cc +++ b/cpp_src/core/transaction/transactionimpl.cc @@ -1,7 +1,7 @@ #include "transactionimpl.h" #include "client/reindexerimpl.h" #include "cluster/sharding/sharding.h" -#include "core/reindexerimpl.h" +#include "core/reindexer_impl/reindexerimpl.h" #include "tools/clusterproxyloghelper.h" namespace reindexer { @@ -139,7 +139,10 @@ Error TransactionImpl::SetTagsMatcher(TagsMatcher &&tm, lsn_t lsn) { Item TransactionImpl::NewItem() { std::lock_guard lck(mtx_); - return Item(new ItemImpl(data_->GetPayloadType(), data_->GetTagsMatcher(), data_->GetPKFileds(), data_->GetSchema())); + assertrx(data_); + Item item(new ItemImpl(data_->GetPayloadType(), data_->GetTagsMatcher(), data_->GetPKFileds(), data_->GetSchema())); + item.impl_->tagsMatcher().clearUpdated(); + return item; } Error TransactionImpl::Status() const noexcept { @@ -193,11 +196,19 @@ Error TransactionImpl::Commit(int serverId, bool expectSharding, ReindexerImpl & if (auto *proxiedTx = std::get_if(&tx_); proxiedTx && *proxiedTx) { const auto ward = ctx.BeforeClusterProxy(); auto res = (*proxiedTx)->Commit(serverId, shardId_, result, ctx); + if (res.ok() && shardingRouter_) { + result.SetShardingConfigVersion(shardingRouter_.SourceId()); + } status_ = kErrCommited; return res; } else if (auto *localTx = std::get_if(&tx_); localTx && *localTx) { try { - result.AddQr(LocalQueryResults(), shardingRouter_ ? shardingRouter_.ActualShardId() : ShardingKeyType::ProxyOff); + if (shardingRouter_) { + result.AddQr(LocalQueryResults(), shardingRouter_.ActualShardId()); + result.SetShardingConfigVersion(shardingRouter_.SourceId()); + } else { + result.AddQr(LocalQueryResults(), shardingRouter_ ? shardingRouter_.ActualShardId() : ShardingKeyType::ProxyOff); + } status_ = kErrCommited; LocalTransaction ltx(std::move(data_), std::move(*localTx), Error()); tx_ = Empty{}; diff --git a/cpp_src/core/transaction/transactionsteps.h b/cpp_src/core/transaction/transactionsteps.h index b83b0e107..8bd81b445 100644 --- a/cpp_src/core/transaction/transactionsteps.h +++ b/cpp_src/core/transaction/transactionsteps.h @@ -55,17 +55,24 @@ class TransactionStep { class TransactionSteps { public: - void Insert(Item &&item, lsn_t lsn) { steps_.emplace_back(TransactionStep{std::move(item), ModeInsert, lsn}); } - void Update(Item &&item, lsn_t lsn) { steps_.emplace_back(TransactionStep{std::move(item), ModeUpdate, lsn}); } - void Upsert(Item &&item, lsn_t lsn) { steps_.emplace_back(TransactionStep{std::move(item), ModeUpsert, lsn}); } - void Delete(Item &&item, lsn_t lsn) { steps_.emplace_back(TransactionStep{std::move(item), ModeDelete, lsn}); } - void Modify(Item &&item, ItemModifyMode mode, lsn_t lsn) { steps_.emplace_back(TransactionStep{std::move(item), mode, lsn}); } - void Modify(Query &&query, lsn_t lsn) { steps_.emplace_back(TransactionStep(std::move(query), lsn)); } - void Nop(lsn_t lsn) { steps_.emplace_back(TransactionStep{lsn}); } - void PutMeta(std::string_view key, std::string_view value, lsn_t lsn) { steps_.emplace_back(TransactionStep{key, value, lsn}); } - void SetTagsMatcher(TagsMatcher &&tm, lsn_t lsn) { steps_.emplace_back(TransactionStep{std::move(tm), lsn}); } + void Insert(Item &&item, lsn_t lsn) { steps.emplace_back(std::move(item), ModeInsert, lsn); } + void Update(Item &&item, lsn_t lsn) { steps.emplace_back(std::move(item), ModeUpdate, lsn); } + void Upsert(Item &&item, lsn_t lsn) { steps.emplace_back(std::move(item), ModeUpsert, lsn); } + void Delete(Item &&item, lsn_t lsn) { steps.emplace_back(std::move(item), ModeDelete, lsn); } + void Modify(Item &&item, ItemModifyMode mode, lsn_t lsn) { + hasDeleteItemSteps_ = hasDeleteItemSteps_ || (mode == ModeDelete); + steps.emplace_back(std::move(item), mode, lsn); + } + void Modify(Query &&query, lsn_t lsn) { steps.emplace_back(std::move(query), lsn); } + void Nop(lsn_t lsn) { steps.emplace_back(lsn); } + void PutMeta(std::string_view key, std::string_view value, lsn_t lsn) { steps.emplace_back(key, value, lsn); } + void SetTagsMatcher(TagsMatcher &&tm, lsn_t lsn) { steps.emplace_back(std::move(tm), lsn); } + bool HasDeleteItemSteps() const noexcept { return hasDeleteItemSteps_; } + + std::vector steps; - std::vector steps_; +private: + bool hasDeleteItemSteps_ = false; }; } // namespace reindexer diff --git a/cpp_src/core/type_consts.h b/cpp_src/core/type_consts.h index 0c98e1950..a018f5414 100644 --- a/cpp_src/core/type_consts.h +++ b/cpp_src/core/type_consts.h @@ -68,7 +68,10 @@ typedef enum QueryItemType { QueryUpdateFieldV2 = 25, QueryBetweenFieldsCondition = 26, QueryAlwaysFalseCondition = 27, - QueryLocal = 28, + QueryAlwaysTrueCondition = 28, + QuerySubQueryCondition = 29, + QueryFieldSubQueryCondition = 30, + QueryLocal = 31, } QueryItemType; typedef enum QuerySerializeMode { @@ -158,7 +161,8 @@ enum QueryResultItemType { QueryResultAggregation = 1, QueryResultExplain = 2, QueryResultShardingVersion = 3, - QueryResultShardId = 4 + QueryResultShardId = 4, + QueryResultIncarnationTags = 5, }; enum CacheMode { CacheModeOn = 0, CacheModeAggressive = 1, CacheModeOff = 2 }; @@ -355,6 +359,7 @@ enum ShardingAlgorithmType { ByValue, ByRange }; enum BindingCapability { kBindingCapabilityQrIdleTimeouts = 1, kBindingCapabilityResultsWithShardIDs = 1 << 1, + kBindingCapabilityIncarnationTags = 1 << 2, }; typedef struct BindingCapabilities { @@ -362,22 +367,15 @@ typedef struct BindingCapabilities { constexpr BindingCapabilities(int64_t c = 0) noexcept : caps(c) {} bool HasQrIdleTimeouts() const noexcept { return caps & kBindingCapabilityQrIdleTimeouts; } - BindingCapabilities& WithQrIdleTimeouts(bool value = true) noexcept { - caps = value ? caps | kBindingCapabilityQrIdleTimeouts : caps & ~int64_t(kBindingCapabilityQrIdleTimeouts); - return *this; - } bool HasResultsWithShardIDs() const noexcept { return caps & kBindingCapabilityResultsWithShardIDs; } - BindingCapabilities& WithResultsWithShardIDs(bool value = true) noexcept { - caps = value ? caps | kBindingCapabilityResultsWithShardIDs : caps & ~int64_t(kBindingCapabilityResultsWithShardIDs); - return *this; - } + bool HasIncarnationTags() const noexcept { return caps & kBindingCapabilityIncarnationTags; } #endif int64_t caps; } BindingCapabilities; typedef struct RPCQrId { #ifdef __cplusplus - explicit RPCQrId(int m = -1, int64_t u = -1) : main(m), uid(u) {} + explicit RPCQrId(int m = -1, int64_t u = -1) noexcept : main(m), uid(u) {} #endif int main; int64_t uid; @@ -387,6 +385,28 @@ typedef struct RPCQrId { namespace ShardingKeyType { enum ShardingKey { ProxyOff = -2, NotSetShard = -1, NotSharded = -3 }; } +namespace ShardingSourceId { +enum SourceId { NotSet = -1 }; +} #endif static const char kSerialPrefix[] = "_SERIAL_"; + +// REINDEX_WITH_V3_FOLLOWERS +enum SubscriptionOpt { + kSubscriptionOptIncrementSubscription = 1 << 0, +}; + +typedef struct SubscriptionOpts { +#ifdef __cplusplus + SubscriptionOpts() : options(0) {} + + bool IsIncrementSubscription() const { return options & kSubscriptionOptIncrementSubscription; } + SubscriptionOpts& IncrementSubscription(bool value = true) { + options = value ? options | kSubscriptionOptIncrementSubscription : options & ~(kSubscriptionOptIncrementSubscription); + return *this; + } +#endif + uint16_t options; +} SubscriptionOpts; +// REINDEX_WITH_V3_FOLLOWERS diff --git a/cpp_src/core/type_consts_helpers.cc b/cpp_src/core/type_consts_helpers.cc index 18fd15e1c..60db10409 100644 --- a/cpp_src/core/type_consts_helpers.cc +++ b/cpp_src/core/type_consts_helpers.cc @@ -110,6 +110,8 @@ namespace reindexer { return "unknown"sv; } +} // namespace reindexer + [[nodiscard]] std::string_view JoinTypeName(JoinType type) { using namespace std::string_view_literals; @@ -126,5 +128,3 @@ namespace reindexer { assertrx(false); return "unknown"sv; } - -} // namespace reindexer diff --git a/cpp_src/core/type_consts_helpers.h b/cpp_src/core/type_consts_helpers.h index 2fc5e36df..fa78bdd50 100644 --- a/cpp_src/core/type_consts_helpers.h +++ b/cpp_src/core/type_consts_helpers.h @@ -11,6 +11,18 @@ namespace reindexer { [[nodiscard]] std::string_view TagTypeToStr(TagType); [[nodiscard]] std::string_view AggTypeToStr(AggType t) noexcept; +constexpr bool IsComposite(IndexType type) noexcept { + return type == IndexCompositeBTree || type == IndexCompositeFastFT || type == IndexCompositeFuzzyFT || type == IndexCompositeHash; +} + +constexpr bool IsFullText(IndexType type) noexcept { + return type == IndexFastFT || type == IndexFuzzyFT || type == IndexCompositeFastFT || type == IndexCompositeFuzzyFT; +} + +constexpr bool IsFastFullText(IndexType type) noexcept { return type == IndexFastFT || type == IndexCompositeFastFT; } + +} // namespace reindexer + /// Get readable Join Type /// @param type - join type /// @returns string with join type name @@ -60,6 +72,19 @@ auto& operator<<(T& os, OpType op) { std::abort(); } +inline std::string_view OpTypeToStr(OpType op) { + using namespace std::string_view_literals; + switch (op) { + case OpOr: + return "OR"sv; + case OpAnd: + return "AND"sv; + case OpNot: + return "NOT"sv; + } + std::abort(); +} + template auto& operator<<(T& os, JoinType jt) { return os << JoinTypeName(jt); @@ -132,15 +157,3 @@ T& operator<<(T& os, CollateMode m) { } std::abort(); } - -constexpr bool IsComposite(IndexType type) noexcept { - return type == IndexCompositeBTree || type == IndexCompositeFastFT || type == IndexCompositeFuzzyFT || type == IndexCompositeHash; -} - -constexpr bool IsFullText(IndexType type) noexcept { - return type == IndexFastFT || type == IndexFuzzyFT || type == IndexCompositeFastFT || type == IndexCompositeFuzzyFT; -} - -constexpr bool IsFastFullText(IndexType type) noexcept { return type == IndexFastFT || type == IndexCompositeFastFT; } - -} // namespace reindexer diff --git a/cpp_src/debug/backtrace.cc b/cpp_src/debug/backtrace.cc index 2c74393ba..e333d263a 100644 --- a/cpp_src/debug/backtrace.cc +++ b/cpp_src/debug/backtrace.cc @@ -3,6 +3,7 @@ #include #include #include +#include "tools/fsops.h" #ifndef WIN32 #include #include @@ -193,6 +194,8 @@ void backtrace_init() noexcept { sigaction(SIGBUS, &sa, nullptr); } +void set_minidump_path(const std::string &) { assert(false); } + void backtrace_set_writer(backtrace_writer_t writer) { std::lock_guard lck(g_mutex); g_writer = std::move(writer); @@ -213,6 +216,102 @@ crash_query_reporter_t backtrace_get_crash_query_reporter() { } // namespace debug } // namespace reindexer +#elif defined(WIN32) && defined(REINDEX_WITH_CPPTRACE) +#include +#include +#include +#undef min +#undef max +#include +#include "cpptrace/cpptrace.hpp" + +namespace reindexer { +namespace debug { +static std::recursive_mutex g_mutex; +static crash_query_reporter_t g_crash_query_reporter = [](std::ostream &) {}; +static backtrace_writer_t g_writer = [](std::string_view sv) { std::cerr << sv; }; +static std::string g_pathMiniDump; + +void outputDebugInfo(const backtrace_writer_t &writer, EXCEPTION_POINTERS *ExceptionInfo) { + std::ostringstream sout; + print_crash_query(sout); + writer(sout.str()); + sout.str(std::string()); + sout.clear(); + print_backtrace(sout, nullptr, 0); + writer(sout.str()); + + const auto tm = std::chrono::system_clock::now(); + const auto duration = tm.time_since_epoch(); + int64_t now = std::chrono::duration_cast(duration).count(); + + std::string fileName = fs::JoinPath(g_pathMiniDump, std::string("crash") + std::to_string(now) + ".dmp"); + HANDLE hFile = CreateFile(fileName.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + std::shared_ptr fileClose{hFile, CloseHandle}; + if (ExceptionInfo) { + MINIDUMP_EXCEPTION_INFORMATION mei; + mei.ThreadId = GetCurrentThreadId(); + mei.ClientPointers = TRUE; + mei.ExceptionPointers = ExceptionInfo; + MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &mei, NULL, NULL); + } else { + MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, NULL, NULL, NULL); + } + + sout.clear(); + sout << "MiniDump file " << fileName.c_str() << std::endl; + writer(sout.str()); +} + +LONG WINAPI exceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) { + const auto writer = backtrace_get_writer(); + std::ostringstream sout; + sout << "*** Backtrace on: " << std::hex << pExceptionInfo->ExceptionRecord->ExceptionCode << std::dec << " ***" << std::endl; + writer(sout.str()); + outputDebugInfo(writer, pExceptionInfo); + return EXCEPTION_CONTINUE_SEARCH; +} + +void signalAbortHandler(int /*signal*/) { + const auto writer = backtrace_get_writer(); + std::ostringstream sout; + sout << "*** Backtrace on: abort SIGNAL ***" << std::endl; + writer(sout.str()); + outputDebugInfo(writer, nullptr); +} + +void backtrace_init() noexcept { + std::signal(SIGABRT, signalAbortHandler); + SetUnhandledExceptionFilter(exceptionHandler); +} + +void set_minidump_path(const std::string &p) { g_pathMiniDump = p; } + +void backtrace_set_writer(backtrace_writer_t writer) { + std::lock_guard lck(g_mutex); + g_writer = std::move(writer); +} +int backtrace_internal(void **, size_t, void *, std::string_view &) { return 0; } +void backtrace_set_crash_query_reporter(crash_query_reporter_t reporter) { + std::lock_guard lck(g_mutex); + g_crash_query_reporter = std::move(reporter); +} +backtrace_writer_t backtrace_get_writer() { + std::lock_guard lck(g_mutex); + return g_writer; +} +crash_query_reporter_t backtrace_get_crash_query_reporter() { + std::lock_guard lck(g_mutex); + return g_crash_query_reporter; +} +void print_backtrace(std::ostream &sout, void *, int) { cpptrace::generate_trace().print(sout, false); } +void print_crash_query(std::ostream &sout) { + auto reporter = backtrace_get_crash_query_reporter(); + if (reporter) reporter(sout); +} + +} // namespace debug +} // namespace reindexer #else namespace reindexer { namespace debug { @@ -221,6 +320,7 @@ static crash_query_reporter_t g_crash_query_reporter = [](std::ostream &) {}; static backtrace_writer_t g_writer = [](std::string_view sv) { std::cerr << sv; }; void backtrace_init() noexcept {} +void set_minidump_path(const std::string &) { assert(false); } void backtrace_set_writer(backtrace_writer_t) {} int backtrace_internal(void **, size_t, void *, std::string_view &) { return 0; } void backtrace_set_crash_query_reporter(crash_query_reporter_t reporter) { @@ -243,5 +343,4 @@ void print_crash_query(std::ostream &sout) { } // namespace debug } // namespace reindexer - #endif diff --git a/cpp_src/debug/backtrace.h b/cpp_src/debug/backtrace.h index 416616f49..285291bc5 100644 --- a/cpp_src/debug/backtrace.h +++ b/cpp_src/debug/backtrace.h @@ -10,6 +10,7 @@ using backtrace_writer_t = std::function; using crash_query_reporter_t = std::function; void backtrace_init() noexcept; +void set_minidump_path(const std::string& p); void backtrace_set_writer(backtrace_writer_t); void backtrace_set_crash_query_reporter(crash_query_reporter_t); backtrace_writer_t backtrace_get_writer(); diff --git a/cpp_src/debug/terminate_handler.cpp b/cpp_src/debug/terminate_handler.cpp index 7269ccde5..b2a365d56 100644 --- a/cpp_src/debug/terminate_handler.cpp +++ b/cpp_src/debug/terminate_handler.cpp @@ -30,6 +30,7 @@ static void terminate_handler() { } catch (Error &err) { sout << ": " << err.what(); } catch (...) { + sout << ": "; } sout << " ***" << std::endl; } else { diff --git a/cpp_src/estl/chunk.h b/cpp_src/estl/chunk.h index e66e7dabb..1174b60af 100644 --- a/cpp_src/estl/chunk.h +++ b/cpp_src/estl/chunk.h @@ -7,7 +7,8 @@ namespace reindexer { class chunk { public: - chunk() : data_(nullptr), len_(0), offset_(0), cap_(0) {} + chunk() noexcept : data_(nullptr), len_(0), offset_(0), cap_(0) {} + chunk(uint8_t *data, size_t len, size_t cap, size_t offset = 0) noexcept : data_(data), len_(len), offset_(offset), cap_(cap) {} ~chunk() { delete[] data_; } chunk(const chunk &) = delete; chunk &operator=(const chunk &) = delete; @@ -35,21 +36,56 @@ class chunk { } return *this; } - void append(std::string_view data) { + void append(std::string_view data) { append_impl(data, std::max(size_t(0x1000), size_t(len_ + data.size()))); } + void append_strict(std::string_view data) { append_impl(data, len_ + data.size()); } + + size_t size() const noexcept { return len_ - offset_; } + uint8_t *data() const noexcept { return data_ + offset_; } + size_t capacity() const noexcept { return cap_; } + size_t len() const noexcept { return len_; } + size_t offset() const noexcept { return offset_; } + + void clear() noexcept { + len_ = 0; + offset_ = 0; + } + void shift(size_t offset) noexcept { offset_ += offset; } + uint8_t *release() noexcept { + auto res = data_; + data_ = nullptr; + return res; + } + + void shrink(size_t k) { + if (k * size() >= cap_) { + return; + } + + cap_ = k * size(); + uint8_t *newdata = new uint8_t[cap_]; + if (data_) { + memcpy(newdata, data(), size()); + len_ = size(); + offset_ = 0; + } + delete[] data_; + data_ = newdata; + } + +private: + void append_impl(std::string_view data, size_t newCapacity) { if (!data_ || len_ + data.size() > cap_) { - cap_ = std::max(size_t(0x1000), size_t(len_ + data.size())); + cap_ = newCapacity; uint8_t *newdata = new uint8_t[cap_]; if (data_) { memcpy(newdata, data_, len_); } - delete data_; + delete[] data_; data_ = newdata; } memcpy(data_ + len_, data.data(), data.size()); len_ += data.size(); } - size_t size() { return len_ - offset_; } - uint8_t *data() { return data_ + offset_; } uint8_t *data_; size_t len_; diff --git a/cpp_src/estl/chunk_buf.h b/cpp_src/estl/chunk_buf.h index 3ee04cc05..daed5c466 100644 --- a/cpp_src/estl/chunk_buf.h +++ b/cpp_src/estl/chunk_buf.h @@ -45,13 +45,12 @@ class chain_buf { assertrx(head_ != tail_); chunk &cur = ring_[tail_]; if (cur.size() > nread) { - cur.offset_ += nread; + cur.shift(nread); break; } nread -= cur.size(); - cur.len_ = 0; - cur.offset_ = 0; - if (free_.size() < ring_.size() && cur.cap_ < 0x10000) + cur.clear(); + if (free_.size() < ring_.size() && cur.capacity() < 0x10000) free_.push_back(std::move(cur)); else cur = chunk(); diff --git a/cpp_src/estl/contexted_locks.h b/cpp_src/estl/contexted_locks.h index ca468b841..a503d76c5 100644 --- a/cpp_src/estl/contexted_locks.h +++ b/cpp_src/estl/contexted_locks.h @@ -13,32 +13,27 @@ using std::adopt_lock_t; namespace reindexer { -const milliseconds kDefaultCondChkTime = milliseconds(20); +constexpr milliseconds kDefaultCondChkTime = milliseconds(20); template class contexted_unique_lock { public: using MutexType = _Mutex; - explicit contexted_unique_lock() : _M_mtx(nullptr), _M_owns(false), _M_context(nullptr), _M_chkTimeout(kDefaultCondChkTime) {} - explicit contexted_unique_lock(MutexType& __mtx, Context* __context, milliseconds __chkTimeout = kDefaultCondChkTime) - : _M_mtx(&__mtx), _M_owns(false), _M_context(__context), _M_chkTimeout(__chkTimeout) { - assertrx(_M_context); + explicit contexted_unique_lock() noexcept : _M_mtx(nullptr), _M_owns(false), _M_context(nullptr), _M_chkTimeout(kDefaultCondChkTime) {} + explicit contexted_unique_lock(MutexType& __mtx, Context& __context, milliseconds __chkTimeout = kDefaultCondChkTime) + : _M_mtx(&__mtx), _M_owns(false), _M_context(&__context), _M_chkTimeout(__chkTimeout) { lock(); } - explicit contexted_unique_lock(MutexType& __mtx, defer_lock_t, Context* __context, milliseconds __chkTimeout = kDefaultCondChkTime) - : _M_mtx(&__mtx), _M_owns(false), _M_context(__context), _M_chkTimeout(__chkTimeout) { - assertrx(_M_context); - } - explicit contexted_unique_lock(MutexType& __mtx, adopt_lock_t, Context* __context, milliseconds __chkTimeout = kDefaultCondChkTime) - : _M_mtx(&__mtx), _M_owns(true), _M_context(__context), _M_chkTimeout(__chkTimeout) { - assertrx(_M_context); - } - explicit contexted_unique_lock(MutexType& __mtx, try_to_lock_t, Context* __context, milliseconds __chkTimeout = kDefaultCondChkTime) - : _M_mtx(&__mtx), _M_owns(__mtx.try_lock()), _M_context(__context), _M_chkTimeout(__chkTimeout) { - assertrx(_M_context); - } - contexted_unique_lock(contexted_unique_lock&& lck) + explicit contexted_unique_lock(MutexType& __mtx, defer_lock_t, Context& __context, + milliseconds __chkTimeout = kDefaultCondChkTime) noexcept + : _M_mtx(&__mtx), _M_owns(false), _M_context(&__context), _M_chkTimeout(__chkTimeout) {} + explicit contexted_unique_lock(MutexType& __mtx, adopt_lock_t, Context& __context, + milliseconds __chkTimeout = kDefaultCondChkTime) noexcept + : _M_mtx(&__mtx), _M_owns(true), _M_context(&__context), _M_chkTimeout(__chkTimeout) {} + explicit contexted_unique_lock(MutexType& __mtx, try_to_lock_t, Context& __context, milliseconds __chkTimeout = kDefaultCondChkTime) + : _M_mtx(&__mtx), _M_owns(__mtx.try_lock()), _M_context(&__context), _M_chkTimeout(__chkTimeout) {} + contexted_unique_lock(contexted_unique_lock&& lck) noexcept : _M_mtx(lck._M_mtx), _M_owns(lck._M_owns), _M_context(lck._M_context), _M_chkTimeout(lck._M_chkTimeout) { lck._M_owns = false; lck._M_mtx = nullptr; @@ -50,7 +45,7 @@ class contexted_unique_lock { contexted_unique_lock(const contexted_unique_lock&) = delete; contexted_unique_lock& operator=(const contexted_unique_lock&) = delete; - contexted_unique_lock& operator=(contexted_unique_lock&& lck) { + contexted_unique_lock& operator=(contexted_unique_lock&& lck) noexcept { if (this != &lck) { if (_M_owns) unlock(); _M_mtx = lck._M_mtx; @@ -102,7 +97,7 @@ class contexted_unique_lock { MutexType* mutex() const noexcept { return _M_mtx; } private: - void _M_lockable() const { + void _M_lockable() const noexcept { if (_M_mtx == nullptr) assertrx(0); if (_M_owns) assertrx(0); } @@ -118,21 +113,17 @@ class contexted_shared_lock { public: using MutexType = _Mutex; - explicit contexted_shared_lock() : _M_mtx(nullptr), _M_owns(false), _M_context(nullptr), _M_chkTimeout(kDefaultCondChkTime) {} - explicit contexted_shared_lock(MutexType& __mtx, Context* __context, milliseconds __chkTimeout = kDefaultCondChkTime) - : _M_mtx(&__mtx), _M_owns(false), _M_context(__context), _M_chkTimeout(__chkTimeout) { - assertrx(_M_context); + explicit contexted_shared_lock() noexcept : _M_mtx(nullptr), _M_owns(false), _M_context(nullptr), _M_chkTimeout(kDefaultCondChkTime) {} + explicit contexted_shared_lock(MutexType& __mtx, Context& __context, milliseconds __chkTimeout = kDefaultCondChkTime) + : _M_mtx(&__mtx), _M_owns(false), _M_context(&__context), _M_chkTimeout(__chkTimeout) { lock(); } - explicit contexted_shared_lock(MutexType& __mtx, adopt_lock_t, Context* __context, milliseconds __chkTimeout = kDefaultCondChkTime) - : _M_mtx(&__mtx), _M_owns(true), _M_context(__context), _M_chkTimeout(__chkTimeout) { - assertrx(_M_context); - } - explicit contexted_shared_lock(MutexType& __mtx, try_to_lock_t, Context* __context, milliseconds __chkTimeout = kDefaultCondChkTime) - : _M_mtx(&__mtx), _M_owns(__mtx.try_lock()), _M_context(__context), _M_chkTimeout(__chkTimeout) { - assertrx(_M_context); - } - contexted_shared_lock(contexted_shared_lock&& lck) + explicit contexted_shared_lock(MutexType& __mtx, adopt_lock_t, Context& __context, + milliseconds __chkTimeout = kDefaultCondChkTime) noexcept + : _M_mtx(&__mtx), _M_owns(true), _M_context(&__context), _M_chkTimeout(__chkTimeout) {} + explicit contexted_shared_lock(MutexType& __mtx, try_to_lock_t, Context& __context, milliseconds __chkTimeout = kDefaultCondChkTime) + : _M_mtx(&__mtx), _M_owns(__mtx.try_lock()), _M_context(&__context), _M_chkTimeout(__chkTimeout) {} + contexted_shared_lock(contexted_shared_lock&& lck) noexcept : _M_mtx(lck._M_mtx), _M_owns(lck._M_owns), _M_context(lck._M_context), _M_chkTimeout(lck._M_chkTimeout) { lck._M_owns = false; lck._M_mtx = nullptr; @@ -144,7 +135,7 @@ class contexted_shared_lock { contexted_shared_lock(const contexted_shared_lock&) = delete; contexted_shared_lock& operator=(const contexted_shared_lock&) = delete; - contexted_shared_lock& operator=(contexted_shared_lock&& lck) { + contexted_shared_lock& operator=(contexted_shared_lock&& lck) noexcept { if (this != &lck) { if (_M_owns) unlock(); _M_mtx = lck._M_mtx; @@ -196,7 +187,7 @@ class contexted_shared_lock { MutexType* mutex() const noexcept { return _M_mtx; } private: - void _M_lockable() const { + void _M_lockable() const noexcept { if (_M_mtx == nullptr) assertrx(0); if (_M_owns) assertrx(0); } diff --git a/cpp_src/estl/h_vector.h b/cpp_src/estl/h_vector.h index 48fc8de84..56333004d 100644 --- a/cpp_src/estl/h_vector.h +++ b/cpp_src/estl/h_vector.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include "debug_macros.h" @@ -123,7 +124,7 @@ class h_vector { return *this; } - bool operator==(const h_vector& other) const noexcept { + bool operator==(const h_vector& other) const noexcept(noexcept(std::declval() == std::declval())) { if (&other != this) { if (size() != other.size()) return false; for (size_t i = 0; i < size(); ++i) { @@ -133,7 +134,9 @@ class h_vector { } return true; } - bool operator!=(const h_vector& other) const noexcept { return !operator==(other); } + bool operator!=(const h_vector& other) const noexcept(noexcept(std::declval() == std::declval())) { + return !operator==(other); + } template void clear() noexcept { diff --git a/cpp_src/estl/suffix_map.h b/cpp_src/estl/suffix_map.h index 37864d4cf..18ae3e179 100644 --- a/cpp_src/estl/suffix_map.h +++ b/cpp_src/estl/suffix_map.h @@ -203,6 +203,6 @@ class suffix_map { std::vector mapped_; std::vector text_; bool built_ = false; -}; // namespace reindexer +}; } // namespace reindexer diff --git a/cpp_src/estl/tokenizer.cc b/cpp_src/estl/tokenizer.cc index 67b9780da..7c12b8de9 100644 --- a/cpp_src/estl/tokenizer.cc +++ b/cpp_src/estl/tokenizer.cc @@ -16,8 +16,9 @@ void tokenizer::skip_space() noexcept { cur_++; pos_++; } - } else + } else { return; + } } } diff --git a/cpp_src/estl/tokenizer.h b/cpp_src/estl/tokenizer.h index 399083e19..b6904a372 100644 --- a/cpp_src/estl/tokenizer.h +++ b/cpp_src/estl/tokenizer.h @@ -13,8 +13,8 @@ class token { token(token_type t = TokenSymbol) noexcept : type(t) {} token(const token &) = delete; token &operator=(const token &) = delete; - token(token &&other) = default; - token &operator=(token &&other) = default; + token(token &&) noexcept = default; + token &operator=(token &&) noexcept = default; std::string_view text() const noexcept { return std::string_view(text_.data(), text_.size()); } void to_lower() noexcept { @@ -45,7 +45,7 @@ class tokenizer { tokenizer(std::string_view query) noexcept : q_(query), cur_(query.begin()) {} token next_token(flags f = flags(flags::to_lower)); - token peek_token(flags f = flags(flags::to_lower)) { + [[nodiscard]] token peek_token(flags f = flags(flags::to_lower)) { auto save_cur = cur_; auto save_pos = pos_; auto res = next_token(f); @@ -53,6 +53,17 @@ class tokenizer { pos_ = save_pos; return res; } + [[nodiscard]] token peek_second_token(flags f = flags(flags::to_lower)) { + auto save_cur = cur_; + auto save_pos = pos_; + auto res = next_token(f); + if (res.type != TokenEnd) { + res = next_token(f); + } + cur_ = save_cur; + pos_ = save_pos; + return res; + } void skip_space() noexcept; bool end() const noexcept { return cur_ == q_.end(); } size_t getPos() const noexcept { return pos_; } @@ -65,7 +76,7 @@ class tokenizer { size_t length() const noexcept { return q_.length(); } const char *begin() const noexcept { return q_.data(); } -protected: +private: std::string_view q_; std::string_view::const_iterator cur_; size_t pos_ = 0; diff --git a/cpp_src/gtests/bench/fixtures/aggregation.cc b/cpp_src/gtests/bench/fixtures/aggregation.cc index 3641684f3..6523df608 100644 --- a/cpp_src/gtests/bench/fixtures/aggregation.cc +++ b/cpp_src/gtests/bench/fixtures/aggregation.cc @@ -1,6 +1,6 @@ #include "aggregation.h" #include "core/cjson/jsonbuilder.h" -#include "tools/random.h" +#include "tools/randompoint.h" template void Aggregation::Insert(State& state) { diff --git a/cpp_src/gtests/bench/fixtures/api_tv_composite.cc b/cpp_src/gtests/bench/fixtures/api_tv_composite.cc index fdeceac96..dc131e080 100644 --- a/cpp_src/gtests/bench/fixtures/api_tv_composite.cc +++ b/cpp_src/gtests/bench/fixtures/api_tv_composite.cc @@ -299,13 +299,14 @@ void ApiTvComposite::RangeHashCompositeIntStr(benchmark::State& state) { } void ApiTvComposite::RangeTreeIntSortByHashInt(State& state) { + using namespace std::string_view_literals; AllocsTracker allocsTracker(state); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) Query q(nsdef_.name); auto idRange = id_seq_->GetRandomIdRange(id_seq_->Count() * 0.02); - q.Where("id", CondRange, {idRange.first, idRange.second}).Sort("age", false).Limit(20); + q.Where("id", CondRange, {idRange.first, idRange.second}).Sort("age"sv, false).Limit(20); QueryResults qres; auto err = db_->Select(q, qres); @@ -314,13 +315,14 @@ void ApiTvComposite::RangeTreeIntSortByHashInt(State& state) { } void ApiTvComposite::RangeTreeIntSortByTreeInt(State& state) { + using namespace std::string_view_literals; AllocsTracker allocsTracker(state); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) Query q(nsdef_.name); auto idRange = id_seq_->GetRandomIdRange(id_seq_->Count() * 0.02); - q.Where("id", CondRange, {idRange.first, idRange.second}).Sort("year", false).Limit(20); + q.Where("id"sv, CondRange, {idRange.first, idRange.second}).Sort("year"sv, false).Limit(20); QueryResults qres; auto err = db_->Select(q, qres); @@ -329,13 +331,14 @@ void ApiTvComposite::RangeTreeIntSortByTreeInt(State& state) { } void ApiTvComposite::RangeTreeStrSortByHashInt(benchmark::State& state) { + using namespace std::string_view_literals; AllocsTracker allocsTracker(state); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) Query q(nsdef_.name); auto idRange = id_seq_->GetRandomIdRange(id_seq_->Count() * 0.02); - q.Where("id", CondRange, {std::to_string(idRange.first), std::to_string(idRange.second)}).Sort("age", false).Limit(20); + q.Where("id"sv, CondRange, {std::to_string(idRange.first), std::to_string(idRange.second)}).Sort("age"sv, false).Limit(20); QueryResults qres; auto err = db_->Select(q, qres); @@ -344,13 +347,14 @@ void ApiTvComposite::RangeTreeStrSortByHashInt(benchmark::State& state) { } void ApiTvComposite::RangeTreeStrSortByTreeInt(benchmark::State& state) { + using namespace std::string_view_literals; AllocsTracker allocsTracker(state); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) Query q(nsdef_.name); auto idRange = id_seq_->GetRandomIdRange(id_seq_->Count() * 0.02); - q.Where("id", CondRange, {std::to_string(idRange.first), std::to_string(idRange.second)}).Sort("year", false).Limit(20); + q.Where("id"sv, CondRange, {std::to_string(idRange.first), std::to_string(idRange.second)}).Sort("year"sv, false).Limit(20); QueryResults qres; auto err = db_->Select(q, qres); @@ -359,6 +363,7 @@ void ApiTvComposite::RangeTreeStrSortByTreeInt(benchmark::State& state) { } void ApiTvComposite::RangeTreeDoubleSortByTreeInt(benchmark::State& state) { + using namespace std::string_view_literals; AllocsTracker allocsTracker(state); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) Query q(nsdef_.name); @@ -366,7 +371,7 @@ void ApiTvComposite::RangeTreeDoubleSortByTreeInt(benchmark::State& state) { auto leftRate = random(0.0, 4.99); auto rightRate = random(5.0, 10.0); - q.Where("rate", CondRange, {leftRate, rightRate}).Sort("year", false).Limit(20); + q.Where("rate"sv, CondRange, {leftRate, rightRate}).Sort("year"sv, false).Limit(20); QueryResults qres; auto err = db_->Select(q, qres); @@ -375,6 +380,7 @@ void ApiTvComposite::RangeTreeDoubleSortByTreeInt(benchmark::State& state) { } void ApiTvComposite::RangeTreeDoubleSortByHashInt(benchmark::State& state) { + using namespace std::string_view_literals; AllocsTracker allocsTracker(state); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) Query q(nsdef_.name); @@ -382,7 +388,7 @@ void ApiTvComposite::RangeTreeDoubleSortByHashInt(benchmark::State& state) { auto leftRate = random(0.0, 4.99); auto rightRate = random(5.0, 10.0); - q.Where("rate", CondRange, {leftRate, rightRate}).Sort("age", false).Limit(20); + q.Where("rate"sv, CondRange, {leftRate, rightRate}).Sort("age"sv, false).Limit(20); QueryResults qres; auto err = db_->Select(q, qres); @@ -391,13 +397,14 @@ void ApiTvComposite::RangeTreeDoubleSortByHashInt(benchmark::State& state) { } void ApiTvComposite::RangeTreeStrSortByHashStrCollateASCII(benchmark::State& state) { + using namespace std::string_view_literals; AllocsTracker allocsTracker(state); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) Query q(nsdef_.name); auto idRange = id_seq_->GetRandomIdRange(id_seq_->Count() * 0.02); - q.Where("id", CondRange, {std::to_string(idRange.first), std::to_string(idRange.second)}).Sort("location", false).Limit(20); + q.Where("id"sv, CondRange, {std::to_string(idRange.first), std::to_string(idRange.second)}).Sort("location"sv, false).Limit(20); QueryResults qres; auto err = db_->Select(q, qres); @@ -406,13 +413,14 @@ void ApiTvComposite::RangeTreeStrSortByHashStrCollateASCII(benchmark::State& sta } void ApiTvComposite::RangeTreeStrSortByHashStrCollateUTF8(benchmark::State& state) { + using namespace std::string_view_literals; AllocsTracker allocsTracker(state); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) Query q(nsdef_.name); auto idRange = id_seq_->GetRandomIdRange(id_seq_->Count() * 0.02); - q.Where("id", CondRange, {std::to_string(idRange.first), std::to_string(idRange.second)}).Sort("name", false).Limit(20); + q.Where("id"sv, CondRange, {std::to_string(idRange.first), std::to_string(idRange.second)}).Sort("name"sv, false).Limit(20); QueryResults qres; auto err = db_->Select(q, qres); @@ -421,11 +429,12 @@ void ApiTvComposite::RangeTreeStrSortByHashStrCollateUTF8(benchmark::State& stat } void ApiTvComposite::SortByHashInt(benchmark::State& state) { + using namespace std::string_view_literals; AllocsTracker allocsTracker(state); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) Query q(nsdef_.name); - q.Sort("id", false).Limit(20); + q.Sort("id"sv, false).Limit(20); QueryResults qres; auto err = db_->Select(q, qres); @@ -434,11 +443,12 @@ void ApiTvComposite::SortByHashInt(benchmark::State& state) { } void ApiTvComposite::ForcedSortByHashInt(benchmark::State& state) { + using namespace std::string_view_literals; AllocsTracker allocsTracker(state); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) Query q(nsdef_.name); - q.Sort("id", false, {10, 20, 30, 40, 50}).Limit(20); + q.Sort("id"sv, false, {10, 20, 30, 40, 50}).Limit(20); QueryResults qres; auto err = db_->Select(q, qres); @@ -447,11 +457,12 @@ void ApiTvComposite::ForcedSortByHashInt(benchmark::State& state) { } void ApiTvComposite::ForcedSortWithSecondCondition(benchmark::State& state) { + using namespace std::string_view_literals; AllocsTracker allocsTracker(state); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) Query q(nsdef_.name); - q.Sort("id", false, {10, 20, 30, 40, 50}).Sort("location", false).Limit(20); + q.Sort("id"sv, false, {10, 20, 30, 40, 50}).Sort("location"sv, false).Limit(20); QueryResults qres; auto err = db_->Select(q, qres); @@ -474,11 +485,12 @@ void ApiTvComposite::Query2CondIdSetComposite(benchmark::State& state) { } void ApiTvComposite::SortByHashStrCollateASCII(benchmark::State& state) { + using namespace std::string_view_literals; AllocsTracker allocsTracker(state); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) Query q(nsdef_.name); - q.Sort("location", false).Limit(20); + q.Sort("location"sv, false).Limit(20); QueryResults qres; auto err = db_->Select(q, qres); @@ -487,11 +499,12 @@ void ApiTvComposite::SortByHashStrCollateASCII(benchmark::State& state) { } void ApiTvComposite::SortByHashStrCollateUTF8(benchmark::State& state) { + using namespace std::string_view_literals; AllocsTracker allocsTracker(state); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) Query q(nsdef_.name); - q.Sort("name", false).Limit(20); + q.Sort("name"sv, false).Limit(20); QueryResults qres; auto err = db_->Select(q, qres); @@ -500,11 +513,12 @@ void ApiTvComposite::SortByHashStrCollateUTF8(benchmark::State& state) { } void ApiTvComposite::SortByHashCompositeIntInt(benchmark::State& state) { + using namespace std::string_view_literals; AllocsTracker allocsTracker(state); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) Query q(nsdef_.name); - q.Sort("id+start_time", false).Limit(20); + q.Sort("id+start_time"sv, false).Limit(20); QueryResults qres; auto err = db_->Select(q, qres); @@ -513,11 +527,12 @@ void ApiTvComposite::SortByHashCompositeIntInt(benchmark::State& state) { } void ApiTvComposite::SortByHashCompositeIntStr(benchmark::State& state) { + using namespace std::string_view_literals; AllocsTracker allocsTracker(state); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) Query q(nsdef_.name); - q.Sort("id+genre", false).Limit(20); + q.Sort("id+genre"sv, false).Limit(20); QueryResults qres; auto err = db_->Select(q, qres); @@ -526,11 +541,12 @@ void ApiTvComposite::SortByHashCompositeIntStr(benchmark::State& state) { } void ApiTvComposite::SortByTreeCompositeIntInt(benchmark::State& state) { + using namespace std::string_view_literals; AllocsTracker allocsTracker(state); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) Query q(nsdef_.name); - q.Sort("id+year", false).Limit(20); + q.Sort("id+year"sv, false).Limit(20); QueryResults qres; auto err = db_->Select(q, qres); @@ -539,11 +555,12 @@ void ApiTvComposite::SortByTreeCompositeIntInt(benchmark::State& state) { } void ApiTvComposite::SortByTreeCompositeIntStrCollateUTF8(benchmark::State& state) { + using namespace std::string_view_literals; AllocsTracker allocsTracker(state); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) Query q(nsdef_.name); - q.Sort("id+name", false).Limit(20); + q.Sort("id+name"sv, false).Limit(20); QueryResults qres; auto err = db_->Select(q, qres); diff --git a/cpp_src/gtests/bench/fixtures/api_tv_simple.cc b/cpp_src/gtests/bench/fixtures/api_tv_simple.cc index a8b2650a1..7c8cec8b9 100644 --- a/cpp_src/gtests/bench/fixtures/api_tv_simple.cc +++ b/cpp_src/gtests/bench/fixtures/api_tv_simple.cc @@ -81,9 +81,9 @@ void ApiTvSimple::RegisterAllCases() { Register("Query2CondLeftJoin", &ApiTvSimple::Query2CondLeftJoin, this); Register("Query2CondLeftJoinTotal", &ApiTvSimple::Query2CondLeftJoinTotal, this); Register("Query2CondLeftJoinCachedTotal", &ApiTvSimple::Query2CondLeftJoinCachedTotal, this); - Register("Query0CondInnerJoinUnlimit", &ApiTvSimple::Query0CondInnerJoinUnlimit, this); - Register("Query0CondInnerJoinUnlimitLowSelectivity", &ApiTvSimple::Query0CondInnerJoinUnlimitLowSelectivity, this); - Register("Query0CondInnerJoinPreResultStoreValues", &ApiTvSimple::Query0CondInnerJoinPreResultStoreValues, this); + Register("Query0CondInnerJoinUnlimit", &ApiTvSimple::Query0CondInnerJoinUnlimit, this)->Iterations(500); + Register("Query0CondInnerJoinUnlimitLowSelectivity", &ApiTvSimple::Query0CondInnerJoinUnlimitLowSelectivity, this)->Iterations(500); + Register("Query0CondInnerJoinPreResultStoreValues", &ApiTvSimple::Query0CondInnerJoinPreResultStoreValues, this)->Iterations(500); Register("Query2CondInnerJoin", &ApiTvSimple::Query2CondInnerJoin, this); Register("Query2CondInnerJoinTotal", &ApiTvSimple::Query2CondInnerJoinTotal, this); Register("Query2CondInnerJoinCachedTotal", &ApiTvSimple::Query2CondInnerJoinCachedTotal, this); @@ -95,9 +95,9 @@ void ApiTvSimple::RegisterAllCases() { Register("Query4Cond", &ApiTvSimple::Query4Cond, this); Register("Query4CondTotal", &ApiTvSimple::Query4CondTotal, this); Register("Query4CondCachedTotal", &ApiTvSimple::Query4CondCachedTotal, this); - Register("Query4CondRange", &ApiTvSimple::Query4CondRange, this); - Register("Query4CondRangeTotal", &ApiTvSimple::Query4CondRangeTotal, this); - Register("Query4CondRangeCachedTotal", &ApiTvSimple::Query4CondRangeCachedTotal, this); + Register("Query4CondRange", &ApiTvSimple::Query4CondRange, this)->Iterations(1000); + Register("Query4CondRangeTotal", &ApiTvSimple::Query4CondRangeTotal, this)->Iterations(1000); + Register("Query4CondRangeCachedTotal", &ApiTvSimple::Query4CondRangeCachedTotal, this)->Iterations(1000); #if !defined(REINDEX_WITH_ASAN) && !defined(REINDEX_WITH_TSAN) && !defined(RX_WITH_STDLIB_DEBUG) Register("Query2CondIdSet10", &ApiTvSimple::Query2CondIdSet10, this); #endif // !defined(REINDEX_WITH_ASAN) && !defined(REINDEX_WITH_TSAN) @@ -108,8 +108,15 @@ void ApiTvSimple::RegisterAllCases() { Register("Query2CondIdSet20000", &ApiTvSimple::Query2CondIdSet20000, this); #endif // !defined(REINDEX_WITH_ASAN) && !defined(REINDEX_WITH_TSAN) && !defined(RX_WITH_STDLIB_DEBUG) Register("FromCJSON", &ApiTvSimple::FromCJSON, this); + Register("FromCJSONPKOnly", &ApiTvSimple::FromCJSONPKOnly, this); Register("GetCJSON", &ApiTvSimple::GetCJSON, this); - // NOLINTEND(*cplusplus.NewDeleteLeaks) + Register("ExtractField", &ApiTvSimple::ExtractField, this); + + // Those benches should be last, because they are recreating indexes cache + Register("Query4CondRangeDropCache", &ApiTvSimple::Query4CondRangeDropCache, this)->Iterations(1000); + Register("Query4CondRangeDropCacheTotal", &ApiTvSimple::Query4CondRangeDropCacheTotal, this)->Iterations(1000); + Register("Query4CondRangeDropCacheCachedTotal", &ApiTvSimple::Query4CondRangeDropCacheCachedTotal, this)->Iterations(1000); + // NOLINTEND(*cplusplus.NewDeleteLeaks) } reindexer::Error ApiTvSimple::Initialize() { @@ -140,6 +147,7 @@ reindexer::Error ApiTvSimple::Initialize() { uuids_.emplace_back(randStrUuid()); } + devices_ = {"iphone", "android", "smarttv", "stb", "ottstb"}; locations_ = {"mos", "ct", "dv", "sth", "vlg", "sib", "ural"}; for (int i = 0; i < 10; i++) packages_.emplace_back(randomNumArray(20, 10000, 10)); @@ -263,12 +271,13 @@ reindexer::Error ApiTvSimple::prepareCJsonBench() { auto err = db_->AddNamespace(cjsonNsDef); if (!err.ok()) return err; + fieldsToExtract_.clear(); itemForCjsonBench_ = std::make_unique(db_->NewItem(cjsonNsName_)); if (!itemForCjsonBench_->Status().ok()) return itemForCjsonBench_->Status(); wrSer_.Reset(); reindexer::JsonBuilder bld(wrSer_); constexpr size_t len = 10; - bld.Put("id", 0); + bld.Put("id", kCjsonBenchItemID); bld.Put("bool_-_index", rand() % 2); bld.Put("int_-_index", rand()); bld.Put("int_hash_index", rand()); @@ -296,20 +305,29 @@ reindexer::Error ApiTvSimple::prepareCJsonBench() { bld.Array("string_tree_array_index", randStringArray()); for (size_t i = 0; i < 10; ++i) { const std::string i_str = std::to_string(i); + fieldsToExtract_.emplace_back("bool_field_" + i_str); bld.Put("bool_field_" + i_str, rand() % 2); + fieldsToExtract_.emplace_back("int_field_" + i_str); bld.Put("int_field_" + i_str, rand()); + fieldsToExtract_.emplace_back("double_field_" + i_str); bld.Put("double_field_" + i_str, rand() / double(rand() + 1)); + fieldsToExtract_.emplace_back("string_field_" + i_str); bld.Put("string_field_" + i_str, randString(len)); bld.Array("bool_array_field_" + i_str, randBoolArray()); bld.Array("int_array_field_" + i_str, randIntArray()); bld.Array("double_array_field_" + i_str, randDoubleArray()); bld.Array("string_array_field_" + i_str, randStringArray()); { - auto obj = bld.Object("nested_obj_" + i_str); + const std::string nestedBase("nested_obj_" + i_str); + auto obj = bld.Object(nestedBase); obj.Put("bool_field", rand() % 2); + fieldsToExtract_.emplace_back(nestedBase + ".bool_field"); obj.Put("int_field", rand()); + fieldsToExtract_.emplace_back(nestedBase + ".int_field"); obj.Put("double_field", rand() / double(rand() + 1)); + fieldsToExtract_.emplace_back(nestedBase + ".double_field"); obj.Put("string_field", randString(len)); + fieldsToExtract_.emplace_back(nestedBase + ".string_field"); obj.Array("bool_array_field", randBoolArray()); obj.Array("int_array_field", randIntArray()); obj.Array("double_array_field", randDoubleArray()); @@ -453,6 +471,30 @@ void ApiTvSimple::FromCJSON(benchmark::State& state) { } } +void ApiTvSimple::FromCJSONPKOnly(benchmark::State& state) { + reindexer::Item item = db_->NewItem(cjsonNsName_); + { + AllocsTracker allocsTracker(state); + for (auto _ : state) { // NOLINT(*deadcode.DeadStores) + const auto err = item.FromCJSON(cjsonOfItem_, true); + if (!err.ok()) state.SkipWithError(err.what().c_str()); + if (!item.Status().ok()) state.SkipWithError(item.Status().what().c_str()); + } + } + assertrx(item["id"].Get() == kCjsonBenchItemID); +} + +void ApiTvSimple::ExtractField(benchmark::State& state) { + assertrx(itemForCjsonBench_); + assertrx(fieldsToExtract_.size()); + AllocsTracker allocsTracker(state); + for (auto _ : state) { // NOLINT(*deadcode.DeadStores) + const auto& fieldName = fieldsToExtract_[rand() % fieldsToExtract_.size()]; + const auto va = VariantArray((*itemForCjsonBench_)[fieldName]); + if (va.size() != 1) state.SkipWithError(fmt::sprintf("Unexpected result size: %d", va.size()).c_str()); + } +} + void ApiTvSimple::StringsSelect(benchmark::State& state) { AllocsTracker allocsTracker(state); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) @@ -738,18 +780,25 @@ void ApiTvSimple::Query2CondLeftJoinCachedTotal(benchmark::State& state) { void ApiTvSimple::Query0CondInnerJoinUnlimit(benchmark::State& state) { AllocsTracker allocsTracker(state); + size_t num = 0; for (auto _ : state) { // NOLINT(*deadcode.DeadStores) Query q4join("JoinItems"); Query q(nsdef_.name); q.ReqTotal().Limit(1); - q4join.Where("device", CondEq, "ottstb").Where("location", CondEq, "mos"); + // Results depend on the joined query expected results size. If preselect is disabled, then + // timing will be much higher. To stabilize benchmark result we are using all combinations of 'device' and 'location' + size_t deviceId = num % devices_.size(); + size_t locationId = (num / devices_.size()) % locations_.size(); + + q4join.Where("device", CondEq, devices_[deviceId]).Where("location", CondEq, locations_[locationId]); q.InnerJoin("price_id", "id", CondSet, std::move(q4join)); QueryResults qres; auto err = db_->Select(q, qres); if (!err.ok()) state.SkipWithError(err.what().c_str()); + ++num; } } @@ -1028,7 +1077,7 @@ void ApiTvSimple::Query4CondCachedTotal(benchmark::State& state) { void ApiTvSimple::Query4CondRange(benchmark::State& state) { AllocsTracker allocsTracker(state); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) - int startTime = random(0, 50000); + int startTime = random(0, 30000); int endTime = startTime + 10000; Query q(nsdef_.name); q.Limit(20) @@ -1047,7 +1096,7 @@ void ApiTvSimple::Query4CondRange(benchmark::State& state) { void ApiTvSimple::Query4CondRangeTotal(benchmark::State& state) { AllocsTracker allocsTracker(state); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) - int startTime = random(0, 50000); + int startTime = random(0, 30000); int endTime = startTime + 10000; Query q(nsdef_.name); q.Limit(20) @@ -1067,7 +1116,72 @@ void ApiTvSimple::Query4CondRangeTotal(benchmark::State& state) { void ApiTvSimple::Query4CondRangeCachedTotal(benchmark::State& state) { AllocsTracker allocsTracker(state); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) - int startTime = random(0, 50000); + int startTime = random(0, 30000); + int endTime = startTime + 10000; + Query q(nsdef_.name); + q.Limit(20) + .Sort("year", false) + .Where("genre", CondEq, 5) + .Where("year", CondRange, {2010, 2016}) + .Where("start_time", CondGt, startTime) + .Where("end_time", CondLt, endTime) + .CachedTotal(); + + QueryResults qres; + auto err = db_->Select(q, qres); + if (!err.ok()) state.SkipWithError(err.what().c_str()); + } +} + +void ApiTvSimple::Query4CondRangeDropCache(benchmark::State& state) { + IndexCacheSetter cacheDropper(*db_); + + AllocsTracker allocsTracker(state); + for (auto _ : state) { // NOLINT(*deadcode.DeadStores) + int startTime = random(0, 30000); + int endTime = startTime + 10000; + Query q(nsdef_.name); + q.Limit(20) + .Sort("year", false) + .Where("genre", CondEq, 5) + .Where("year", CondRange, {2010, 2016}) + .Where("start_time", CondGt, startTime) + .Where("end_time", CondLt, endTime); + + QueryResults qres; + auto err = db_->Select(q, qres); + if (!err.ok()) state.SkipWithError(err.what().c_str()); + } +} + +void ApiTvSimple::Query4CondRangeDropCacheTotal(benchmark::State& state) { + IndexCacheSetter cacheDropper(*db_); + + AllocsTracker allocsTracker(state); + for (auto _ : state) { // NOLINT(*deadcode.DeadStores) + int startTime = random(0, 30000); + int endTime = startTime + 10000; + Query q(nsdef_.name); + q.Limit(20) + .Sort("year", false) + .Where("genre", CondEq, 5) + .Where("year", CondRange, {2010, 2016}) + .Where("start_time", CondGt, startTime) + .Where("end_time", CondLt, endTime) + .ReqTotal(); + + QueryResults qres; + auto err = db_->Select(q, qres); + if (!err.ok()) state.SkipWithError(err.what().c_str()); + } +} + +void ApiTvSimple::Query4CondRangeDropCacheCachedTotal(benchmark::State& state) { + IndexCacheSetter cacheDropper(*db_); + + AllocsTracker allocsTracker(state); + for (auto _ : state) { // NOLINT(*deadcode.DeadStores) + int startTime = random(0, 30000); int endTime = startTime + 10000; Query q(nsdef_.name); q.Limit(20) diff --git a/cpp_src/gtests/bench/fixtures/api_tv_simple.h b/cpp_src/gtests/bench/fixtures/api_tv_simple.h index 7f1587d6b..97a0362d2 100644 --- a/cpp_src/gtests/bench/fixtures/api_tv_simple.h +++ b/cpp_src/gtests/bench/fixtures/api_tv_simple.h @@ -28,6 +28,43 @@ class ApiTvSimple : private BaseFixture { reindexer::Error Initialize() override; private: + class IndexCacheSetter { + public: + constexpr static unsigned kVeryLargeHitsValue = 1000000; + + IndexCacheSetter(reindexer::Reindexer& db, unsigned hitsCount = kVeryLargeHitsValue) : db_(db) { + shrinkCache(); + setHitsCount(hitsCount); + } + ~IndexCacheSetter() { setHitsCount(kDefaultCacheHits); } + + private: + constexpr static int64_t kDefaultCacheSize = 134217728; + constexpr static int64_t kDefaultCacheHits = 2; + + void shrinkCache() { + // Shrink cache size to force cache invalidation + auto q = reindexer::Query("#config").Set("namespaces.cache.index_idset_cache_size", 1024).Where("type", CondEq, "namespaces"); + reindexer::QueryResults qr; + auto err = db_.Update(q, qr); + assertrx(err.ok()); + assertrx(qr.Count() == 1); + } + void setHitsCount(unsigned hitsCount) { + // Set required hits count and default cache size + auto q = reindexer::Query("#config") + .Set("namespaces.cache.index_idset_cache_size", kDefaultCacheSize) + .Set("namespaces.cache.index_idset_hits_to_cache", int64_t(hitsCount)) + .Where("type", CondEq, "namespaces"); + reindexer::QueryResults qr; + auto err = db_.Update(q, qr); + assertrx(err.ok()); + assertrx(qr.Count() == 1); + } + + reindexer::Reindexer& db_; + }; + reindexer::Item MakeItem(benchmark::State&) override; reindexer::Item MakeStrItem(); @@ -80,7 +117,12 @@ class ApiTvSimple : private BaseFixture { void Query4CondRangeTotal(State& state); void Query4CondRangeCachedTotal(State& state); void FromCJSON(State&); + void FromCJSONPKOnly(State&); void GetCJSON(State&); + void ExtractField(State&); + void Query4CondRangeDropCache(State& state); + void Query4CondRangeDropCacheTotal(State& state); + void Query4CondRangeDropCacheCachedTotal(State& state); void query2CondIdSet(State& state, const std::vector>& idsets); reindexer::Error prepareCJsonBench(); @@ -88,6 +130,7 @@ class ApiTvSimple : private BaseFixture { std::vector countries_; std::vector countryLikePatterns_; std::vector locations_; + std::vector devices_; std::vector start_times_; std::vector> packages_; std::vector> priceIDs_; @@ -108,5 +151,7 @@ class ApiTvSimple : private BaseFixture { std::string innerJoinLowSelectivityRightNs_{"inner_join_low_selectivity_right_ns"}; std::string cjsonNsName_{"cjson_ns_name"}; std::unique_ptr itemForCjsonBench_; + std::vector fieldsToExtract_; + constexpr static int kCjsonBenchItemID = 9973; std::string cjsonOfItem_; }; diff --git a/cpp_src/gtests/bench/fixtures/api_tv_simple_comparators.cc b/cpp_src/gtests/bench/fixtures/api_tv_simple_comparators.cc index 48a1306f0..30846061c 100644 --- a/cpp_src/gtests/bench/fixtures/api_tv_simple_comparators.cc +++ b/cpp_src/gtests/bench/fixtures/api_tv_simple_comparators.cc @@ -454,7 +454,7 @@ void ApiTvSimpleComparators::Query4CondCachedTotal(benchmark::State& state) { void ApiTvSimpleComparators::Query4CondRange(benchmark::State& state) { AllocsTracker allocsTracker(state); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) - int startTime = random(0, 50000); + int startTime = random(0, 30000); int endTime = startTime + 10000; Query q(nsdef_.name); q.Limit(20) @@ -473,7 +473,7 @@ void ApiTvSimpleComparators::Query4CondRange(benchmark::State& state) { void ApiTvSimpleComparators::Query4CondRangeTotal(benchmark::State& state) { AllocsTracker allocsTracker(state); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) - int startTime = random(0, 50000); + int startTime = random(0, 30000); int endTime = startTime + 10000; Query q(nsdef_.name); q.Limit(20) @@ -493,7 +493,7 @@ void ApiTvSimpleComparators::Query4CondRangeTotal(benchmark::State& state) { void ApiTvSimpleComparators::Query4CondRangeCachedTotal(benchmark::State& state) { AllocsTracker allocsTracker(state); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) - int startTime = random(0, 50000); + int startTime = random(0, 30000); int endTime = startTime + 10000; Query q(nsdef_.name); q.Limit(20) diff --git a/cpp_src/gtests/bench/fixtures/ft_fixture.cc b/cpp_src/gtests/bench/fixtures/ft_fixture.cc index ea479521f..b0f103cc9 100644 --- a/cpp_src/gtests/bench/fixtures/ft_fixture.cc +++ b/cpp_src/gtests/bench/fixtures/ft_fixture.cc @@ -7,6 +7,7 @@ #include "core/cjson/jsonbuilder.h" #include "core/ft/config/ftfastconfig.h" +#include "tools/errors.h" #include "tools/stringstools.h" #include @@ -111,51 +112,50 @@ reindexer::Error FullText::Initialize() { return {}; } -void FullText::RegisterAllCases(size_t iterationCount) { +void FullText::RegisterAllCases(std::optional fastIterationCount, std::optional slowIterationCount) { constexpr static auto Mem = reindexer::FtFastConfig::Optimization::Memory; constexpr static auto CPU = reindexer::FtFastConfig::Optimization::CPU; - RegisterWrapper wrap(iterationCount); //-1 test limit - default time + RegisterWrapper wrapSlow(slowIterationCount); // std::numeric_limits::max() test limit - default time + RegisterWrapper wrapFast(fastIterationCount); // NOLINTBEGIN(*cplusplus.NewDeleteLeaks) Register("BuildAndInsertNs2", &FullText::BuildAndInsertLowWordsDiversityNs, this)->Iterations(1000)->Unit(benchmark::kMicrosecond); - wrap.SetOptions(Register("Fast3PhraseLowDiversity", &FullText::Fast3PhraseLowDiversity, this)); - wrap.SetOptions(Register("Fast3WordsLowDiversity", &FullText::Fast3WordsLowDiversity, this)); + wrapSlow.SetOptions(Register("Fast3PhraseLowDiversity", &FullText::Fast3PhraseLowDiversity, this)); + wrapSlow.SetOptions(Register("Fast3WordsLowDiversity", &FullText::Fast3WordsLowDiversity, this)); - wrap.SetOptions(Register("Fast3WordsWithAreasLowDiversity", &FullText::Fast3WordsWithAreasLowDiversity, this)); - wrap.SetOptions(Register("Fast3PhraseWithAreasLowDiversity", &FullText::Fast3PhraseWithAreasLowDiversity, this)); + wrapSlow.SetOptions(Register("Fast3WordsWithAreasLowDiversity", &FullText::Fast3WordsWithAreasLowDiversity, this)); + wrapSlow.SetOptions(Register("Fast3PhraseWithAreasLowDiversity", &FullText::Fast3PhraseWithAreasLowDiversity, this)); - wrap.SetOptions(Register("Fast2PhraseLowDiversity", &FullText::Fast2PhraseLowDiversity, this)); - wrap.SetOptions(Register("Fast2AndWordLowDiversity", &FullText::Fast2AndWordLowDiversity, this)); + wrapFast.SetOptions(Register("Fast2PhraseLowDiversity", &FullText::Fast2PhraseLowDiversity, this)); + wrapFast.SetOptions(Register("Fast2AndWordLowDiversity", &FullText::Fast2AndWordLowDiversity, this)); Register("Insert", &FullText::Insert, this)->Iterations(id_seq_->Count())->Unit(benchmark::kMicrosecond); - // Register("BuildCommonIndexes", &FullText::BuildCommonIndexes, this)->Iterations(1)->Unit(benchmark::kMicrosecond); Register("BuildFastTextIndex", &FullText::BuildFastTextIndex, this)->Iterations(1)->Unit(benchmark::kMicrosecond); - // Register("BuildFuzzyTextIndex", &FullText::BuildFuzzyTextIndex, this)->Iterations(1)->Unit(benchmark::kMicrosecond); - - wrap.SetOptions(Register("Fast1WordMatch.OptByMem", &FullText::Fast1WordMatch, this)); - wrap.SetOptions(Register("Fast2WordsMatch.OptByMem", &FullText::Fast2WordsMatch, this)); - wrap.SetOptions(Register("Fast1PrefixMatch.OptByMem", &FullText::Fast1PrefixMatch, this)); - wrap.SetOptions(Register("Fast2PrefixMatch.OptByMem", &FullText::Fast2PrefixMatch, this)); - wrap.SetOptions(Register("Fast1SuffixMatch.OptByMem", &FullText::Fast1SuffixMatch, this)); - - wrap.SetOptions(Register("Fast2SuffixMatch.OptByMem", &FullText::Fast2SuffixMatch, this)); - wrap.SetOptions(Register("Fast1TypoWordMatch.OptByMem", &FullText::Fast1TypoWordMatch, this)); - wrap.SetOptions(Register("Fast2TypoWordMatch.OptByMem", &FullText::Fast2TypoWordMatch, this)); - wrap.SetOptions(Register("Fast1WordWithAreaHighDiversity.optByMem", &FullText::Fast1WordWithAreaHighDiversity, this)); + wrapFast.SetOptions(Register("Fast1WordMatch.OptByMem", &FullText::Fast1WordMatch, this)); + wrapFast.SetOptions(Register("Fast2WordsMatch.OptByMem", &FullText::Fast2WordsMatch, this)); + wrapSlow.SetOptions(Register("Fast1PrefixMatch.OptByMem", &FullText::Fast1PrefixMatch, this)); + wrapSlow.SetOptions(Register("Fast2PrefixMatch.OptByMem", &FullText::Fast2PrefixMatch, this)); + wrapSlow.SetOptions(Register("Fast1SuffixMatch.OptByMem", &FullText::Fast1SuffixMatch, this)); + wrapSlow.SetOptions(Register("Fast2SuffixMatch.OptByMem", &FullText::Fast2SuffixMatch, this)); + wrapFast.SetOptions(Register("Fast1TypoWordMatch.OptByMem", &FullText::Fast1TypoWordMatch, this)); + wrapFast.SetOptions(Register("Fast2TypoWordMatch.OptByMem", &FullText::Fast2TypoWordMatch, this)); + wrapFast.SetOptions(Register("Fast1WordWithAreaHighDiversity.OptByMem", &FullText::Fast1WordWithAreaHighDiversity, this)); Register("SetOptimizationByCPU", &FullText::UpdateIndex, this)->Iterations(1)->Unit(benchmark::kMicrosecond); - wrap.SetOptions(Register("Fast1WordMatch.OptByCPU", &FullText::Fast1WordMatch, this)); + wrapFast.SetOptions(Register("Fast1WordMatch.OptByCPU", &FullText::Fast1WordMatch, this)); + wrapFast.SetOptions(Register("Fast2WordsMatch.OptByCPU", &FullText::Fast2WordsMatch, this)); + wrapSlow.SetOptions(Register("Fast1PrefixMatch.OptByCPU", &FullText::Fast1PrefixMatch, this)); + wrapSlow.SetOptions(Register("Fast2PrefixMatch.OptByCPU", &FullText::Fast2PrefixMatch, this)); + wrapSlow.SetOptions(Register("Fast1SuffixMatch.OptByCPU", &FullText::Fast1SuffixMatch, this)); + wrapSlow.SetOptions(Register("Fast2SuffixMatch.OptByCPU", &FullText::Fast2SuffixMatch, this)); + wrapFast.SetOptions(Register("Fast1TypoWordMatch.OptByCPU", &FullText::Fast1TypoWordMatch, this)); + wrapFast.SetOptions(Register("Fast2TypoWordMatch.OptByCPU", &FullText::Fast2TypoWordMatch, this)); + wrapFast.SetOptions(Register("Fast1WordWithAreaHighDiversity.OptByCPU", &FullText::Fast1WordWithAreaHighDiversity, this)); - wrap.SetOptions(Register("Fast2WordsMatch.OptByCPU", &FullText::Fast2WordsMatch, this)); - wrap.SetOptions(Register("Fast1PrefixMatch.OptByCPU", &FullText::Fast1PrefixMatch, this)); - wrap.SetOptions(Register("Fast2PrefixMatch.OptByCPU", &FullText::Fast2PrefixMatch, this)); - wrap.SetOptions(Register("Fast1SuffixMatch.OptByCPU", &FullText::Fast1SuffixMatch, this)); - wrap.SetOptions(Register("Fast2SuffixMatch.OptByCPU", &FullText::Fast2SuffixMatch, this)); - wrap.SetOptions(Register("Fast1TypoWordMatch.OptByCPU", &FullText::Fast1TypoWordMatch, this)); - wrap.SetOptions(Register("Fast2TypoWordMatch.OptByCPU", &FullText::Fast2TypoWordMatch, this)); + // Register("BuildFuzzyTextIndex", &FullText::BuildFuzzyTextIndex, this)->Iterations(1)->Unit(benchmark::kMicrosecond); // Register("Fuzzy1WordMatch", &FullText::Fuzzy1WordMatch, this)->Unit(benchmark::kMicrosecond); // Register("Fuzzy2WordsMatch", &FullText::Fuzzy2WordsMatch, this)->Unit(benchmark::kMicrosecond); @@ -197,17 +197,19 @@ void FullText::RegisterAllCases(size_t iterationCount) { Register("InitForAlternatingUpdatesAndSelects.OptByMem", &FullText::InitForAlternatingUpdatesAndSelects, this) ->Iterations(1) ->Unit(benchmark::kMicrosecond); - wrap.SetOptions(Register("AlternatingUpdatesAndSelects.OptByMem", &FullText::AlternatingUpdatesAndSelects, this)); - wrap.SetOptions(Register("AlternatingUpdatesAndSelectsByComposite.OptByMem", &FullText::AlternatingUpdatesAndSelectsByComposite, this)); - wrap.SetOptions(Register("AlternatingUpdatesAndSelectsByCompositeByNotIndexFields.OptByMem", - &FullText::AlternatingUpdatesAndSelectsByCompositeByNotIndexFields, this)); + wrapFast.SetOptions(Register("AlternatingUpdatesAndSelects.OptByMem", &FullText::AlternatingUpdatesAndSelects, this)); + wrapFast.SetOptions( + Register("AlternatingUpdatesAndSelectsByComposite.OptByMem", &FullText::AlternatingUpdatesAndSelectsByComposite, this)); + wrapFast.SetOptions(Register("AlternatingUpdatesAndSelectsByCompositeByNotIndexFields.OptByMem", + &FullText::AlternatingUpdatesAndSelectsByCompositeByNotIndexFields, this)); Register("InitForAlternatingUpdatesAndSelects.OptByCPU", &FullText::InitForAlternatingUpdatesAndSelects, this) ->Iterations(1) ->Unit(benchmark::kMicrosecond); - wrap.SetOptions(Register("AlternatingUpdatesAndSelects.OptByCPU", &FullText::AlternatingUpdatesAndSelects, this)); - wrap.SetOptions(Register("AlternatingUpdatesAndSelectsByComposite.OptByCPU", &FullText::AlternatingUpdatesAndSelectsByComposite, this)); - wrap.SetOptions(Register("AlternatingUpdatesAndSelectsByCompositeByNotIndexFields.OptByCPU", - &FullText::AlternatingUpdatesAndSelectsByCompositeByNotIndexFields, this)); + wrapFast.SetOptions(Register("AlternatingUpdatesAndSelects.OptByCPU", &FullText::AlternatingUpdatesAndSelects, this)); + wrapFast.SetOptions( + Register("AlternatingUpdatesAndSelectsByComposite.OptByCPU", &FullText::AlternatingUpdatesAndSelectsByComposite, this)); + wrapFast.SetOptions(Register("AlternatingUpdatesAndSelectsByCompositeByNotIndexFields.OptByCPU", + &FullText::AlternatingUpdatesAndSelectsByCompositeByNotIndexFields, this)); // NOLINTEND(*cplusplus.NewDeleteLeaks) } @@ -241,18 +243,33 @@ reindexer::Item FullText::MakeItem(benchmark::State&) { void FullText::BuildInsertSteps(State& state) { AllocsTracker allocsTracker(state, printFlags); - db_->DropNamespace(nsdef_.name); + auto err = db_->DropNamespace(nsdef_.name); + if (!err.ok()) { + state.SkipWithError(err.what().c_str()); + assertf(err.ok(), "%s", err.what()); + } id_seq_->Reset(); - auto err = BaseFixture::Initialize(); + err = BaseFixture::Initialize(); + if (!err.ok()) { + state.SkipWithError(err.what().c_str()); + assertf(err.ok(), "%s", err.what()); + } size_t i = 0; size_t mem = 0; + assert(!words_.empty()); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) auto item = MakeSpecialItem(); - if (!item.Status().ok()) state.SkipWithError(item.Status().what().c_str()); + if (!item.Status().ok()) { + state.SkipWithError(item.Status().what().c_str()); + assertf(item.Status().ok(), "%s", item.Status().what()); + } err = db_->Insert(nsdef_.name, item); - if (!err.ok()) state.SkipWithError(err.what().c_str()); + if (!err.ok()) { + state.SkipWithError(err.what().c_str()); + assertf(err.ok(), "%s", err.what()); + } if (i % 12000 == 0) { Query q(nsdef_.name); @@ -279,7 +296,10 @@ void FullText::Insert(State& state) { AllocsTracker allocsTracker(state, printFlags); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) auto item = MakeItem(state); - if (!item.Status().ok()) state.SkipWithError(item.Status().what().c_str()); + if (!item.Status().ok()) { + state.SkipWithError(item.Status().what().c_str()); + continue; + } auto err = db_->Insert(nsdef_.name, item); if (!err.ok()) state.SkipWithError(err.what().c_str()); @@ -290,10 +310,11 @@ void FullText::Insert(State& state) { } void FullText::BuildCommonIndexes(benchmark::State& state) { + using namespace std::string_view_literals; AllocsTracker allocsTracker(state, printFlags); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) Query q(nsdef_.name); - q.Where("year", CondRange, {2010, 2016}).Limit(20).Sort("year", false); + q.Where("year"sv, CondRange, {2010, 2016}).Limit(20).Sort("year"sv, false); std::this_thread::sleep_for(std::chrono::milliseconds(2000)); QueryResults qres; @@ -325,7 +346,10 @@ void FullText::BuildAndInsertLowWordsDiversityNs(State& state) { item["description1"] = d1; item["description2"] = d2; - if (!item.Status().ok()) state.SkipWithError(item.Status().what().c_str()); + if (!item.Status().ok()) { + state.SkipWithError(item.Status().what().c_str()); + continue; + } auto err = db_->Insert(lowWordsDiversityNsDef_.name, item); if (!err.ok()) state.SkipWithError(err.what().c_str()); @@ -908,7 +932,13 @@ void FullText::InitForAlternatingUpdatesAndSelects(State& state) { ftCfg.optimization = opt; ftIndexOpts.config = ftCfg.GetJSON({}); AllocsTracker allocsTracker(state, printFlags); - db_->DropNamespace(alternatingNs_); + auto err = db_->DropNamespace(alternatingNs_); + if (!err.ok()) { + if (err.code() != errNotFound || err.what() != "Namespace '" + alternatingNs_ + "' does not exist") { + state.SkipWithError(err.what().c_str()); + assertf(err.ok(), "%s", err.what()); + } + } for (auto _ : state) { // NOLINT(*deadcode.DeadStores) NamespaceDef nsDef{alternatingNs_}; nsDef.AddIndex("id", "hash", "int", IndexOpts().PK()) @@ -918,7 +948,10 @@ void FullText::InitForAlternatingUpdatesAndSelects(State& state) { .AddIndex("search_comp", {"search1", "search2"}, "text", "composite", ftIndexOpts) .AddIndex("search_comp_not_index_fields", {"field1", "field2"}, "text", "composite", ftIndexOpts); auto err = db_->AddNamespace(nsDef); - if (!err.ok()) state.SkipWithError(err.what().c_str()); + if (!err.ok()) { + state.SkipWithError(err.what().c_str()); + assertf(err.ok(), "%s", err.what()); + } values_.clear(); values_.reserve(kNsSize); reindexer::WrSerializer ser; @@ -934,42 +967,51 @@ void FullText::InitForAlternatingUpdatesAndSelects(State& state) { bld.Put("rand", rand()); bld.End(); auto item = db_->NewItem(alternatingNs_); - if (!item.Status().ok()) state.SkipWithError(item.Status().what().c_str()); + if (!item.Status().ok()) { + state.SkipWithError(item.Status().what().c_str()); + continue; + } err = item.FromJSON(ser.Slice()); - if (!err.ok()) state.SkipWithError(err.what().c_str()); + if (!err.ok()) { + state.SkipWithError(err.what().c_str()); + continue; + } err = db_->Insert(alternatingNs_, item); if (!err.ok()) state.SkipWithError(err.what().c_str()); } } - auto err = db_->Commit(alternatingNs_); + err = db_->Commit(alternatingNs_); if (!err.ok()) state.SkipWithError(err.what().c_str()); // Init index build - Query q = + assert(!values_.empty()); + const Query q1 = Query(alternatingNs_) .Where("search1", CondEq, values_[randomGenerator_(randomEngine_, std::uniform_int_distribution::param_type{0, int(values_.size() - 1)})] .search1); QueryResults qres; - err = db_->Select(q, qres); + err = db_->Select(q1, qres); if (!err.ok()) state.SkipWithError(err.what().c_str()); size_t index = randomGenerator_(randomEngine_, std::uniform_int_distribution::param_type{0, int(values_.size() - 1)}); - q = Query(alternatingNs_).Where("search_comp", CondEq, values_[index].search1 + ' ' + values_[index].search2); + const Query q2 = Query(alternatingNs_).Where("search_comp", CondEq, values_[index].search1 + ' ' + values_[index].search2); qres.Clear(); - err = db_->Select(q, qres); + err = db_->Select(q2, qres); if (!err.ok()) state.SkipWithError(err.what().c_str()); index = randomGenerator_(randomEngine_, std::uniform_int_distribution::param_type{0, int(values_.size() - 1)}); - q = Query(alternatingNs_).Where("search_comp_not_index_fields", CondEq, values_[index].field1 + ' ' + values_[index].field2); + const Query q3 = + Query(alternatingNs_).Where("search_comp_not_index_fields", CondEq, values_[index].field1 + ' ' + values_[index].field2); qres.Clear(); - err = db_->Select(q, qres); + err = db_->Select(q3, qres); if (!err.ok()) state.SkipWithError(err.what().c_str()); } void FullText::updateAlternatingNs(reindexer::WrSerializer& ser, benchmark::State& state) { using namespace std::string_literals; + assert(!values_.empty()); const int i = randomGenerator_(randomEngine_, std::uniform_int_distribution::param_type{0, int(values_.size() - 1)}); ser.Reset(); reindexer::JsonBuilder bld(ser); @@ -982,9 +1024,15 @@ void FullText::updateAlternatingNs(reindexer::WrSerializer& ser, benchmark::Stat bld.End(); auto item = db_->NewItem(alternatingNs_); item.Unsafe(false); - if (!item.Status().ok()) state.SkipWithError(item.Status().what().c_str()); + if (!item.Status().ok()) { + state.SkipWithError(item.Status().what().c_str()); + return; + } auto err = item.FromJSON(ser.Slice()); - if (!err.ok()) state.SkipWithError(err.what().c_str()); + if (!err.ok()) { + state.SkipWithError(err.what().c_str()); + return; + } err = db_->Update(alternatingNs_, item); if (!err.ok()) state.SkipWithError(err.what().c_str()); @@ -997,6 +1045,7 @@ void FullText::updateAlternatingNs(reindexer::WrSerializer& ser, benchmark::Stat } void FullText::AlternatingUpdatesAndSelects(benchmark::State& state) { + assert(!values_.empty()); reindexer::WrSerializer ser; AllocsTracker allocsTracker(state, printFlags); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) @@ -1015,6 +1064,7 @@ void FullText::AlternatingUpdatesAndSelects(benchmark::State& state) { } void FullText::AlternatingUpdatesAndSelectsByComposite(benchmark::State& state) { + assert(!values_.empty()); reindexer::WrSerializer ser; AllocsTracker allocsTracker(state, printFlags); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) @@ -1030,6 +1080,7 @@ void FullText::AlternatingUpdatesAndSelectsByComposite(benchmark::State& state) } void FullText::AlternatingUpdatesAndSelectsByCompositeByNotIndexFields(benchmark::State& state) { + assert(!values_.empty()); reindexer::WrSerializer ser; AllocsTracker allocsTracker(state, printFlags); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) diff --git a/cpp_src/gtests/bench/fixtures/ft_fixture.h b/cpp_src/gtests/bench/fixtures/ft_fixture.h index 309c0de75..954e89c7e 100644 --- a/cpp_src/gtests/bench/fixtures/ft_fixture.h +++ b/cpp_src/gtests/bench/fixtures/ft_fixture.h @@ -19,7 +19,7 @@ class FullText : private BaseFixture { FullText(Reindexer* db, const std::string& name, size_t maxItems); virtual reindexer::Error Initialize() override; - void RegisterAllCases(size_t iterationCount = -1); + void RegisterAllCases(std::optional fastIterationCount, std::optional slowIterationCount); private: virtual reindexer::Item MakeItem(benchmark::State&) override; @@ -101,18 +101,16 @@ class FullText : private BaseFixture { class RegisterWrapper { public: - //-1 test iteration limit - time - RegisterWrapper(size_t iterationCoun = -1) : iterationCoun_(iterationCoun) {} - Benchmark* SetOptions(Benchmark* b) { - b = b->Unit(benchmark::kMicrosecond); - if (iterationCoun_ != size_t(-1)) { - b = b->Iterations(iterationCoun_); + RegisterWrapper(std::optional iterationCoun) noexcept : iterationCoun_(iterationCoun) {} + void SetOptions(Benchmark* b) { + if (iterationCoun_.has_value()) { + b = b->Iterations(*iterationCoun_); } - return b; + b->Unit(benchmark::kMicrosecond); } private: - size_t iterationCoun_; + std::optional iterationCoun_; }; #ifdef ENABLE_TIME_TRACKER #define TIMETRACKER(fileName) TimeTracker timeTracker(fileName); @@ -200,7 +198,7 @@ class FullText : private BaseFixture { void updateAlternatingNs(reindexer::WrSerializer&, benchmark::State&); reindexer::Error readDictFile(const std::string& fileName, std::vector& words); - const char* alternatingNs_ = "FtAlternatingUpdatesAndSelects"; + const std::string alternatingNs_ = "FtAlternatingUpdatesAndSelects"; size_t raw_data_sz_ = 0; std::mt19937 randomEngine_{1}; diff --git a/cpp_src/gtests/bench/fixtures/geometry.cc b/cpp_src/gtests/bench/fixtures/geometry.cc index d1a2abc71..1ef73f10c 100644 --- a/cpp_src/gtests/bench/fixtures/geometry.cc +++ b/cpp_src/gtests/bench/fixtures/geometry.cc @@ -1,6 +1,6 @@ #include "geometry.h" #include "core/cjson/jsonbuilder.h" -#include "tools/random.h" +#include "tools/randompoint.h" namespace { @@ -30,7 +30,7 @@ void Geometry::GetDWithin(benchmark::State& state) { benchmark::AllocsTracker allocsTracker(state); for (auto _ : state) { // NOLINT(*deadcode.DeadStores) reindexer::Query q(nsdef_.name); - q.DWithin("point", randPoint(kRange), kRange / N); + q.DWithin("point", reindexer::randPoint(kRange), kRange / N); reindexer::QueryResults qres; auto err = db_->Select(q, qres); if (!err.ok()) state.SkipWithError(err.what().c_str()); @@ -117,7 +117,7 @@ reindexer::Item Geometry::MakeItem(benchmark::State& state) { wrSer_.Reset(); reindexer::JsonBuilder bld(wrSer_); bld.Put("id", id_++); - const reindexer::Point point = randPoint(kRange); + const reindexer::Point point = reindexer::randPoint(kRange); double coords[]{point.X(), point.Y()}; bld.Array("point", reindexer::span(coords, 2)); bld.End(); diff --git a/cpp_src/gtests/bench/ft_bench.cc b/cpp_src/gtests/bench/ft_bench.cc index 2358cd617..02a9ff7f7 100644 --- a/cpp_src/gtests/bench/ft_bench.cc +++ b/cpp_src/gtests/bench/ft_bench.cc @@ -30,30 +30,39 @@ int main(int argc, char** argv) { } shared_ptr DB = std::make_shared(); - DB->Connect("builtin://" + kStoragePath); + auto err = DB->Connect("builtin://" + kStoragePath); + if (!err.ok()) return err.code(); FullText ft(DB.get(), "fulltext", kItemsInBenchDataset); - auto err = ft.Initialize(); + err = ft.Initialize(); if (!err.ok()) return err.code(); ::benchmark::Initialize(&argc, argv); - size_t iterationCount = -1; // count is determined by the execution time + std::optional slowIterationCount; + std::optional fastIterationCount; if (argc > 1) { try { args::ArgumentParser parser("ft_bench additional args"); - args::ValueFlag iterCountF(parser, "ITERCOUNT", "iteration count", {"iteration_count"}, args::Options::Single); + args::ValueFlag siterCountF(parser, "SITERCOUNT", "iteration count for the slow cases", {"slow_iteration_count"}, + args::Options::Single); + args::ValueFlag fiterCountF(parser, "FITERCOUNT", "iteration count for the fast cases", {"fast_iteration_count"}, + args::Options::Single); parser.ParseCLI(argc, argv); - if (iterCountF) { - iterationCount = args::get(iterCountF); - argc--; // only one additional argument, otherwise need to rearrange the argv rows + if (siterCountF) { + slowIterationCount = args::get(siterCountF); + argc--; // sub argument, otherwise need to rearrange the argv rows + } + if (fiterCountF) { + fastIterationCount = args::get(fiterCountF); + argc--; // sub additional argument, otherwise need to rearrange the argv rows } } catch (const args::ParseError& e) { std::cout << "argument parse error '" << e.what() << "'" << std::endl; return 1; } } - ft.RegisterAllCases(iterationCount); + ft.RegisterAllCases(fastIterationCount, slowIterationCount); #ifdef _GLIBCXX_DEBUG ::benchmark::RunSpecifiedBenchmarks(); diff --git a/cpp_src/gtests/bench/reindexer_bench.cc b/cpp_src/gtests/bench/reindexer_bench.cc index 2053b0d1f..216a8d42a 100644 --- a/cpp_src/gtests/bench/reindexer_bench.cc +++ b/cpp_src/gtests/bench/reindexer_bench.cc @@ -39,7 +39,8 @@ int main(int argc, char** argv) { } shared_ptr DB = std::make_shared(); - DB->Connect("builtin://" + kStoragePath); + auto err = DB->Connect("builtin://" + kStoragePath); + if (!err.ok()) return err.code(); JoinItems joinItems(DB.get(), 500); ApiTvSimple apiTvSimple(DB.get(), "ApiTvSimple", kItemsInBenchDataset); @@ -48,7 +49,7 @@ int main(int argc, char** argv) { Geometry geometry(DB.get(), "Geometry", kItemsInBenchDataset); Aggregation aggregation(DB.get(), "Aggregation", kItemsInBenchDataset); - auto err = apiTvSimple.Initialize(); + err = apiTvSimple.Initialize(); if (!err.ok()) return err.code(); err = apiTvSimpleComparators.Initialize(); diff --git a/cpp_src/gtests/tests/API/api.cc b/cpp_src/gtests/tests/API/api.cc index bdb97049f..cf14719c5 100644 --- a/cpp_src/gtests/tests/API/api.cc +++ b/cpp_src/gtests/tests/API/api.cc @@ -1,9 +1,19 @@ #include #include "gtest/gtest.h" +#include "tools/assertrx.h" +#include "tools/fsops.h" -int main(int argc, char* argv[]) { +int main(int argc, char *argv[]) { srand(time(NULL)); ::testing::InitGoogleTest(&argc, argv); + +#ifndef _WIN32 + const char *tmpDir = getenv("REINDEXER_TEST_DB_ROOT"); + if (tmpDir && *tmpDir) { + reindexer::fs::SetTempDir(std::string(tmpDir)); + } +#endif // _WIN32 + return RUN_ALL_TESTS(); } diff --git a/cpp_src/gtests/tests/API/base_tests.cc b/cpp_src/gtests/tests/API/base_tests.cc index a3d276b94..1a1b959c5 100644 --- a/cpp_src/gtests/tests/API/base_tests.cc +++ b/cpp_src/gtests/tests/API/base_tests.cc @@ -22,11 +22,8 @@ #include "server/loggerwrapper.h" #include "tools/serializer.h" -static const std::string kBaseTestsStoragePath = "/tmp/reindex/base_tests"; - TEST(ReindexerTest, DeleteTemporaryNamespaceOnConnect) { - // Change storage path to prevent tests interference by filesystem locks - const std::string kStoragePath = kBaseTestsStoragePath + "_DeleteTemporaryNamespaceOnConnect"; + const auto kStoragePath = reindexer::fs::JoinPath(reindexer::fs::GetTempDir(), "reindex/base_tests/DeleteTemporaryNamespaceOnConnect"); const std::string kBuiltin = "builtin://" + kStoragePath; std::string temporaryNamespacePath; @@ -120,11 +117,14 @@ TEST_F(ReindexerApi, RenameNamespace) { auto getRowsInJSON = [&](const std::string& namespaceName, std::vector& resStrings) { QueryResults result; - rt.reindexer->Select(Query(namespaceName), result); + auto err = rt.reindexer->Select(Query(namespaceName), result); + ASSERT_TRUE(err.ok()) << err.what(); resStrings.clear(); for (auto it = result.begin(); it != result.end(); ++it) { + ASSERT_TRUE(it.Status().ok()) << it.Status().what(); reindexer::WrSerializer sr; - it.GetJSON(sr, false); + err = it.GetJSON(sr, false); + ASSERT_TRUE(err.ok()) << err.what(); std::string_view sv = sr.Slice(); resStrings.emplace_back(sv.data(), sv.size()); } @@ -277,8 +277,7 @@ TEST_F(ReindexerApi, DistinctCompositeIndex) { EXPECT_TRUE(err.ok()) << err.what(); } - Query q; - q._namespace = default_namespace; + Query q{default_namespace}; q.Distinct("v1+v2"); { QueryResults qr; @@ -536,6 +535,7 @@ TEST_F(ReindexerApi, CloseNamespace) { } TEST_F(ReindexerApi, DropStorage) { + const std::string kBaseTestsStoragePath = reindexer::fs::JoinPath(reindexer::fs::GetTempDir(), "reindex/api_drop_storage/"); auto rx = std::make_unique(); auto err = rx->Connect("builtin://" + kBaseTestsStoragePath); ASSERT_TRUE(err.ok()) << err.what(); @@ -1373,9 +1373,8 @@ TEST_F(ReindexerApi, DslFieldsTest) { TEST_F(ReindexerApi, DistinctQueriesEncodingTest) { const std::string sql = "select distinct(country), distinct(city) from clients;"; - Query q1; - q1.FromSQL(sql); - EXPECT_EQ(q1.entries.Size(), 0); + Query q1 = Query::FromSQL(sql); + EXPECT_EQ(q1.Entries().Size(), 0); ASSERT_EQ(q1.aggregations_.size(), 2); EXPECT_EQ(q1.aggregations_[0].Type(), AggDistinct); ASSERT_EQ(q1.aggregations_[0].Fields().size(), 1); @@ -1386,15 +1385,15 @@ TEST_F(ReindexerApi, DistinctQueriesEncodingTest) { std::string dsl = q1.GetJSON(); Query q2; - q2.FromJSON(dsl); - EXPECT_EQ(q1, q2); + const auto err = q2.FromJSON(dsl); + ASSERT_TRUE(err.ok()) << err.what(); + EXPECT_EQ(q1, q2) << "q1: " << q1.GetSQL() << "\nq2: " << q2.GetSQL(); Query q3{Query(default_namespace).Distinct("name").Distinct("city").Where("id", CondGt, static_cast(10))}; std::string sql2 = q3.GetSQL(); - Query q4; - q4.FromSQL(sql2); - EXPECT_EQ(q3, q4); + Query q4 = Query::FromSQL(sql2); + EXPECT_EQ(q3, q4) << "q3: " << q3.GetSQL() << "\nq4: " << q4.GetSQL(); EXPECT_EQ(sql2, q4.GetSQL()); } @@ -1499,21 +1498,19 @@ TEST_F(ReindexerApi, ContextCancelingTest) { } TEST_F(ReindexerApi, JoinConditionsSqlParserTest) { - Query q1, q2; const std::string sql1 = "SELECT * FROM ns WHERE a > 0 AND INNER JOIN (SELECT * FROM ns2 WHERE b > 10 AND c = 1) ON ns2.id = ns.fk_id"; - q1.FromSQL(sql1); + const auto q1 = Query::FromSQL(sql1); ASSERT_EQ(q1.GetSQL(), sql1); const std::string sql2 = "SELECT * FROM ns WHERE a > 0 AND INNER JOIN (SELECT * FROM ns2 WHERE b > 10 AND c = 1 LIMIT 0) ON ns2.id = ns.fk_id"; - q2.FromSQL(sql2); + const auto q2 = Query::FromSQL(sql2); ASSERT_EQ(q2.GetSQL(), sql2); } TEST_F(ReindexerApi, UpdateWithBoolParserTest) { - Query query; const std::string sql = "UPDATE ns SET flag1 = true,flag2 = false WHERE id > 100"; - query.FromSQL(sql); + Query query = Query::FromSQL(sql); ASSERT_EQ(query.UpdateFields().size(), 2); EXPECT_EQ(query.UpdateFields().front().Column(), "flag1"); EXPECT_EQ(query.UpdateFields().front().Mode(), FieldModeSet); @@ -1533,14 +1530,13 @@ TEST_F(ReindexerApi, EqualPositionsSqlParserTest) { "SELECT * FROM ns WHERE (f1 = 1 AND f2 = 2 OR f3 = 3 equal_position(f1, f2) equal_position(f1, f3)) OR (f4 = 4 AND f5 > 5 " "equal_position(f4, f5))"; - Query query; - query.FromSQL(sql); + Query query = Query::FromSQL(sql); EXPECT_EQ(query.GetSQL(), sql); - EXPECT_TRUE(query.entries.equalPositions.empty()); - ASSERT_EQ(query.entries.Size(), 7); + EXPECT_TRUE(query.Entries().equalPositions.empty()); + ASSERT_EQ(query.Entries().Size(), 7); - ASSERT_TRUE(query.entries.IsSubTree(0)); - const auto& ep1 = query.entries.Get(0).equalPositions; + ASSERT_TRUE(query.Entries().IsSubTree(0)); + const auto& ep1 = query.Entries().Get(0).equalPositions; ASSERT_EQ(ep1.size(), 2); ASSERT_EQ(ep1[0].size(), 2); EXPECT_EQ(ep1[0][0], "f1"); @@ -1549,8 +1545,8 @@ TEST_F(ReindexerApi, EqualPositionsSqlParserTest) { EXPECT_EQ(ep1[1][0], "f1"); EXPECT_EQ(ep1[1][1], "f3"); - ASSERT_TRUE(query.entries.IsSubTree(4)); - const auto& ep2 = query.entries.Get(4).equalPositions; + ASSERT_TRUE(query.Entries().IsSubTree(4)); + const auto& ep2 = query.Entries().Get(4).equalPositions; ASSERT_EQ(ep2.size(), 1); ASSERT_EQ(ep2[0].size(), 2); EXPECT_EQ(ep2[0][0], "f4"); @@ -1608,48 +1604,20 @@ TEST_F(ReindexerApi, SchemaSuggestions) { err = rt.reindexer->SetSchema(default_namespace, jsonschema); ASSERT_TRUE(err.ok()) << err.what(); - auto validateSuggestions = [](const std::vector& actual, const std::unordered_set& expected) { - ASSERT_EQ(actual.size(), expected.size()); - for (auto& sugg : actual) { + auto validateSuggestions = [this](std::string_view sql, const std::unordered_set& expected) { + std::vector suggestions; + auto err = rt.reindexer->GetSqlSuggestions(sql, sql.size() - 1, suggestions); + ASSERT_TRUE(err.ok()) << err.what(); + ASSERT_EQ(suggestions.size(), expected.size()) << sql; + for (auto& sugg : suggestions) { EXPECT_TRUE(expected.find(sugg) != expected.end()) << "Unexpected suggestion: " << sugg; } }; - { - std::unordered_set expected = {"Nest_fake", "nested"}; - std::vector suggestions; - std::string_view query = "select * from test_namespace where ne"; - err = rt.reindexer->GetSqlSuggestions(query, query.size() - 1, suggestions); - ASSERT_TRUE(err.ok()) << err.what(); - validateSuggestions(suggestions, expected); - } - - { - std::unordered_set expected; - std::vector suggestions; - std::string_view query = "select * from test_namespace where nested"; - err = rt.reindexer->GetSqlSuggestions(query, query.size() - 1, suggestions); - ASSERT_TRUE(err.ok()) << err.what(); - validateSuggestions(suggestions, expected); - } - - { - std::unordered_set expected = {".Name", ".Naame", ".Age"}; - std::vector suggestions; - std::string_view query = "select * from test_namespace where nested."; - err = rt.reindexer->GetSqlSuggestions(query, query.size() - 1, suggestions); - ASSERT_TRUE(err.ok()) << err.what(); - validateSuggestions(suggestions, expected); - } - - { - std::unordered_set expected = {".Name", ".Naame"}; - std::vector suggestions; - std::string_view query = "select * from test_namespace where nested.Na"; - err = rt.reindexer->GetSqlSuggestions(query, query.size() - 1, suggestions); - ASSERT_TRUE(err.ok()) << err.what(); - validateSuggestions(suggestions, expected); - } + validateSuggestions("select * from test_namespace where ne", {"Nest_fake", "nested"}); + validateSuggestions("select * from test_namespace where nested", {}); + validateSuggestions("select * from test_namespace where nested.", {".Name", ".Naame", ".Age"}); + validateSuggestions("select * from test_namespace where nested.Na", {".Name", ".Naame"}); } TEST_F(ReindexerApi, LoggerWriteInterruptTest) { @@ -1669,7 +1637,7 @@ TEST_F(ReindexerApi, LoggerWriteInterruptTest) { spdlog::drop_all(); std::remove(logFile.c_str()); } - const std::string logFile = "/tmp/logtest.out"; + const std::string logFile = "/tmp/reindex_logtest.out"; reindexer_server::LoggerWrapper logger; std::shared_ptr sinkPtr; } instance; @@ -1742,18 +1710,16 @@ TEST_F(ReindexerApi, SelectFilterWithAggregationConstraints) { Query q; std::string sql = "select id, distinct(year) from test_namespace"; - EXPECT_NO_THROW(q.FromSQL(sql)); + EXPECT_NO_THROW(q = Query::FromSQL(sql)); Error status = Query().FromJSON(q.GetJSON()); EXPECT_TRUE(status.ok()) << status.what(); - q = Query(); - q.selectFilter_.emplace_back("id"); + q = Query().Select({"id"}); EXPECT_NO_THROW(q.Aggregate(AggDistinct, {"year"}, {})); sql = "select id, max(year) from test_namespace"; - EXPECT_THROW(q.FromSQL(sql), Error); - q = Query(default_namespace); - q.selectFilter_.emplace_back("id"); + EXPECT_THROW(q = Query::FromSQL(sql), Error); + q = Query(default_namespace).Select({"id"}); q.aggregations_.emplace_back(reindexer::AggregateEntry{AggMax, {"year"}}); status = Query().FromJSON(q.GetJSON()); EXPECT_FALSE(status.ok()); @@ -1761,23 +1727,39 @@ TEST_F(ReindexerApi, SelectFilterWithAggregationConstraints) { EXPECT_THROW(q.Aggregate(AggMax, {"price"}, {}), Error); sql = "select facet(year), id, name from test_namespace"; - EXPECT_THROW(q.FromSQL(sql), Error); - q = Query(default_namespace); - q.selectFilter_.emplace_back("id"); - q.selectFilter_.emplace_back("name"); + EXPECT_THROW(q = Query::FromSQL(sql), Error); + q = Query(default_namespace).Select({"id", "name"}); EXPECT_THROW(q.Aggregate(AggFacet, {"year"}, {}), Error); - q = Query(default_namespace); - EXPECT_NO_THROW(q.Aggregate(AggFacet, {"year"}, {})); - q.selectFilter_.emplace_back("id"); - q.selectFilter_.emplace_back("name"); - status = Query().FromJSON(q.GetJSON()); + status = Query().FromJSON(fmt::sprintf(R"({"namespace":"%s", + "limit":-1, + "offset":0, + "req_total":"disabled", + "explain":false, + "type":"select", + "select_with_rank":false, + "select_filter":[ + "id", + "name" + ], + "select_functions":[], + "sort":[], + "filters":[], + "merge_queries":[], + "aggregations":[ + { + "type":"facet", + "sort":[], + "fields":["year"] + } + ]})", + default_namespace)); EXPECT_FALSE(status.ok()); EXPECT_TRUE(status.what() == std::string(reindexer::kAggregationWithSelectFieldsMsgError)); - EXPECT_THROW(Query().FromSQL("select max(id), * from test_namespace"), Error); - EXPECT_THROW(Query().FromSQL("select *, max(id) from test_namespace"), Error); - EXPECT_NO_THROW(Query().FromSQL("select *, count(*) from test_namespace")); - EXPECT_NO_THROW(Query().FromSQL("select count(*), * from test_namespace")); + EXPECT_THROW((void)Query::FromSQL("select max(id), * from test_namespace"), Error); + EXPECT_THROW((void)Query::FromSQL("select *, max(id) from test_namespace"), Error); + EXPECT_NO_THROW((void)Query::FromSQL("select *, count(*) from test_namespace")); + EXPECT_NO_THROW((void)Query::FromSQL("select count(*), * from test_namespace")); } TEST_F(ReindexerApi, InsertIncorrectItemWithJsonPathsDuplication) { @@ -1860,6 +1842,138 @@ TEST_F(ReindexerApi, InsertIncorrectItemWithJsonPathsDuplication) { } } +TEST_F(ReindexerApi, UpdateDoublesItemByPKIndex) { + rt.SetVerbose(true); + + Error err = rt.reindexer->OpenNamespace(default_namespace); + + err = rt.reindexer->AddIndex(default_namespace, {"id", "tree", "int", IndexOpts().PK()}); + ASSERT_TRUE(err.ok()) << err.what(); + err = rt.reindexer->AddIndex(default_namespace, {"v1", "tree", "int", IndexOpts().Sparse()}); + ASSERT_TRUE(err.ok()) << err.what(); + err = rt.reindexer->AddIndex(default_namespace, {"v2", "tree", "string", IndexOpts()}); + ASSERT_TRUE(err.ok()) << err.what(); + + struct ItemData { + ItemData() = default; + ItemData(unsigned int _id, unsigned int _v1, const std::string& _v2) : id(_id), v1(_v1), v2(_v2) {} + unsigned int id = 0; + unsigned int v1 = 0; + std::string v2; + }; + constexpr size_t kItemsCount = 4; + std::vector data; + std::string checkUuid; + for (unsigned i = 0; i < kItemsCount; i++) { + Item item(rt.reindexer->NewItem(default_namespace)); + ASSERT_TRUE(!!item); + ASSERT_TRUE(item.Status().ok()) << item.Status().what(); + data.emplace_back(ItemData{i, i + 100, RandString()}); + item["id"] = int(data.back().id); + item["v1"] = int(data.back().v1); + item["v2"] = data.back().v2; + err = rt.reindexer->Insert(default_namespace, item); + ASSERT_TRUE(err.ok()) << err.what(); + } + + { + reindexer::QueryResults qr; + const std::string sql = "UPDATE test_namespace SET v1=125, id = 3 WHERE id = 2"; + Query query = Query::FromSQL(sql); + err = rt.reindexer->Update(query, qr); + ASSERT_EQ(err.code(), errLogic); + EXPECT_EQ(err.what(), "Duplicate Primary Key {id: {3}} for rows [2, 3]!"); + } + + { + reindexer::QueryResults qr; + err = rt.reindexer->Select(Query(default_namespace).Sort("id", false), qr); + ASSERT_TRUE(err.ok()) << err.what(); + ASSERT_EQ(qr.Count(), kItemsCount); + + unsigned int i = 0; + for (auto it : qr) { + auto item = it.GetItem(); + ASSERT_EQ(item["id"].As(), data[i].id); + ASSERT_EQ(item["v1"].As(), data[i].v1); + ASSERT_EQ(item["v2"].As(), data[i].v2); + i++; + } + } +} +TEST_F(ReindexerApi, IntFieldConvertToStringIndexTest) { + Error err = rt.reindexer->OpenNamespace(default_namespace, StorageOpts().Enabled(false)); + ASSERT_TRUE(err.ok()) << err.what(); + + err = rt.reindexer->AddIndex(default_namespace, {"id", "hash", "int", IndexOpts().PK()}); + ASSERT_TRUE(err.ok()) << err.what(); + + static int id = 0; + enum class Order { InsertThenAddIndex, AddIndexThenUpdate }; + + auto testImpl = [this](Order order) { + std::srand(std::time(0)); + int value = std::rand(); + auto indexName = fmt::sprintf("data_%d", id); + auto indexPaths = order == Order::AddIndexThenUpdate ? reindexer::JsonPaths{"n." + indexName} : reindexer::JsonPaths{indexName}; + auto insert = [this](const char* tmplt, auto&&... args) { + Item item(rt.reindexer->NewItem(default_namespace)); + ASSERT_TRUE(item.Status().ok()) << item.Status().what(); + auto err = item.FromJSON(fmt::sprintf(tmplt, std::forward(args)...)); + ASSERT_TRUE(err.ok()) << err.what(); + err = rt.reindexer->Insert(default_namespace, item); + ASSERT_TRUE(item.Status().ok()) << item.Status().what(); + ASSERT_TRUE(err.ok()) << err.what(); + }; + + auto update = [&] { + QueryResults qr; + auto err = rt.reindexer->Select( + fmt::sprintf("UPDATE %s SET n = {\"%s\":%d} where id = %d", default_namespace, indexName, value, id), qr); + ASSERT_TRUE(err.ok()) << err.what(); + ASSERT_EQ(qr.Count(), 1); + }; + + auto addIndex = [&] { + auto err = rt.reindexer->AddIndex(default_namespace, {indexName, std::move(indexPaths), "hash", "string", IndexOpts()}); + ASSERT_TRUE(err.ok()) << err.what(); + }; + + auto checkResult = [&](const std::string& searchIndex, const std::string& searchValue) { + QueryResults qr; + auto err = rt.reindexer->Select(Query(default_namespace).Where(searchIndex, CondEq, searchValue), qr); + ASSERT_TRUE(err.ok()) << err.what(); + + ASSERT_EQ(qr.Count(), 1); + + auto item = qr.begin().GetItem(); + ASSERT_TRUE(item.Status().ok()) << item.Status().what(); + ASSERT_TRUE(Variant(item[indexName]).Type().Is()) << Variant(item[indexName]).Type().Name(); + ASSERT_EQ(item[indexName].As(), std::to_string(value)); + }; + + switch (order) { + case Order::InsertThenAddIndex: { + insert("{\"id\":%d,\"%s\":%d})", id, indexName, value); + addIndex(); + break; + } + case Order::AddIndexThenUpdate: { + addIndex(); + insert("{\"id\":%d}", id); + update(); + break; + } + } + checkResult("id", std::to_string(id)); + checkResult(indexName, std::to_string(value)); + id++; + }; + + testImpl(Order::InsertThenAddIndex); + testImpl(Order::AddIndexThenUpdate); +} + TEST_F(ReindexerApi, Meta) { const std::string kMetaKey = "key"; const std::string kMetaVal = "value"; @@ -1897,4 +2011,3 @@ TEST_F(ReindexerApi, Meta) { ASSERT_EQ(data[0].shardId, ShardingKeyType::NotSetShard); } } - diff --git a/cpp_src/gtests/tests/fixtures/clientsstats_api.h b/cpp_src/gtests/tests/fixtures/clientsstats_api.h index 7d9628132..497fd7e32 100644 --- a/cpp_src/gtests/tests/fixtures/clientsstats_api.h +++ b/cpp_src/gtests/tests/fixtures/clientsstats_api.h @@ -24,7 +24,7 @@ class ClientsStatsApi : public ::testing::Test { void SetProfilingFlag(bool val, const std::string& column, reindexer::client::CoroReindexer& c); - const std::string kdbPath = "/tmp/testdb/"; + const std::string kdbPath = reindexer::fs::JoinPath(reindexer::fs::GetTempDir(), "clientstats_test"); const std::string kdbName = "test"; const std::string kipaddress = "127.0.0.1"; const uint16_t kPortI = 7777; diff --git a/cpp_src/gtests/tests/fixtures/clusterization_api.cc b/cpp_src/gtests/tests/fixtures/clusterization_api.cc index 17c55d416..040656f6d 100644 --- a/cpp_src/gtests/tests/fixtures/clusterization_api.cc +++ b/cpp_src/gtests/tests/fixtures/clusterization_api.cc @@ -1,33 +1,6 @@ #include "clusterization_api.h" -#include -#include - -#include "core/cjson/jsonbuilder.h" -#include "core/dbconfig.h" -#include "tools/fsops.h" -#include "vendor/gason/gason.h" - -const std::string ClusterizationApi::kConfigNs = "#config"; -const std::chrono::seconds ClusterizationApi::kMaxServerStartTime = std::chrono::seconds(15); -const std::chrono::seconds ClusterizationApi::kMaxSyncTime = std::chrono::seconds(15); -const std::chrono::seconds ClusterizationApi::kMaxElectionsTime = std::chrono::seconds(12); - -const std::string ClusterizationApi::kIdField = "id"; -const std::string ClusterizationApi::kStringField = "string"; -const std::string ClusterizationApi::kIntField = "int"; -const std::string ClusterizationApi::kFTField = "ft_str"; - -void ClusterizationApi::SetUp() { - const auto def = GetDefaults(); - reindexer::fs::RmDirAll(def.baseTestsetDbPath); -} - -void ClusterizationApi::TearDown() // -V524 -{ - const auto def = GetDefaults(); - reindexer::fs::RmDirAll(def.baseTestsetDbPath); -} +#include "yaml-cpp/yaml.h" void ClusterizationApi::Cluster::initCluster(size_t count, size_t initialServerId, const YAML::Node& clusterConf) { for (size_t i = 0; i < count; ++i) { @@ -38,8 +11,7 @@ void ClusterizationApi::Cluster::initCluster(size_t count, size_t initialServerI svc_.emplace_back(); InitServer(i, clusterConf, replConf, initialServerId); - clients_.emplace_back(); - clients_.back().Connect(svc_.back().Get(false)->kRPCDsn, loop_); + clients_.emplace_back().Connect(svc_.back().Get(false)->kRPCDsn, loop_); } } @@ -65,38 +37,34 @@ ClusterizationApi::Cluster::Cluster(net::ev::dynamic_loop& loop, size_t initialS ClusterizationApi::Cluster::~Cluster() { StopClients(); - for (size_t i = 0; i < svc_.size(); ++i) { - if (svc_[i].IsRunning()) { - svc_[i].Get()->Stop(); + for (auto& sv : svc_) { + if (sv.IsRunning()) { + sv.Get()->Stop(); } } } void ClusterizationApi::Cluster::InitNs(size_t id, std::string_view nsName) { - auto opt = StorageOpts().Enabled(true); - // until we use shared ptr it will be not destroyed assert(id < svc_.size()); auto srv = svc_[id].Get(); auto& api = srv->api; - Error err = api.reindexer->OpenNamespace(nsName, opt); + Error err = api.reindexer->OpenNamespace(nsName, StorageOpts().Enabled(true)); ASSERT_TRUE(err.ok()) << err.what(); api.DefineNamespaceDataset(std::string(nsName), { - IndexDeclaration{kIdField.c_str(), "hash", "int", IndexOpts().PK(), 0}, - IndexDeclaration{kIntField.c_str(), "tree", "int", IndexOpts(), 0}, - IndexDeclaration{kStringField.c_str(), "hash", "string", IndexOpts(), 0}, - IndexDeclaration{kFTField.c_str(), "text", "string", IndexOpts(), 0}, + IndexDeclaration{kIdField, "hash", "int", IndexOpts().PK(), 0}, + IndexDeclaration{kIntField, "tree", "int", IndexOpts(), 0}, + IndexDeclaration{kStringField, "hash", "string", IndexOpts(), 0}, + IndexDeclaration{kFTField, "text", "string", IndexOpts(), 0}, }); } void ClusterizationApi::Cluster::DropNs(size_t id, std::string_view nsName) { // until we use shared ptr it will be not destroyed assert(id < svc_.size()); - auto srv = svc_[id].Get(); - auto& api = srv->api; - Error err = api.reindexer->DropNamespace(nsName); + Error err = svc_[id].Get()->api.reindexer->DropNamespace(nsName); ASSERT_TRUE(err.ok()) << err.what(); } @@ -163,8 +131,6 @@ size_t ClusterizationApi::Cluster::InitServer(size_t id, const YAML::Node& clust assert(id < svc_.size()); auto& server = svc_[id]; id += offset; - auto storagePath = fs::JoinPath(defaults_.baseTestsetDbPath, "node/" + std::to_string(id)); - ServerControlConfig scConfig(id, defaults_.defaultRpcPort + id, defaults_.defaultHttpPort + id, fs::JoinPath(defaults_.baseTestsetDbPath, "node/" + std::to_string(id)), "node" + std::to_string(id), true, maxUpdatesSize_); @@ -230,7 +196,7 @@ void ClusterizationApi::Cluster::StopServers(const std::vector& ids) { int ClusterizationApi::Cluster::AwaitLeader(std::chrono::seconds timeout, bool fulltime) { auto beg = std::chrono::high_resolution_clock::now(); auto end = beg; - int leaderId = -1; + int leaderId; do { leaderId = -1; for (size_t i = 0; i < clients_.size(); ++i) { @@ -340,8 +306,7 @@ void ClusterizationApi::Cluster::PrintClusterNsList(const std::vector nsDefs; while (syncedCnt != svc_.size()) { now += pause; if (now >= kMaxSyncTime) { @@ -380,7 +346,7 @@ void ClusterizationApi::Cluster::ValidateNamespaceList(const std::vector nsDefs; + nsDefs.clear(); node.Get(false)->api.reindexer->EnumNamespaces(nsDefs, EnumNamespacesOpts().HideSystem().WithClosed().OnlyNames()); if (namespaces.size() != nsDefs.size()) { continue; @@ -416,8 +382,7 @@ size_t ClusterizationApi::Cluster::GetSynchronizedNodesCount(size_t nodeId) { } void ClusterizationApi::Cluster::EnablePerfStats(size_t nodeId) { - auto node = GetNode(nodeId); - auto rx = node->api.reindexer; + auto rx = GetNode(nodeId)->api.reindexer; auto item = rx->NewItem(kConfigNs); ASSERT_TRUE(item.Status().ok()) << item.Status().what(); auto err = item.FromJSON( @@ -443,8 +408,8 @@ void ClusterizationApi::Cluster::AddAsyncNode(size_t nodeId, const std::string& void ClusterizationApi::Cluster::AwaitLeaderBecomeAvailable(size_t nodeId, std::chrono::milliseconds awaitTime) { auto now = std::chrono::milliseconds(0); const auto pause = std::chrono::milliseconds(100); + const Query q = Query("#replicationstats").Where("type", CondEq, "cluster"); while (now < awaitTime) { - Query q = Query("#replicationstats").Where("type", CondEq, Variant("cluster")); BaseApi::QueryResultsType qr; auto err = GetNode(nodeId)->api.reindexer->WithTimeout(pause).Select(q, qr); if (err.ok()) { @@ -461,6 +426,7 @@ YAML::Node ClusterizationApi::Cluster::CreateClusterConfigStatic(size_t initialS std::chrono::milliseconds resyncTimeout, int maxSyncCount, int syncThreadsCount) { std::vector nodeIds; + nodeIds.reserve(count); for (size_t id = initialServerId; id < initialServerId + count; ++id) { nodeIds.emplace_back(id); } diff --git a/cpp_src/gtests/tests/fixtures/clusterization_api.h b/cpp_src/gtests/tests/fixtures/clusterization_api.h index b9164a46d..4ce051609 100644 --- a/cpp_src/gtests/tests/fixtures/clusterization_api.h +++ b/cpp_src/gtests/tests/fixtures/clusterization_api.h @@ -1,37 +1,27 @@ #pragma once -#include -#include "client/cororeindexer.h" #include "client/raftclient.h" -#include "debug/backtrace.h" -#include "estl/fast_hash_set.h" +#include "cluster/config.h" +#include "net/ev/ev.h" #include "reindexer_api.h" -#include "reindexertestapi.h" -#include "server/dbmanager.h" -#include "server/server.h" -#include "thread" #include "tools/fsops.h" -#include "tools/logger.h" -#include "tools/serializer.h" -#include "yaml-cpp/yaml.h" -using namespace reindexer_server; -using std::shared_ptr; +using namespace reindexer; class ClusterizationApi : public ::testing::Test { public: - static const std::string kConfigNs; - static const std::chrono::seconds kMaxServerStartTime; - static const std::chrono::seconds kMaxSyncTime; - static const std::chrono::seconds kMaxElectionsTime; + static constexpr std::string_view kConfigNs = "#config"; + static constexpr auto kMaxServerStartTime = std::chrono::seconds(15); + static constexpr auto kMaxSyncTime = std::chrono::seconds(15); + static constexpr auto kMaxElectionsTime = std::chrono::seconds(12); - static const std::string kIdField; - static const std::string kStringField; - static const std::string kIntField; - static const std::string kFTField; + static constexpr std::string_view kIdField = "id"; + static constexpr std::string_view kStringField = "string"; + static constexpr std::string_view kIntField = "int"; + static constexpr std::string_view kFTField = "ft_str"; - void SetUp(); - void TearDown(); + void SetUp() { fs::RmDirAll(GetDefaults().baseTestsetDbPath); } + void TearDown() { fs::RmDirAll(GetDefaults().baseTestsetDbPath); } struct Defaults { size_t defaultRpcPort; diff --git a/cpp_src/gtests/tests/fixtures/clusterization_async_api.h b/cpp_src/gtests/tests/fixtures/clusterization_async_api.h index eee71cde5..c78a9e18a 100644 --- a/cpp_src/gtests/tests/fixtures/clusterization_async_api.h +++ b/cpp_src/gtests/tests/fixtures/clusterization_async_api.h @@ -5,12 +5,10 @@ class ClusterizationAsyncApi : public ClusterizationApi { public: void SetUp() override { // -V524 - const auto def = GetDefaults(); - reindexer::fs::RmDirAll(def.baseTestsetDbPath); + fs::RmDirAll(GetDefaults().baseTestsetDbPath); } void TearDown() override { // -V524 - const auto def = GetDefaults(); - reindexer::fs::RmDirAll(def.baseTestsetDbPath); + fs::RmDirAll(GetDefaults().baseTestsetDbPath); } const Defaults& GetDefaults() const override { static Defaults defs{14200, 16200, fs::JoinPath(fs::GetTempDir(), "rx_test/ClusterizationAsyncApi")}; diff --git a/cpp_src/gtests/tests/fixtures/clusterization_extras_api.h b/cpp_src/gtests/tests/fixtures/clusterization_extras_api.h index 0f112904b..deaad359b 100644 --- a/cpp_src/gtests/tests/fixtures/clusterization_extras_api.h +++ b/cpp_src/gtests/tests/fixtures/clusterization_extras_api.h @@ -5,12 +5,10 @@ class ClusterizationExtrasApi : public ClusterizationApi { public: void SetUp() override { // -V524 - const auto def = GetDefaults(); - reindexer::fs::RmDirAll(def.baseTestsetDbPath); + fs::RmDirAll(GetDefaults().baseTestsetDbPath); } void TearDown() override { // -V524 - const auto def = GetDefaults(); - reindexer::fs::RmDirAll(def.baseTestsetDbPath); + fs::RmDirAll(GetDefaults().baseTestsetDbPath); } const Defaults& GetDefaults() const override { static Defaults defs{14300, 16300, fs::JoinPath(fs::GetTempDir(), "rx_test/ClusterizationExtrasApi")}; @@ -28,8 +26,8 @@ class ClusterizationExtrasApi : public ClusterizationApi { awaitTime -= step; std::this_thread::sleep_for(step); } - auto stats = cluster.GetNode(leaderId)->GetReplicationStats(cluster::kClusterReplStatsType); - WrSerializer wser; + auto stats = cluster.GetNode(leaderId)->GetReplicationStats(reindexer::cluster::kClusterReplStatsType); + reindexer::WrSerializer wser; stats.GetJSON(wser); ASSERT_TRUE(false) << "Stats: " << wser.Slice(); } diff --git a/cpp_src/gtests/tests/fixtures/clusterization_proxy.h b/cpp_src/gtests/tests/fixtures/clusterization_proxy.h index 09557b385..df114697c 100644 --- a/cpp_src/gtests/tests/fixtures/clusterization_proxy.h +++ b/cpp_src/gtests/tests/fixtures/clusterization_proxy.h @@ -2,6 +2,7 @@ #include #include +#include #include "clusterization_api.h" #include "spdlog/fmt/fmt.h" #include "tools/timetools.h" @@ -110,7 +111,7 @@ class ClusterizationProxyApi : public ClusterizationApi { } void Validate(reindexer::client::QueryResults& qr) { bool validateOk = true; - WrSerializer ser; + reindexer::WrSerializer ser; for (auto& it : qr) { ser.Reset(); it.GetJSON(ser, false); diff --git a/cpp_src/gtests/tests/fixtures/ft_api.cc b/cpp_src/gtests/tests/fixtures/ft_api.cc index 18d9adb70..0725c254d 100644 --- a/cpp_src/gtests/tests/fixtures/ft_api.cc +++ b/cpp_src/gtests/tests/fixtures/ft_api.cc @@ -61,7 +61,10 @@ reindexer::Error FTApi::SetFTConfig(const reindexer::FtFastConfig& ftCfg, const fieldsMap.emplace(fields[i], i); } std::vector nses; - rt.reindexer->EnumNamespaces(nses, reindexer::EnumNamespacesOpts().WithFilter(ns)); + auto err = rt.reindexer->EnumNamespaces(nses, reindexer::EnumNamespacesOpts().WithFilter(ns)); + if (!err.ok()) { + return err; + } const auto it = std::find_if(nses[0].indexes.begin(), nses[0].indexes.end(), [&index](const reindexer::IndexDef& idef) { return idef.name_ == index; }); it->opts_.SetConfig(ftCfg.GetJSON(fieldsMap)); @@ -174,11 +177,11 @@ reindexer::QueryResults FTApi::SimpleSelect3(std::string word) { return res; } -void FTApi::Delete(int id) { +reindexer::Error FTApi::Delete(int id) { reindexer::Item item = rt.NewItem("nm1"); item["id"] = id; - this->rt.reindexer->Delete("nm1", item); + return this->rt.reindexer->Delete("nm1", item); } reindexer::QueryResults FTApi::SimpleCompositeSelect(std::string word) { @@ -187,7 +190,7 @@ reindexer::QueryResults FTApi::SimpleCompositeSelect(std::string word) { auto mqr{reindexer::Query("nm2").Where("ft3", CondEq, std::move(word))}; mqr.AddFunction("ft1 = snippet(,\"\",3,2,,d)"); - qr.mergeQueries_.emplace_back(Merge, std::move(mqr)); + qr.Merge(std::move(mqr)); qr.AddFunction("ft3 = highlight(,)"); auto err = rt.reindexer->Select(qr, res); EXPECT_TRUE(err.ok()) << err.what(); @@ -202,7 +205,7 @@ reindexer::QueryResults FTApi::CompositeSelectField(const std::string& field, st auto mqr{reindexer::Query("nm2").Where("ft3", CondEq, std::move(word))}; mqr.AddFunction(field + " = snippet(,\"\",3,2,,d)"); - qr.mergeQueries_.emplace_back(Merge, std::move(mqr)); + qr.Merge(std::move(mqr)); qr.AddFunction(field + " = highlight(,)"); auto err = rt.reindexer->Select(qr, res); EXPECT_TRUE(err.ok()) << err.what(); diff --git a/cpp_src/gtests/tests/fixtures/ft_api.h b/cpp_src/gtests/tests/fixtures/ft_api.h index 5e56bf8b3..542630dec 100644 --- a/cpp_src/gtests/tests/fixtures/ft_api.h +++ b/cpp_src/gtests/tests/fixtures/ft_api.h @@ -32,7 +32,7 @@ class FTApi : public ::testing::TestWithParam& indexes) const { + const FieldType fldType = std::visit( + reindexer::overloaded{[](const Child& c) noexcept { return c.type; }, [](const Children&) noexcept { return FieldType::Struct; }}, + content_); IndexOpts opts; - const bool pk = rnd.PkIndex(isPk); + const bool pk = rnd.PkIndex(isPk_); opts.PK(pk); - opts.Array(rnd.RndArrayField(isArray)); - opts.Sparse(rnd.SparseIndex(pk)); + opts.Array(rnd.RndArrayField(isArray_) == IsArrayT::Yes); + opts.Sparse(rnd.RndSparseIndex(isSparse_)); opts.Dense(rnd.DenseIndex()); opts.RTreeType(static_cast(rnd.RndInt(IndexOpts::Linear, IndexOpts::RStar))); - FieldType fldType = std::visit( - reindexer::overloaded{[](const Child& c) noexcept { return c.type; }, [](const Children&) noexcept { return FieldType::Struct; }}, - content); std::string fieldType = rnd.IndexFieldType(fldType); - std::string indexType = rnd.RndIndexType(fldType, isPk); + std::string indexType{ToText(rnd.RndIndexType(type_))}; reindexer::JsonPaths jsonPaths; std::visit(reindexer::overloaded{[&](const Child& c) { jsonPaths.push_back(scheme.GetJsonPath(c.fieldPath)); }, [&](const Children& c) { + jsonPaths.reserve(c.size()); for (const auto& child : c) { + if (rnd.RndBool(0.5)) { + std::vector scalarIndexes; + scalarIndexes.reserve(indexes.size()); + for (size_t i = 0, s = indexes.size(); i < s; ++i) { + if (const auto* c = std::get_if(&indexes[i].content_); + c && c->fieldPath == child.fieldPath) { + scalarIndexes.push_back(i); + } + } + if (!scalarIndexes.empty()) { + jsonPaths.push_back(indexes[rnd.RndWhich(scalarIndexes)].name_); + continue; + } + } jsonPaths.push_back(scheme.GetJsonPath(child.fieldPath)); } }}, - content); - return {name, std::move(jsonPaths), std::move(indexType), std::move(fieldType), std::move(opts), rnd.ExpiredIndex()}; + content_); + return {name_, std::move(jsonPaths), std::move(indexType), std::move(fieldType), std::move(opts), rnd.ExpiredIndex()}; } void Index::Dump(std::ostream& os, const NsScheme& scheme, size_t offset) const { for (size_t i = 0; i < offset; ++i) os << " "; os << "{\n"; for (size_t i = 0; i <= offset; ++i) os << " "; - os << "name: " << name << '\n'; + os << "name: " << name_ << '\n'; + for (size_t i = 0; i <= offset; ++i) os << " "; + os << "type: " << type_ << '\n'; for (size_t i = 0; i <= offset; ++i) os << " "; - os << "pk: " << (isPk ? "true" : "false") << '\n'; + os << "pk: " << std::boolalpha << isPk_ << '\n'; for (size_t i = 0; i <= offset; ++i) os << " "; - os << "array: " << (isArray ? "true" : "false") << '\n'; + os << "array: " << std::boolalpha << IsArray() << '\n'; + for (size_t i = 0; i <= offset; ++i) os << " "; + os << "sparse: " << std::boolalpha << (IsSparse() == IsSparseT::Yes) << '\n'; for (size_t i = 0; i <= offset; ++i) os << " "; std::visit(reindexer::overloaded{[&](const Child& child) { + os << "composite: false\n"; + for (size_t i = 0; i <= offset; ++i) os << " "; os << "field: {\n"; for (size_t i = 0; i < offset + 2; ++i) os << " "; os << "type: " << child.type << '\n'; @@ -48,6 +72,8 @@ void Index::Dump(std::ostream& os, const NsScheme& scheme, size_t offset) const os << "}\n"; }, [&](const Children& children) { + os << "composite: true\n"; + for (size_t i = 0; i <= offset; ++i) os << " "; os << "fields: [\n"; for (const auto& c : children) { for (size_t i = 0; i < offset + 2; ++i) os << " "; @@ -62,7 +88,7 @@ void Index::Dump(std::ostream& os, const NsScheme& scheme, size_t offset) const for (size_t i = 0; i <= offset; ++i) os << " "; os << "]\n"; }}, - content); + content_); } } // namespace fuzzing diff --git a/cpp_src/gtests/tests/fixtures/fuzzing/index.h b/cpp_src/gtests/tests/fixtures/fuzzing/index.h index 1daeb9266..4fe90a47b 100644 --- a/cpp_src/gtests/tests/fixtures/fuzzing/index.h +++ b/cpp_src/gtests/tests/fixtures/fuzzing/index.h @@ -1,7 +1,9 @@ #pragma once -#include "ns_scheme.h" -#include "random_generator.h" +#include +#include +#include +#include "types.h" namespace reindexer { struct IndexDef; @@ -9,20 +11,43 @@ struct IndexDef; namespace fuzzing { -struct Index { - reindexer::IndexDef IndexDef(RandomGenerator&, const NsScheme&) const; +class RandomGenerator; +class NsScheme; +class Index { +public: struct Child { FieldType type; FieldPath fieldPath; }; using Children = std::vector; - std::string name; - std::variant content; - bool isPk{false}; - bool isArray{false}; + Index(std::string name, IndexType type, IsArrayT isArray, IsSparseT isSparse, Children content) noexcept + : name_{std::move(name)}, type_{type}, content_{std::move(content)}, isArray_{isArray}, isSparse_{isSparse} {} + Index(std::string name, IndexType type, IsArrayT isArray, IsSparseT isSparse, Child content) noexcept + : name_{std::move(name)}, type_{type}, content_{std::move(content)}, isArray_{isArray}, isSparse_{isSparse} {} + + const std::string& Name() const& noexcept { return name_; } + const std::string& Name() const&& = delete; + IndexType Type() const noexcept { return type_; } + const auto& Content() const& noexcept { return content_; } + const auto& Content() const&& = delete; + bool IsPk() const noexcept { return isPk_; } + void SetPk() noexcept { isPk_ = true; } + bool IsArray() const noexcept { return isArray_ == IsArrayT::Yes; } + auto IsSparse() const noexcept { return isSparse_; } + + reindexer::IndexDef IndexDef(RandomGenerator&, const NsScheme&, const std::vector&) const; + void Dump(std::ostream&, const NsScheme&, size_t offset) const; + +private: + std::string name_; + IndexType type_; + std::variant content_; + bool isPk_{false}; + IsArrayT isArray_{IsArrayT::No}; + IsSparseT isSparse_{IsSparseT::No}; }; } // namespace fuzzing diff --git a/cpp_src/gtests/tests/fixtures/fuzzing/ns.cc b/cpp_src/gtests/tests/fixtures/fuzzing/ns.cc index 93d5f16fc..831653a84 100644 --- a/cpp_src/gtests/tests/fixtures/fuzzing/ns.cc +++ b/cpp_src/gtests/tests/fixtures/fuzzing/ns.cc @@ -1,5 +1,8 @@ #include "ns.h" #include +#include "estl/overloaded.h" +#include "index.h" +#include "tools/assertrx.h" struct FieldPathHash { size_t operator()(const fuzzing::FieldPath& fp) const noexcept { @@ -30,47 +33,74 @@ static bool availablePkFieldType(FieldType ft) { } } -Ns::Ns(std::string name, std::ostream& os, RandomGenerator::ErrFactorType errorFactor) - : name_{std::move(name)}, rndGen_{os, errorFactor}, scheme_{name_, rndGen_} { - std::unordered_set generatedNames; +static bool availablePkIndexType(IndexType it) { + switch (it) { + case IndexType::Store: + case IndexType::FastFT: + case IndexType::FuzzyFT: + case IndexType::RTree: + return false; + case IndexType::Hash: + case IndexType::Tree: + case IndexType::Ttl: + return true; + default: + assertrx(false); + std::abort(); + } +} + +Ns::Ns(std::string name, RandomGenerator::ErrFactorType errorFactor) + : name_{std::move(name)}, rndGen_{errorFactor}, scheme_{name_, rndGen_} { + std::unordered_set usedIndexNames; std::unordered_set usedPaths; constexpr static size_t kMaxTries = 10; const size_t idxCount = rndGen_.IndexesCount(); const bool withErr = rndGen_.RndErr(); indexes_.reserve(idxCount); + std::vector scalarIndexes; + scalarIndexes.reserve(idxCount); for (size_t i = 0; i < idxCount; ++i) { const bool uniqueName = rndGen_.UniqueName(); - if (rndGen_.CompositeIndex()) { - bool fail = false; - Index index{uniqueName ? rndGen_.IndexName(generatedNames) : std::string{}, Index::Children{}}; - auto& children = std::get(index.content); - const size_t size = rndGen_.CompositeIndexSize(); - children.reserve(size); - for (size_t i = 0; i < size; ++i) { - auto fldPath = rndGen_.RndScalarField(scheme_); - FieldType fldType; - if (scheme_.IsStruct(fldPath)) { - if (!rndGen_.RndErr()) { - fail = true; - break; - } - fldType = rndGen_.RndFieldType(); + if (rndGen_.CompositeIndex(scalarIndexes.size())) { + bool array = false; + bool containsUuid = false; + std::string name; + Index::Children children; + const auto fields = rndGen_.RndFieldsForCompositeIndex(scalarIndexes); + children.reserve(fields.size()); + for (size_t f : fields) { + Index::Child fieldData; + if (f < indexes_.size()) { + const auto& idx = indexes_[f]; + fieldData = std::get(idx.Content()); + array |= idx.IsArray(); } else { - fldType = scheme_.GetFieldType(fldPath); - } - if (!uniqueName) { - if (!index.name.empty()) index.name += '+'; - if (fldPath.empty()) { - index.name += rndGen_.FieldName(generatedNames); + fieldData.fieldPath = rndGen_.RndScalarField(scheme_); + if (scheme_.IsStruct(fieldData.fieldPath)) { + fieldData.type = rndGen_.RndFieldType(); } else { - index.name += scheme_.GetJsonPath(fldPath); + fieldData.type = scheme_.GetFieldType(fieldData.fieldPath); } } - children.emplace_back(Index::Child{fldType, std::move(fldPath)}); + if (!uniqueName) { + if (!name.empty()) name += '+'; + name += scheme_.GetJsonPath(fieldData.fieldPath); + } + containsUuid |= fieldData.type == FieldType::Uuid; + children.emplace_back(std::move(fieldData)); + } + const auto indexType = + containsUuid ? rndGen_.RndIndexType({FieldType::Struct, FieldType::Uuid}) : rndGen_.RndIndexType({FieldType::Struct}); + if (uniqueName) { + name = rndGen_.IndexName(usedIndexNames); + } else if (!usedIndexNames.insert(name).second) { + name = rndGen_.IndexName(usedIndexNames); + usedIndexNames.insert(name); } - if (fail) continue; - index.isArray = rndGen_.RndArrayField(false); - indexes_.emplace_back(std::move(index)); + + indexes_.emplace_back(std::move(name), indexType, rndGen_.RndArrayField(array ? IsArrayT::Yes : IsArrayT::No), IsSparseT::No, + std::move(children)); } else { FieldPath fldPath; size_t tryCounts = 0; @@ -82,47 +112,73 @@ Ns::Ns(std::string name, std::ostream& os, RandomGenerator::ErrFactorType errorF if (scheme_.IsStruct(fldPath)) { if (!rndGen_.RndErr()) continue; const auto fldType = rndGen_.RndFieldType(); - indexes_.emplace_back(Index{rndGen_.IndexName(generatedNames), Index::Child{fldType, std::move(fldPath)}}); + indexes_.emplace_back(rndGen_.IndexName(usedIndexNames), rndGen_.RndIndexType({fldType}), + rndGen_.RndBool(0.5) ? IsArrayT::Yes : IsArrayT::No, + rndGen_.RndBool(0.5) ? IsSparseT::Yes : IsSparseT::No, Index::Child{fldType, std::move(fldPath)}); } else { const auto fldType = scheme_.GetFieldType(fldPath); - const bool isArray = scheme_.IsArray(fldPath); - std::string idxName = uniqueName ? rndGen_.IndexName(generatedNames) : scheme_.GetJsonPath(fldPath); - indexes_.emplace_back(Index{std::move(idxName), Index::Child{fldType, std::move(fldPath)}}); - indexes_.back().isArray = rndGen_.RndArrayField(isArray); + const auto isArray = scheme_.IsArray(fldPath); + std::string idxName; + if (uniqueName) { + idxName = rndGen_.IndexName(usedIndexNames); + } else { + idxName = scheme_.GetJsonPath(fldPath); + if (!usedIndexNames.insert(idxName).second) { + idxName = rndGen_.IndexName(usedIndexNames); + usedIndexNames.insert(idxName); + } + } + indexes_.emplace_back(std::move(idxName), rndGen_.RndIndexType({fldType}), rndGen_.RndArrayField(isArray), + rndGen_.RndSparseIndex(fldType), Index::Child{fldType, std::move(fldPath)}); + } + if (const auto& idx = indexes_.back(); + !idx.IsArray() && idx.IsSparse() == IsSparseT::No && + std::get(idx.Content()).type != FieldType::Point) { // TODO remove point check after #1352 + scalarIndexes.push_back(indexes_.size() - 1); } } } + if (rndGen_.RndErr()) { + // Do not set PK index + return; + } std::vector ii; for (size_t i = 0, s = indexes_.size(); i < s; ++i) { const auto& idx = indexes_[i]; - if (!idx.isArray && - (std::holds_alternative(idx.content) || availablePkFieldType(std::get(idx.content).type))) { + if (!idx.IsArray() && idx.IsSparse() == IsSparseT::No && availablePkIndexType(idx.Type()) && + (std::holds_alternative(idx.Content()) || availablePkFieldType(std::get(idx.Content()).type))) { ii.push_back(i); } } if (ii.empty()) { - if (!rndGen_.RndErr()) { - auto path = scheme_.AddRndPkField(rndGen_); - const auto fldType = scheme_.GetFieldType(path); - std::string name = rndGen_.UniqueName() ? rndGen_.IndexName(generatedNames) : scheme_.GetJsonPath(path); - indexes_.emplace_back(Index{std::move(name), Index::Child{fldType, std::move(path)}}); - indexes_.back().isArray = false; - indexes_.back().isPk = true; + auto path = scheme_.AddRndPkField(rndGen_); + const auto fldType = scheme_.GetFieldType(path); + std::string name; + if (rndGen_.UniqueName()) { + name = rndGen_.IndexName(usedIndexNames); + } else { + name = scheme_.GetJsonPath(path); + if (!usedIndexNames.insert(name).second) { + name = rndGen_.IndexName(usedIndexNames); + usedIndexNames.insert(name); + } } + indexes_.emplace_back(std::move(name), rndGen_.RndPkIndexType({fldType}), IsArrayT::No, IsSparseT::No, + Index::Child{fldType, std::move(path)}); + indexes_.back().SetPk(); } else { - indexes_[rndGen_.RndWhich(ii)].isPk = true; + indexes_[rndGen_.RndWhich(ii)].SetPk(); } } -void Ns::AddIndex(Index& index, bool isSparse) { - if (isSparse) return; - std::visit(reindexer::overloaded{[&](const Index::Child& c) { scheme_.AddIndex(c.fieldPath, isSparse); }, +void Ns::AddIndexToScheme(const Index& index, size_t indexNumber) { + std::visit(reindexer::overloaded{[&](const Index::Child& c) { scheme_.AddIndex(c.fieldPath, indexNumber, index.IsSparse()); }, [&](const Index::Children& c) { for (const auto& child : c) { - scheme_.AddIndex(child.fieldPath, isSparse); + scheme_.AddIndex(child.fieldPath, indexNumber, index.IsSparse()); } }}, - index.content); + index.Content()); } void Ns::Dump(std::ostream& os) const { diff --git a/cpp_src/gtests/tests/fixtures/fuzzing/ns.h b/cpp_src/gtests/tests/fixtures/fuzzing/ns.h index 53baf5a3e..6c93abb46 100644 --- a/cpp_src/gtests/tests/fixtures/fuzzing/ns.h +++ b/cpp_src/gtests/tests/fixtures/fuzzing/ns.h @@ -1,21 +1,23 @@ #pragma once -#include "index.h" #include "ns_scheme.h" #include "random_generator.h" namespace fuzzing { +class Index; + class Ns { public: - Ns(std::string name, std::ostream&, RandomGenerator::ErrFactorType errorFactor); - std::vector& GetIndexes() noexcept { return indexes_; } + Ns(std::string name, RandomGenerator::ErrFactorType errorFactor); + const std::vector& GetIndexes() const& noexcept { return indexes_; } + std::vector& GetIndexes() & noexcept { return indexes_; } + const std::vector& GetIndexes() const&& = delete; const std::string& GetName() const noexcept { return name_; } const NsScheme& GetScheme() const noexcept { return scheme_; } RandomGenerator& GetRandomGenerator() noexcept { return rndGen_; } - void AddIndex(Index&, bool isSparse); - void NewItem(reindexer::WrSerializer& ser) { scheme_.NewItem(ser, rndGen_); } - const std::vector& GetIndexes() const noexcept { return indexes_; } + void AddIndexToScheme(const Index&, size_t indexNumber); + void NewItem(reindexer::WrSerializer& ser) { scheme_.NewItem(ser, rndGen_, indexes_); } void Dump(std::ostream&) const; private: diff --git a/cpp_src/gtests/tests/fixtures/fuzzing/ns_scheme.cc b/cpp_src/gtests/tests/fixtures/fuzzing/ns_scheme.cc index 8fb4e9e52..0a1c92829 100644 --- a/cpp_src/gtests/tests/fixtures/fuzzing/ns_scheme.cc +++ b/cpp_src/gtests/tests/fixtures/fuzzing/ns_scheme.cc @@ -1,10 +1,12 @@ #include "ns_scheme.h" #include "core/cjson/jsonbuilder.h" +#include "index.h" +#include "random_generator.h" #include "tools/serializer.h" namespace fuzzing { -void NsScheme::NewItem(reindexer::WrSerializer& ser, RandomGenerator& rnd) { +void NsScheme::NewItem(reindexer::WrSerializer& ser, RandomGenerator& rnd, const std::vector& indexes) { ser.Reset(); if (rnd.RndErr()) { enum Err : uint8_t { Zero, Random, END = Random }; @@ -23,10 +25,195 @@ void NsScheme::NewItem(reindexer::WrSerializer& ser, RandomGenerator& rnd) { } } reindexer::JsonBuilder builder{ser}; - toJson(builder, std::get(ns_.content), rnd); + toJson(builder, std::get(ns_.content), rnd, indexes); } -void NsScheme::rndValueToJson(reindexer::JsonBuilder& builder, FieldType ft, std::string_view name, RandomGenerator& rnd) { +bool NsScheme::IsStruct(const FieldPath& path) const noexcept { + if (path.empty()) return true; + const Node::Children& ref = findLastContainer(path); + assertrx(ref.size() > path.back()); + return std::holds_alternative(ref[path.back()].content); +} + +bool NsScheme::IsPoint(const FieldPath& path) const noexcept { + if (path.empty()) return false; + const Node::Children& ref = findLastContainer(path); + assertrx(ref.size() > path.back()); + return !std::holds_alternative(ref[path.back()].content) && + std::get(ref[path.back()].content).type == FieldType::Point; +} + +bool NsScheme::isTtl(const std::vector& idxNumbers, const std::vector& indexes) noexcept { + for (size_t idx : idxNumbers) { + assertrx(idx < indexes.size()); + if (indexes[idx].Type() == IndexType::Ttl) { + return true; + } + } + return false; +} + +bool NsScheme::IsTtl(const FieldPath& path, const std::vector& indexes) const noexcept { + if (path.empty()) return false; + const Node::Children& ref = findLastContainer(path); + assertrx(ref.size() > path.back()); + if (std::holds_alternative(ref[path.back()].content)) { + return false; + } + return isTtl(std::get(ref[path.back()].content).indexes, indexes); +} + +size_t NsScheme::FieldsCount(const FieldPath& path) const noexcept { + if (path.empty()) { + return std::get(ns_.content).size(); + } + const Node::Children& ref = findLastContainer(path); + assertrx(ref.size() > path.back()); + return std::visit(reindexer::overloaded{[](const Node::Child&) noexcept -> size_t { + assertrx(false); + return 0; + }, + [](const Node::Children& c) noexcept { return c.size(); }}, + ref[path.back()].content); +} + +IsArrayT NsScheme::IsArray(const FieldPath& path) const noexcept { + if (path.empty()) return ns_.array; + const Node::Children* ptr = &std::get(ns_.content); + for (size_t i = 0, s = path.size() - 1; i < s; ++i) { + assertrx(ptr->size() > path[i]); + const auto& idx = (*ptr)[path[i]]; + if (idx.array == IsArrayT::Yes) return IsArrayT::Yes; + std::visit( + reindexer::overloaded{[&ptr](const Node::Children& c) noexcept { ptr = &c; }, [](const Node::Child&) noexcept { assertrx(0); }}, + idx.content); + } + assertrx(ptr->size() > path.back()); + return (*ptr)[path.back()].array; +} + +FieldType NsScheme::GetFieldType(const FieldPath& path) const noexcept { + assertrx(!path.empty()); + const Node::Children& ref = findLastContainer(path); + assertrx(ref.size() > path.back()); + return std::visit(reindexer::overloaded{[](const Node::Child& c) noexcept { return c.type; }, + [](const Node::Children&) noexcept { return FieldType::Struct; }}, + ref[path.back()].content); +} + +void NsScheme::SetFieldType(const FieldPath& path, FieldType ft) noexcept { + assertrx(!path.empty()); + Node::Children& ref = findLastContainer(path); + assertrx(ref.size() > path.back()); + return std::visit(reindexer::overloaded{[ft](Node::Child& c) noexcept { c.type = ft; }, [](Node::Children&) noexcept { assertrx(0); }}, + ref[path.back()].content); +} + +std::string NsScheme::GetJsonPath(const FieldPath& path) const noexcept { + if (path.empty()) return {}; + std::string res; + const Node::Children* ptr = &std::get(ns_.content); + for (size_t i = 0, s = path.size() - 1; i < s; ++i) { + assertrx(ptr->size() > path[i]); + const auto& idx = (*ptr)[path[i]]; + res += idx.name; + std::visit( + reindexer::overloaded{[&ptr](const Node::Children& c) noexcept { ptr = &c; }, [](const Node::Child&) noexcept { assertrx(0); }}, + idx.content); + res += '.'; + } + assertrx(ptr->size() > path.back()); + res += (*ptr)[path.back()].name; + return res; +} + +void NsScheme::AddIndex(const FieldPath& path, size_t index, IsSparseT isSparse) { + assertrx(!path.empty()); + if (isSparse == IsSparseT::No) { + ns_.sparse = IsSparseT::No; + } + Node::Children* ptr = &std::get(ns_.content); + for (size_t i = 0, s = path.size() - 1; i < s; ++i) { + assertrx(ptr->size() > path[i]); + if (isSparse == IsSparseT::No) { + (*ptr)[path[i]].sparse = IsSparseT::No; + } + std::visit(reindexer::overloaded{[&ptr](Node::Children& c) noexcept { ptr = &c; }, [](Node::Child&) noexcept { assertrx(0); }}, + (*ptr)[path[i]].content); + } + assertrx(ptr->size() > path.back()); + addIndex((*ptr)[path.back()], index, isSparse); +} + +FieldPath NsScheme::AddRndPkField(RandomGenerator& rnd) { + auto& children = std::get(ns_.content); + children.emplace_back(Node{rnd.FieldName(generatedNames_), Node::Child{rnd.RndPkIndexFieldType()}}); + children.back().array = IsArrayT::No; + children.back().sparse = IsSparseT::No; + return {children.size() - 1}; +} + +void NsScheme::addIndex(Node& node, size_t index, IsSparseT isSparse) { + if (isSparse == IsSparseT::No) { + node.sparse = IsSparseT::No; + } + std::visit(reindexer::overloaded{[index](Node::Child& c) noexcept { c.indexes.push_back(index); }, + [](Node::Children&) noexcept { assertrx(0); }}, + node.content); +} + +void NsScheme::fillChildren(Node::Children& children, RandomGenerator& rnd, unsigned level, bool& canBeArray, bool& canBeSparse) { + const size_t fieldsCount = rnd.FieldsCount(level == 0); + children.reserve(fieldsCount); + for (size_t i = 0; i < fieldsCount; ++i) { + auto fName = rnd.FieldName(generatedNames_); + const auto type = rnd.RndFieldType(level); + if (type == FieldType::Struct) { + children.emplace_back(Node{std::move(fName), Node::Children{}}); + fillChildren(std::get(children.back().content), rnd, level + 1, canBeArray, canBeSparse); + if (canBeArray || rnd.RndErr()) { + children.back().array = rnd.RndArrayField(); + } + if (!canBeSparse && !rnd.RndErr()) { + children.back().sparse = IsSparseT::No; + } + } else { + children.emplace_back(Node{std::move(fName), Node::Child{type}}); + if (type == FieldType::Point) { + canBeSparse = false; + canBeArray = false; + children.back().sparse = IsSparseT::No; + } + if (canBeArray || rnd.RndErr()) { + children.back().array = rnd.RndArrayField(); + } + } + } +} + +const NsScheme::Node::Children& NsScheme::findLastContainer(const FieldPath& path) const noexcept { + const Node::Children* ptr = &std::get(ns_.content); + for (size_t i = 0, s = path.size() - 1; i < s; ++i) { + assertrx(ptr->size() > path[i]); + std::visit( + reindexer::overloaded{[&ptr](const Node::Children& c) noexcept { ptr = &c; }, [](const Node::Child&) noexcept { assertrx(0); }}, + (*ptr)[path[i]].content); + } + return *ptr; +} + +NsScheme::Node::Children& NsScheme::findLastContainer(const FieldPath& path) noexcept { + Node::Children* ptr = &std::get(ns_.content); + for (size_t i = 0, s = path.size() - 1; i < s; ++i) { + assertrx(ptr->size() > path[i]); + std::visit(reindexer::overloaded{[&ptr](Node::Children& c) noexcept { ptr = &c; }, [](Node::Child&) noexcept { assertrx(0); }}, + (*ptr)[path[i]].content); + } + return *ptr; +} + +void NsScheme::rndValueToJson(reindexer::JsonBuilder& builder, FieldType ft, std::string_view name, const std::vector& idxNumbers, + const std::vector& indexes, RandomGenerator& rnd) { switch (ft) { case FieldType::Bool: builder.Put(name, rnd.RndBool(0.5)); @@ -35,7 +222,11 @@ void NsScheme::rndValueToJson(reindexer::JsonBuilder& builder, FieldType ft, std builder.Put(name, rnd.RndIntValue()); break; case FieldType::Int64: - builder.Put(name, rnd.RndInt64Value()); + if (isTtl(idxNumbers, indexes)) { + builder.Put(name, rnd.RndTtlValue()); + } else { + builder.Put(name, rnd.RndInt64Value()); + } break; case FieldType::Double: builder.Put(name, rnd.RndDoubleValue()); @@ -54,41 +245,44 @@ void NsScheme::rndValueToJson(reindexer::JsonBuilder& builder, FieldType ft, std Node::Children children; fillChildren(children, rnd, 2, canBeArray, canBeSparse); auto obj = builder.Object(name); - toJson(obj, children, rnd); + toJson(obj, children, rnd, indexes); } break; default: assertrx(0); } } -void NsScheme::toJson(reindexer::JsonBuilder& builder, const Node::Children& children, RandomGenerator& rnd) { +void NsScheme::toJson(reindexer::JsonBuilder& builder, const Node::Children& children, RandomGenerator& rnd, + const std::vector& indexes) { for (const Node& n : children) { if (!rnd.NeedThisNode(n.sparse)) continue; - if (rnd.RndArrayField(n.array)) { + if (rnd.RndArrayField(n.array) == IsArrayT::Yes) { auto arr = builder.Array(n.name); const size_t arrSize = rnd.ArraySize(); for (size_t i = 0; i < arrSize; ++i) { if (rnd.RndErr()) { - rndValueToJson(arr, rnd.RndFieldType(), {}, rnd); + rndValueToJson(arr, rnd.RndFieldType(), {}, {}, indexes, rnd); } else { - std::visit(reindexer::overloaded{[&](const Node::Child& c) { rndValueToJson(arr, c.type, {}, rnd); }, - [&](const Node::Children& c) { - auto obj = arr.Object(); - toJson(obj, c, rnd); - }}, - n.content); + std::visit( + reindexer::overloaded{[&](const Node::Child& c) { rndValueToJson(arr, c.type, {}, c.indexes, indexes, rnd); }, + [&](const Node::Children& c) { + auto obj = arr.Object(); + toJson(obj, c, rnd, indexes); + }}, + n.content); } } } else { if (rnd.RndErr()) { - rndValueToJson(builder, rnd.RndFieldType(), n.name, rnd); + rndValueToJson(builder, rnd.RndFieldType(), n.name, {}, indexes, rnd); } else { - std::visit(reindexer::overloaded{[&](const Node::Child& c) { rndValueToJson(builder, c.type, n.name, rnd); }, - [&](const Node::Children& c) { - auto obj = builder.Object(n.name); - toJson(obj, c, rnd); - }}, - n.content); + std::visit( + reindexer::overloaded{[&](const Node::Child& c) { rndValueToJson(builder, c.type, n.name, c.indexes, indexes, rnd); }, + [&](const Node::Children& c) { + auto obj = builder.Object(n.name); + toJson(obj, c, rnd, indexes); + }}, + n.content); } } } @@ -100,9 +294,9 @@ void NsScheme::Node::Dump(std::ostream& os, size_t offset) const { for (size_t i = 0; i <= offset; ++i) os << " "; os << "name: " << name << '\n'; for (size_t i = 0; i <= offset; ++i) os << " "; - os << "sparse: " << (sparse ? "true" : "false") << '\n'; + os << "sparse: " << std::boolalpha << (sparse == IsSparseT::Yes) << '\n'; for (size_t i = 0; i <= offset; ++i) os << " "; - os << "array: " << (array ? "true" : "false") << '\n'; + os << "array: " << std::boolalpha << (array == IsArrayT::Yes) << '\n'; std::visit(reindexer::overloaded{[&](const Child& child) { for (size_t i = 0; i <= offset; ++i) os << " "; os << "type: " << child.type << '\n'; diff --git a/cpp_src/gtests/tests/fixtures/fuzzing/ns_scheme.h b/cpp_src/gtests/tests/fixtures/fuzzing/ns_scheme.h index 675173c36..8123d726f 100644 --- a/cpp_src/gtests/tests/fixtures/fuzzing/ns_scheme.h +++ b/cpp_src/gtests/tests/fixtures/fuzzing/ns_scheme.h @@ -1,10 +1,10 @@ #pragma once #include +#include #include #include -#include "estl/overloaded.h" -#include "random_generator.h" +#include "types.h" namespace reindexer { @@ -15,17 +15,22 @@ class JsonBuilder; namespace fuzzing { +class RandomGenerator; +class Index; + class NsScheme { struct Node { using Children = std::vector; struct Child { + Child(FieldType t) noexcept : type{t} {} FieldType type; + std::vector indexes; }; std::string name; std::variant content; - bool sparse{true}; - bool array{false}; + IsSparseT sparse{IsSparseT::Yes}; + IsArrayT array{IsArrayT::No}; void Dump(std::ostream&, size_t offset) const; }; @@ -34,164 +39,29 @@ class NsScheme { bool canBeArray = true, canBeSparse = true; fillChildren(std::get(ns_.content), rnd, 0, canBeArray, canBeSparse); } - size_t FieldsCount(const FieldPath& path) const noexcept { - if (path.empty()) { - return std::get(ns_.content).size(); - } - const Node::Children& ref = findLastContainer(path); - assertrx(ref.size() > path.back()); - return std::visit(reindexer::overloaded{[](const Node::Child&) noexcept -> size_t { - assertrx(false); - return 0; - }, - [](const Node::Children& c) noexcept { return c.size(); }}, - ref[path.back()].content); - } - bool IsStruct(const FieldPath& path) const noexcept { - if (path.empty()) return true; - const Node::Children& ref = findLastContainer(path); - assertrx(ref.size() > path.back()); - return std::holds_alternative(ref[path.back()].content); - } - bool IsPoint(const FieldPath& path) const noexcept { - if (path.empty()) return false; - const Node::Children& ref = findLastContainer(path); - assertrx(ref.size() > path.back()); - return !std::holds_alternative(ref[path.back()].content) && - std::get(ref[path.back()].content).type == FieldType::Point; - } - bool IsArray(const FieldPath& path) const noexcept { - if (path.empty()) return ns_.array; - const Node::Children* ptr = &std::get(ns_.content); - for (size_t i = 0, s = path.size() - 1; i < s; ++i) { - assertrx(ptr->size() > path[i]); - const auto& idx = (*ptr)[path[i]]; - if (idx.array) return true; - std::visit(reindexer::overloaded{[&ptr](const Node::Children& c) noexcept { ptr = &c; }, - [](const Node::Child&) noexcept { assert(0); }}, - idx.content); - } - assertrx(ptr->size() > path.back()); - return (*ptr)[path.back()].array; - } - FieldType GetFieldType(const FieldPath& path) const noexcept { - assertrx(!path.empty()); - const Node::Children& ref = findLastContainer(path); - assertrx(ref.size() > path.back()); - return std::visit(reindexer::overloaded{[](const Node::Child& c) noexcept { return c.type; }, - [](const Node::Children&) noexcept { return FieldType::Struct; }}, - ref[path.back()].content); - } - void SetFieldType(const FieldPath& path, FieldType ft) noexcept { - assertrx(!path.empty()); - Node::Children& ref = findLastContainer(path); - assertrx(ref.size() > path.back()); - return std::visit( - reindexer::overloaded{[ft](Node::Child& c) noexcept { c.type = ft; }, [](Node::Children&) noexcept { assert(0); }}, - ref[path.back()].content); - } - std::string GetJsonPath(const FieldPath& path) const noexcept { - if (path.empty()) return {}; - std::string res; - const Node::Children* ptr = &std::get(ns_.content); - for (size_t i = 0, s = path.size() - 1; i < s; ++i) { - assertrx(ptr->size() > path[i]); - const auto& idx = (*ptr)[path[i]]; - res += idx.name; - std::visit(reindexer::overloaded{[&ptr](const Node::Children& c) noexcept { ptr = &c; }, - [](const Node::Child&) noexcept { assert(0); }}, - idx.content); - res += '.'; - } - assertrx(ptr->size() > path.back()); - res += (*ptr)[path.back()].name; - return res; - } - void AddIndex(const FieldPath& path, bool isSparse) { - if (path.empty()) return; - if (!isSparse) ns_.sparse = false; - Node::Children* ptr = &std::get(ns_.content); - for (size_t i = 0, s = path.size() - 1; i < s; ++i) { - assertrx(ptr->size() > path[i]); - if (!isSparse) { - (*ptr)[path[i]].sparse = false; - } - std::visit(reindexer::overloaded{[&ptr](Node::Children& c) noexcept { ptr = &c; }, [](Node::Child&) noexcept { assert(0); }}, - (*ptr)[path[i]].content); - } - assertrx(ptr->size() > path.back()); - mark((*ptr)[path.back()], isSparse); - } - void NewItem(reindexer::WrSerializer&, RandomGenerator&); + size_t FieldsCount(const FieldPath&) const noexcept; + bool IsStruct(const FieldPath&) const noexcept; + bool IsPoint(const FieldPath&) const noexcept; + bool IsTtl(const FieldPath&, const std::vector&) const noexcept; + IsArrayT IsArray(const FieldPath&) const noexcept; + FieldType GetFieldType(const FieldPath&) const noexcept; + void SetFieldType(const FieldPath&, FieldType) noexcept; + std::string GetJsonPath(const FieldPath&) const noexcept; + void AddIndex(const FieldPath&, size_t index, IsSparseT); + void NewItem(reindexer::WrSerializer&, RandomGenerator&, const std::vector&); void Dump(std::ostream& os, size_t offset) const { ns_.Dump(os, offset); } - FieldPath AddRndPkField(RandomGenerator& rnd) { - auto& children = std::get(ns_.content); - children.emplace_back(Node{rnd.FieldName(generatedNames_), Node::Child{rnd.RndPkIndexFieldType()}}); - children.back().array = false; - children.back().sparse = false; - return {children.size() - 1}; - } + FieldPath AddRndPkField(RandomGenerator&); private: - static void mark(Node& node, bool isSparse) { - if (!isSparse) { - node.sparse = false; - } - std::visit(reindexer::overloaded{[](Node::Child&) noexcept {}, - [isSparse](Node::Children& c) noexcept { - for (Node& n : c) mark(n, isSparse); - }}, - node.content); - } - void fillChildren(Node::Children& children, RandomGenerator& rnd, unsigned level, bool& canBeArray, bool& canBeSparse) { - const size_t fieldsCount = rnd.FieldsCount(level == 0); - children.reserve(fieldsCount); - for (size_t i = 0; i < fieldsCount; ++i) { - auto fName = rnd.FieldName(generatedNames_); - const auto type = rnd.RndFieldType(level); - if (type == FieldType::Struct) { - children.emplace_back(Node{std::move(fName), Node::Children{}}); - fillChildren(std::get(children.back().content), rnd, level + 1, canBeArray, canBeSparse); - if (canBeArray || rnd.RndErr()) { - children.back().array = rnd.RndArrayField(); - } - if (!canBeSparse && !rnd.RndErr()) { - children.back().sparse = false; - } - } else { - children.emplace_back(Node{std::move(fName), Node::Child{type}}); - if (type == FieldType::Point) { - canBeSparse = false; - canBeArray = false; - children.back().sparse = false; - } - if (canBeArray || rnd.RndErr()) { - children.back().array = rnd.RndArrayField(); - } - } - } - } - const Node::Children& findLastContainer(const FieldPath& path) const noexcept { - const Node::Children* ptr = &std::get(ns_.content); - for (size_t i = 0, s = path.size() - 1; i < s; ++i) { - assertrx(ptr->size() > path[i]); - std::visit(reindexer::overloaded{[&ptr](const Node::Children& c) noexcept { ptr = &c; }, - [](const Node::Child&) noexcept { assert(0); }}, - (*ptr)[path[i]].content); - } - return *ptr; - } - Node::Children& findLastContainer(const FieldPath& path) noexcept { - Node::Children* ptr = &std::get(ns_.content); - for (size_t i = 0, s = path.size() - 1; i < s; ++i) { - assertrx(ptr->size() > path[i]); - std::visit(reindexer::overloaded{[&ptr](Node::Children& c) noexcept { ptr = &c; }, [](Node::Child&) noexcept { assert(0); }}, - (*ptr)[path[i]].content); - } - return *ptr; - } - void toJson(reindexer::JsonBuilder&, const Node::Children&, RandomGenerator&); - void rndValueToJson(reindexer::JsonBuilder&, FieldType, std::string_view name, RandomGenerator&); + static void addIndex(Node&, size_t index, IsSparseT); + void fillChildren(Node::Children&, RandomGenerator&, unsigned level, bool& canBeArray, bool& canBeSparse); + const Node::Children& findLastContainer(const FieldPath&) const noexcept; + Node::Children& findLastContainer(const FieldPath&) noexcept; + void toJson(reindexer::JsonBuilder&, const Node::Children&, RandomGenerator&, const std::vector&); + void rndValueToJson(reindexer::JsonBuilder&, FieldType, std::string_view name, const std::vector& idxNumbers, + const std::vector&, RandomGenerator&); + static bool isTtl(const std::vector& idxNumbers, const std::vector&) noexcept; + Node ns_; std::unordered_set generatedNames_; }; diff --git a/cpp_src/gtests/tests/fixtures/fuzzing/query_generator.cc b/cpp_src/gtests/tests/fixtures/fuzzing/query_generator.cc index 520c3abe7..da0f1d081 100644 --- a/cpp_src/gtests/tests/fixtures/fuzzing/query_generator.cc +++ b/cpp_src/gtests/tests/fixtures/fuzzing/query_generator.cc @@ -1,12 +1,13 @@ #include "query_generator.h" #include "core/query/query.h" +#include "index.h" +#include "ns.h" namespace fuzzing { reindexer::Query QueryGenerator::operator()() { if (namespaces_.empty() || rndGen_.RndErr()) { - std::unordered_set generatedNames; - return reindexer::Query{rndGen_.NsName(generatedNames)}; + return reindexer::Query{rndGen_.GenerateNsName()}; } const auto& ns = rndGen_.RndWhich(namespaces_); reindexer::Query query{ns.GetName()}; @@ -15,21 +16,24 @@ reindexer::Query QueryGenerator::operator()() { case Index: if (const auto& indexes = ns.GetIndexes(); !indexes.empty()) { const auto& idx = rndGen_.RndWhich(indexes); - std::visit(reindexer::overloaded{[&](const Index::Child& c) { rndGen_.RndWhere(query, idx.name, {c.type}); }, + std::visit(reindexer::overloaded{[&](const Index::Child& c) { rndGen_.RndWhere(query, idx.Name(), c.type, idx.Type()); }, [&](const Index::Children& c) { std::vector types; types.reserve(c.size()); for (const auto& child : c) types.push_back(child.type); - rndGen_.RndWhere(query, idx.name, types); + rndGen_.RndWhereComposite(query, idx.Name(), std::move(types), idx.Type()); }}, - idx.content); + idx.Content()); } break; case Field: { const auto path = rndGen_.RndField(ns.GetScheme()); const FieldType type = ns.GetScheme().GetFieldType(path); - if (type != FieldType::Struct) { - rndGen_.RndWhere(query, ns.GetScheme().GetJsonPath(path), {type}); + if (type == FieldType::Struct) { // TODO object find + } else { + const std::optional indexType = + ns.GetScheme().IsTtl(path, ns.GetIndexes()) ? IndexType::Ttl : std::optional{}; + rndGen_.RndWhere(query, ns.GetScheme().GetJsonPath(path), type, indexType); } } break; case Empty: diff --git a/cpp_src/gtests/tests/fixtures/fuzzing/query_generator.h b/cpp_src/gtests/tests/fixtures/fuzzing/query_generator.h index 2724b23c5..c2644492b 100644 --- a/cpp_src/gtests/tests/fixtures/fuzzing/query_generator.h +++ b/cpp_src/gtests/tests/fixtures/fuzzing/query_generator.h @@ -1,6 +1,6 @@ #pragma once -#include "ns.h" +#include "random_generator.h" namespace reindexer { @@ -10,10 +10,11 @@ class Query; namespace fuzzing { +class Ns; + class QueryGenerator { public: - QueryGenerator(const std::vector& nss, std::ostream& os, RandomGenerator::ErrFactorType errorFactor) - : namespaces_{nss}, rndGen_{os, errorFactor} {} + QueryGenerator(const std::vector& nss, RandomGenerator::ErrFactorType errorFactor) : namespaces_{nss}, rndGen_{errorFactor} {} reindexer::Query operator()(); private: diff --git a/cpp_src/gtests/tests/fixtures/fuzzing/random_generator.cc b/cpp_src/gtests/tests/fixtures/fuzzing/random_generator.cc index 25e0f640e..82d85a68e 100644 --- a/cpp_src/gtests/tests/fixtures/fuzzing/random_generator.cc +++ b/cpp_src/gtests/tests/fixtures/fuzzing/random_generator.cc @@ -1,17 +1,97 @@ #include "random_generator.h" +#include +#include #include +#include +#include "core/payload/fieldsset.h" #include "core/query/query.h" +#include "index.h" #include "ns_scheme.h" namespace fuzzing { -RandomGenerator::RandomGenerator(std::ostream& os, ErrFactorType errorFactor) - : gen_(std::chrono::system_clock::now().time_since_epoch().count()), errFactor_{errorFactor} { +std::string& RandomGenerator::out() noexcept { + static std::string outStr; + return outStr; +} + +std::unique_ptr& RandomGenerator::in() noexcept { + static std::unique_ptr f; + return f; +} + +void RandomGenerator::SetOut(std::string o) { + ASSERT_TRUE(out().empty()); + ASSERT_FALSE(in()); + out() = std::move(o); + { + std::ifstream f{out()}; + ASSERT_FALSE(f.is_open()) << "File '" << out() << "' already exists"; + } +} + +void RandomGenerator::SetIn(const std::string& i) { + ASSERT_FALSE(in()); + ASSERT_TRUE(out().empty()); + in() = std::make_unique(i); + ASSERT_TRUE(in()->is_open()) << "Cannot open file '" << i << '\''; + in()->exceptions(std::ios_base::badbit | std::ios_base::failbit | std::ios_base::eofbit); +} + +RandomGenerator::RandomEngine RandomGenerator::createRandomEngine() { + if (in()) { + RandomEngine ret; + std::string buf; + std::getline(*in(), buf); + std::istringstream ss{buf}; + ss >> ret; + return ret; + } else { + RandomEngine ret(std::chrono::system_clock::now().time_since_epoch().count()); + if (!out().empty()) { + std::ofstream file{out(), std::ios_base::app}; + if (file.is_open()) { + file.exceptions(std::ios_base::badbit | std::ios_base::failbit | std::ios_base::eofbit); + file << ret << std::endl; + } else { + EXPECT_TRUE(false) << "Cannot open file '" << out() << '\''; + } + } + return ret; + } +} + +RandomGenerator::RandomGenerator(ErrFactorType errorFactor) : gen_{createRandomEngine()}, errFactor_{errorFactor} { assertrx(errFactor_.first < errFactor_.second); errParams_ = {static_cast(errFactor_.second - errFactor_.first), static_cast(errFactor_.first)}; - os << gen_ << std::endl; } -RandomGenerator::RandomGenerator(std::istream& is) { is >> gen_; } + +size_t RandomGenerator::FieldsCount(bool firstLevel) { + if (RndErr()) { + enum Err : uint8_t { Zero, TooMany, END = TooMany }; + switch (RndWhich()) { + case Zero: + return 0; + case TooMany: + return RndInt(0, 10'000); + default: + assertrx(0); + } + } + if (firstLevel) { + enum Size : uint8_t { Normal, Long, END = Long }; + switch (RndWhich()) { + case Normal: + return RndInt(1, 9); + case Long: + return RndInt(10, 100); + default: + assertrx(false); + std::abort(); + } + } + return RndInt(1, 5); +} std::string RandomGenerator::FieldName(std::unordered_set& generatedNames) { // TODO static constexpr char alfas[] = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; @@ -148,7 +228,7 @@ FieldPath RandomGenerator::RndScalarField(const NsScheme& nsScheme) { const int end = idx + size; while (idx < end) { res.back() = idx % size; - if (!nsScheme.IsArray(res) && !nsScheme.IsPoint(res)) break; + if (nsScheme.IsArray(res) == IsArrayT::No && !nsScheme.IsPoint(res)) break; ++idx; } if (idx == end) return {}; @@ -156,7 +236,7 @@ FieldPath RandomGenerator::RndScalarField(const NsScheme& nsScheme) { return res; } -std::string RandomGenerator::IndexFieldType(fuzzing::FieldType ft) { +std::string RandomGenerator::IndexFieldType(FieldType ft) { static const std::string types[] = {"bool", "int", "int64", "double", "string", "uuid", "point", "composite"}; if (RndErr()) { // TODO rnd string @@ -167,52 +247,158 @@ std::string RandomGenerator::IndexFieldType(fuzzing::FieldType ft) { return types[i]; } -std::string RandomGenerator::RndIndexType(fuzzing::FieldType ft, bool pk) { - static const std::string types[] = {"-", "hash", "tree", "ttl", "text", "fuzzytext", "rtree"}; - static const std::vector availableTypes[] = { - {0}, // Bool - {0, 1, 2}, // Int - {0, 1, 2, 3}, // Int64 - {0, 2}, // Double - {0, 1, 2 /*, 4, 5*/}, // String // TODO FT indexes - {1}, // Uuid - {6}, // Point - {1, 2 /*, 4, 5*/} // Struct // TODO FT indexes +IndexType RandomGenerator::RndIndexType(IndexType it) { + if (RndErr()) { + return RndWhich(); // TODO + } + return it; +} + +template * Availables> +IndexType RandomGenerator::rndIndexType(const std::vector& fieldTypes) { + if (RndErr()) { + // TODO rnd string + return RndWhich(); // TODO + } + assertrx(!fieldTypes.empty()); + std::vector availables; + { + const size_t f = static_cast(fieldTypes[0]); + assertrx(f < N); + availables = Availables[f]; + } + for (size_t i = 1, s = fieldTypes.size(); i < s; ++i) { + const size_t f = static_cast(fieldTypes[i]); + std::vector tmp; + tmp.reserve(availables.size()); + assertrx(f < N); + std::set_intersection(availables.begin(), availables.end(), Availables[f].begin(), Availables[f].end(), std::back_inserter(tmp)); + availables = tmp; + } + if (availables.empty()) { + return RndWhich(); // TODO + } else { + return RndWhich(availables); + } +} + +IndexType RandomGenerator::RndIndexType(const std::vector& fieldTypes) { + static const std::vector availableTypes[] = { + {IndexType::Store}, // Bool + {IndexType::Store, IndexType::Hash, IndexType::Tree}, // Int + {IndexType::Store, IndexType::Hash, IndexType::Tree, IndexType::Ttl}, // Int64 + {IndexType::Store, IndexType::Tree}, // Double + {IndexType::Store, IndexType::Hash, IndexType::Tree}, // String // TODO IndexType::FastFT IndexType::FuzzyFT + {IndexType::Hash}, // Uuid + {IndexType::RTree}, // Point + {IndexType::Hash, IndexType::Tree} // Struct // TODO IndexType::FastFT IndexType::FuzzyFT }; - static const std::vector availablePkTypes[] = { - {}, // Bool - {1, 2}, // Int - {1, 2, 3}, // Int64 - {2}, // Double - {1, 2}, // String - {1}, // Uuid - {}, // Point - {1, 2} // Struct + return rndIndexType, availableTypes>(fieldTypes); +} + +IndexType RandomGenerator::RndPkIndexType(const std::vector& fieldTypes) { + static const std::vector availablePkTypes[] = { + {}, // Bool + {IndexType::Hash, IndexType::Tree}, // Int + {IndexType::Hash, IndexType::Tree, IndexType::Ttl}, // Int64 + {IndexType::Tree}, // Double + {IndexType::Hash, IndexType::Tree}, // String + {IndexType::Hash}, // Uuid + {}, // Point + {IndexType::Hash, IndexType::Tree} // Struct }; + return rndIndexType, availablePkTypes>(fieldTypes); +} + +size_t RandomGenerator::ArraySize() { + if (RndErr()) return RndInt(0, 100'000); + enum Size : uint8_t { Short, Normal, Long, VeryLong, END = VeryLong }; + switch (RndWhich()) { + case Short: + return RndInt(0, 5); + case Normal: + return RndInt(6, 20); + case Long: + return RndInt(21, 200); + case VeryLong: + return RndInt(201, 10'000); + default: + assertrx(false); + std::abort(); + } +} + +size_t RandomGenerator::IndexesCount() { if (RndErr()) { - // TODO rnd string - return RndWhich(types); + enum Err : uint8_t { Zero, TooMany, END = TooMany }; + switch (RndWhich()) { + case Zero: + return 0; + case TooMany: + return RndInt(reindexer::kMaxIndexes, 5 + reindexer::kMaxIndexes); + default: + assertrx(0); + } } - const size_t i = static_cast(ft); - size_t n; - if (pk) { - assertrx(i < std::size(availablePkTypes)); - if (availablePkTypes[i].empty()) { - return RndWhich(types); + enum Count : uint8_t { Few, Normal, Many, TooMany, END = TooMany }; + switch (RndWhich()) { + case Few: + return RndInt(1, 3); + case Normal: + return RndInt(4, 20); + case Many: + return RndInt(21, 63); + case TooMany: + return RndInt(64, reindexer::kMaxIndexes); + default: + assertrx(false); + std::abort(); + } +} + +size_t RandomGenerator::compositeIndexSize(size_t scalarIndexesCount) { + if (RndErr()) { + enum Err : uint8_t { Zero, /*One,*/ TooMany, END = TooMany }; + switch (RndWhich()) { + case Zero: + return 0; + /*case One: + return 1;*/ + case TooMany: + return RndInt(0, 10'000); + default: + assertrx(0); + } + } + assertrx(scalarIndexesCount >= 1); + return RndInt(1, scalarIndexesCount); +} + +std::vector RandomGenerator::RndFieldsForCompositeIndex(const std::vector& scalarIndexes) { + std::vector result; + const size_t count = compositeIndexSize(scalarIndexes.size()); + result.reserve(count); + const bool uniqueFields = count <= scalarIndexes.size() && !RndErr(); + // TODO unexisted and not indexed fields + if (uniqueFields) { + auto scalars = scalarIndexes; + while (result.size() < count) { + const size_t idx = rndSize(0, scalars.size() - 1); + result.push_back(scalars[idx]); + scalars.erase(scalars.begin() + idx); } - n = RndWhich(availablePkTypes[i]); } else { - assertrx(i < std::size(availableTypes)); - n = RndWhich(availableTypes[i]); + while (result.size() < count) { + result.push_back(scalarIndexes[rndSize(0, scalarIndexes.size() - 1)]); + } } - assertrx(n < std::size(types)); - return types[n]; + return result; } template <> constexpr size_t RandomGenerator::itemsCount = CondType::CondDWithin + 1; -CondType RandomGenerator::rndCond(fuzzing::FieldType ft) { // TODO array +CondType RandomGenerator::rndCond(FieldType ft) { // TODO array if (RndErr()) { return RndWhich(); } @@ -240,8 +426,6 @@ std::string RandomGenerator::rndStrUuidValue(bool noErrors) { if (!noErrors && RndErr()) { err = RndWhich(); } - std::string res; - if (err == Empty) return res; size_t size = 32; switch (err) { case Short: @@ -253,15 +437,17 @@ std::string RandomGenerator::rndStrUuidValue(bool noErrors) { case TooLong: size = RndInt(51, 100'000); break; - case NoErrors: case Empty: + return {}; + case NoErrors: case WrongVariant: case WrongChar: break; default: - assert(0); + assertrx(0); abort(); } + std::string res; res.reserve(size + 4); if (RndBool(0.001)) { res = std::string(std::string::size_type{size}, '0'); @@ -293,94 +479,127 @@ std::string RandomGenerator::rndStrUuidValue(bool noErrors) { reindexer::Uuid RandomGenerator::rndUuidValue() { return reindexer::Uuid{rndStrUuidValue(true)}; } -void RandomGenerator::RndWhere(reindexer::Query& query, const std::string& field, - const std::vector& types) { // TODO array +int64_t RandomGenerator::RndTtlValue() { + const int64_t now = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); + // TODO uncomment this after TTL subscribe done + /*enum Size : uint8_t { Negative, FarPast, Past, Now, Future, FarFuture, AnyShort, Any, END = Any }; + switch (RndWhich()) { case Negative: return rndInt64(std::numeric_limits::min(), 0); case FarPast: + return rndInt64(0, now - 10'000); + case Past: + return rndInt64(now - 10'000, now - 10); + case Now: + return rndInt64(now - 10, now + 10); + case Future: + return rndInt64(now + 10, now + 10'000); + case FarFuture: + return rndInt64(now + 10'000, std::numeric_limits::max()); + case AnyShort: + return rndInt64(-50, 50); + case Any: + return rndInt64(std::numeric_limits::min(), std::numeric_limits::max()); + default: + assertrx(false); + std::abort(); + }*/ + return rndInt64(now + 10'000, std::numeric_limits::max()); +} + +void RandomGenerator::RndWhere(reindexer::Query& query, const std::string& field, FieldType fieldType, + std::optional indexType) { // TODO array + if (RndErr()) { + return RndWhereComposite(query, field, RndFieldTypesArray({fieldType}), indexType); + } std::unordered_set generatedNames; - assertrx(!types.empty()); const std::string fldName = FieldName(field, generatedNames); - const auto type = types.size() > 1 ? fuzzing::FieldType::Struct : types[0]; - const auto cond = rndCond(type); - switch (RndFieldType(type)) { - case fuzzing::FieldType::Bool: + const auto cond = rndCond(fieldType); + switch (RndFieldType(fieldType)) { + case FieldType::Bool: query.Where(fldName, cond, RndBool(0.5)); break; - case fuzzing::FieldType::Int: + case FieldType::Int: query.Where(fldName, cond, RndIntValue()); break; - case fuzzing::FieldType::Int64: - query.Where(fldName, cond, RndInt64Value()); + case FieldType::Int64: + if (indexType == IndexType::Ttl) { + query.Where(fldName, cond, RndTtlValue()); + } else { + query.Where(fldName, cond, RndInt64Value()); + } break; - case fuzzing::FieldType::Double: + case FieldType::Double: query.Where(fldName, cond, RndDoubleValue()); break; - case fuzzing::FieldType::String: + case FieldType::String: query.Where(fldName, cond, RndStringValue()); break; - case fuzzing::FieldType::Uuid: + case FieldType::Uuid: if (RndBool(0.5)) { query.Where(fldName, cond, rndUuidValue()); } else { query.Where(fldName, cond, rndStrUuidValue(false)); } break; - case fuzzing::FieldType::Point: + case FieldType::Point: query.Where(fldName, cond, {reindexer::Variant{reindexer::Point{RndDoubleValue(), RndDoubleValue()}}, reindexer::Variant{RndErr() ? RndDoubleValue() : std::abs(RndDoubleValue())}}); break; - case fuzzing::FieldType::Struct: // TODO - if (type == fuzzing::FieldType::Struct) { - } else { - } + case FieldType::Struct: // TODO break; default: assertrx(0); } } -std::ostream& operator<<(std::ostream& os, FieldType ft) { - switch (ft) { - case FieldType::Bool: - return os << "bool"; - case FieldType::Int: - return os << "int"; - case FieldType::Int64: - return os << "int64"; - case FieldType::Double: - return os << "double"; - case FieldType::String: - return os << "string"; - case FieldType::Uuid: - return os << "uuid"; - case FieldType::Point: - return os << "point"; - case FieldType::Struct: - return os << "struct"; - default: - assertrx(0); +void RandomGenerator::RndWhereComposite(reindexer::Query& query, const std::string& field, std::vector&& fieldTypes, + std::optional indexType) { // TODO array + if (RndErr()) { + return RndWhere(query, field, RndFieldType(), indexType); } - return os; -} - -reindexer::KeyValueType ToKeyValueType(FieldType ft) { - switch (ft) { - case FieldType::Bool: - return reindexer::KeyValueType::Bool{}; - case FieldType::Int: - return reindexer::KeyValueType::Int{}; - case FieldType::Int64: - return reindexer::KeyValueType::Int64{}; - case FieldType::Double: - return reindexer::KeyValueType::Double{}; - case FieldType::String: - return reindexer::KeyValueType::String{}; - case FieldType::Uuid: - return reindexer::KeyValueType::Uuid{}; - case FieldType::Point: - case FieldType::Struct: - default: - assertrx(0); + std::unordered_set generatedNames; + const std::string fldName = FieldName(field, generatedNames); + fieldTypes = RndFieldTypesArray(std::move(fieldTypes)); + const auto cond = rndCond(FieldType::Struct); + reindexer::VariantArray keys; + keys.reserve(fieldTypes.size()); + for (const FieldType ft : fieldTypes) { + switch (ft) { + case FieldType::Bool: + keys.emplace_back(RndBool(0.5)); + break; + case FieldType::Int: + keys.emplace_back(RndIntValue()); + break; + case FieldType::Int64: + if (indexType == IndexType::Ttl) { + keys.emplace_back(RndTtlValue()); + } else { + keys.emplace_back(RndInt64Value()); + } + break; + case FieldType::Double: + keys.emplace_back(RndDoubleValue()); + break; + case FieldType::String: + keys.emplace_back(RndStringValue()); + break; + case FieldType::Uuid: + if (RndBool(0.5)) { + keys.emplace_back(rndUuidValue()); + } else { + keys.emplace_back(rndStrUuidValue(false)); + } + break; + case FieldType::Point: + keys.emplace_back(reindexer::Point{RndDoubleValue(), RndDoubleValue()}); + break; + case FieldType::Struct: // TODO + break; + default: + assertrx(0); + } } + query.WhereComposite(fldName, cond, {std::move(keys)}); } } // namespace fuzzing diff --git a/cpp_src/gtests/tests/fixtures/fuzzing/random_generator.h b/cpp_src/gtests/tests/fixtures/fuzzing/random_generator.h index 7a628fe85..bebb70490 100644 --- a/cpp_src/gtests/tests/fixtures/fuzzing/random_generator.h +++ b/cpp_src/gtests/tests/fixtures/fuzzing/random_generator.h @@ -1,10 +1,12 @@ #pragma once -#include +#include +#include #include #include #include "core/type_consts.h" #include "tools/assertrx.h" +#include "types.h" namespace reindexer { @@ -16,137 +18,69 @@ class KeyValueType; namespace fuzzing { -struct Index; class NsScheme; -enum class FieldType { Bool, Int, Int64, Double, String, Uuid, Point, Struct, END = Struct }; -reindexer::KeyValueType ToKeyValueType(FieldType); -std::ostream& operator<<(std::ostream&, FieldType); -using FieldPath = std::vector; - class RandomGenerator { using ErrFactorInt = uint32_t; public: using ErrFactorType = std::pair; - RandomGenerator(std::ostream&, ErrFactorType errorFactor); - RandomGenerator(std::istream&); + RandomGenerator(ErrFactorType errorFactor); - size_t FieldsCount(bool firstLevel) { - if (RndErr()) { - enum Err : uint8_t { Zero, TooMany, END = TooMany }; - switch (RndWhich()) { - case Zero: - return 0; - case TooMany: - return RndInt(0, 10'000); - default: - assertrx(0); - } - } - if (firstLevel) { - enum Size : uint8_t { Normal, Long, END = Long }; - switch (RndWhich()) { - case Normal: - return RndInt(1, 9); - case Long: - return RndInt(10, 100); - default: - assertrx(false); - std::abort(); - } - } - return RndInt(1, 5); - } - fuzzing::FieldType RndFieldType(unsigned level) { + size_t FieldsCount(bool firstLevel); + FieldType RndFieldType(unsigned level) { const bool withoutStruct = level > 2 && (level > 5 || !RndBool(1.0 / (2 << (2 * level)))); - return static_cast( - RndInt(static_cast(fuzzing::FieldType::Bool), static_cast(fuzzing::FieldType::Struct) - withoutStruct)); + return static_cast(RndInt(static_cast(FieldType::Bool), static_cast(FieldType::Struct) - withoutStruct)); } - fuzzing::FieldType RndFieldType() { - return static_cast( - RndInt(static_cast(fuzzing::FieldType::Bool), static_cast(fuzzing::FieldType::Point))); + FieldType RndFieldType() { + return static_cast(RndInt(static_cast(FieldType::Bool), static_cast(FieldType::Point))); } - fuzzing::FieldType RndPkIndexFieldType() { - return static_cast( - RndInt(static_cast(fuzzing::FieldType::Int), static_cast(fuzzing::FieldType::Uuid))); + FieldType RndPkIndexFieldType() { + return static_cast(RndInt(static_cast(FieldType::Int), static_cast(FieldType::Uuid))); } - std::string IndexFieldType(fuzzing::FieldType); - fuzzing::FieldType RndFieldType(fuzzing::FieldType type) { + std::string IndexFieldType(FieldType); + FieldType RndFieldType(FieldType type) { if (RndErr()) { - return RndWhich(); + return RndWhich(); } return type; } - std::string RndIndexType(fuzzing::FieldType, bool pk); - bool RndArrayField() { return RndBool(0.2); } - bool RndArrayField(bool array) { return RndErr() ? !array : array; } - size_t ArraySize() { - if (RndErr()) return RndInt(0, 100'000); - enum Size : uint8_t { Short, Normal, Long, VeryLong, END = VeryLong }; - switch (RndWhich()) { - case Short: - return RndInt(0, 5); - case Normal: - return RndInt(6, 20); - case Long: - return RndInt(21, 200); - case VeryLong: - return RndInt(201, 10'000); - default: - assertrx(false); - std::abort(); + std::vector RndFieldTypesArray(std::vector&& types) { + if (!RndErr()) { + return std::move(types); } - } - bool PkIndex(bool pk) { return RndErr() ? RndBool(0.5) : pk; } - bool SparseIndex(bool pk) { return pk ? RndErr() : RndBool(0.2); } - bool DenseIndex() { return RndBool(0.2); } - int64_t ExpiredIndex() { return RndInt(0, 100'000); } // TODO - size_t IndexesCount() { - if (RndErr()) { - enum Err : uint8_t { Zero, TooMany, END = TooMany }; - switch (RndWhich()) { - case Zero: - return 0; - case TooMany: - return RndInt(0, 1'000); - default: - assertrx(0); - } + if (RndBool(0.5)) { + types.resize(compositeIndexSize(types.size())); } - enum Count : uint8_t { Few, Normal, Many, TooMany, END = TooMany }; - switch (RndWhich()) { - case Few: - return RndInt(1, 3); - case Normal: - return RndInt(4, 6); - case Many: - return RndInt(7, 20); - case TooMany: - return RndInt(21, 63); - default: - assertrx(false); - std::abort(); + for (auto& t : types) { + t = RndFieldType(); } + return std::move(types); } - bool CompositeIndex() { return RndBool(0.2); } - bool UniqueName() { return RndBool(0.5); } - size_t CompositeIndexSize() { + IndexType RndIndexType(const std::vector&); + IndexType RndPkIndexType(const std::vector&); + IndexType RndIndexType(IndexType); + IsArrayT RndArrayField() { return RndBool(0.2) ? IsArrayT::Yes : IsArrayT::No; } + IsArrayT RndArrayField(IsArrayT array) { if (RndErr()) { - enum Err : uint8_t { Zero, One, TooMany, END = TooMany }; - switch (RndWhich()) { - case Zero: - return 0; - case One: - return 1; - case TooMany: - return RndInt(0, 10'000); - default: - assertrx(0); - } + return array == IsArrayT::Yes ? IsArrayT::No : IsArrayT::Yes; } - return RndInt(2, 5); + return array; + } + size_t ArraySize(); + bool PkIndex(bool pk) { return RndErr() ? RndBool(0.5) : pk; } + IsSparseT RndSparseIndex(FieldType fldType) { + const bool couldBeSparse = fldType != FieldType::Struct && fldType != FieldType::Uuid; // TODO remove uuid #1470 + return (couldBeSparse ? RndBool(0.2) : RndErr()) ? IsSparseT::Yes : IsSparseT::No; } + bool RndSparseIndex(IsSparseT isSparse) { return (isSparse == IsSparseT::Yes) != RndErr(); } + bool DenseIndex() { return RndBool(0.2); } + int64_t ExpiredIndex() { return RndInt(0, 100'000); } // TODO + size_t IndexesCount(); + bool CompositeIndex(size_t scalarIndexesCount) { return scalarIndexesCount < 1 ? RndErr() : RndBool(0.2); } + bool UniqueName() { return RndBool(0.5); } + size_t compositeIndexSize(size_t scalarIndexesCount); + std::vector RndFieldsForCompositeIndex(const std::vector& scalarIndexes); std::string FieldName(std::unordered_set& generatedNames); std::string FieldName(const std::string& fieldName, std::unordered_set& generatedNames) { if (RndErr()) return FieldName(generatedNames); @@ -155,9 +89,12 @@ class RandomGenerator { FieldPath RndField(const NsScheme&); FieldPath RndScalarField(const NsScheme&); std::string IndexName(std::unordered_set& generatedNames) { return FieldName(generatedNames); } // TODO - std::string NsName(std::unordered_set& generatedNames) { return FieldName(generatedNames); } // TODO - std::string NsName(const std::string& nsName, std::unordered_set& generatedNames) { - if (RndErr()) return NsName(generatedNames); + std::string GenerateNsName() { // TODO + std::unordered_set generatedNames; + return FieldName(generatedNames); + } + std::string NsName(const std::string& nsName) { + if (RndErr()) return GenerateNsName(); return nsName; } int RndInt(int min, int max) { return rndInt_(gen_, IntRndParams(min, max)); } @@ -200,7 +137,7 @@ class RandomGenerator { return err; } char RndChar() { return rndChar_(gen_); } - bool NeedThisNode(bool sparse) { return sparse ? RndBool(0.5) : !RndErr(); } + bool NeedThisNode(IsSparseT sparse) { return sparse == IsSparseT::Yes ? RndBool(0.5) : !RndErr(); } int RndIntValue() { enum Size : uint8_t { Short, Long, END = Long }; switch (RndWhich()) { @@ -225,6 +162,7 @@ class RandomGenerator { std::abort(); } } + int64_t RndTtlValue(); bool RndBool(double p) { return rndBool_(gen_, BoolRndParams{p}); } double RndDoubleValue() { enum Size : uint8_t { Short, Long, END = Long }; @@ -273,14 +211,19 @@ class RandomGenerator { } template const auto& RndWhich(const Cont& cont) { - assert(!std::empty(cont)); + assertrx(!std::empty(cont)); auto it = std::begin(cont); std::advance(it, rndSize(0, std::size(cont) - 1)); return *it; } - void RndWhere(reindexer::Query&, const std::string& field, const std::vector&); + void RndWhere(reindexer::Query&, const std::string& field, FieldType, std::optional); + void RndWhereComposite(reindexer::Query&, const std::string& field, std::vector&&, std::optional); + + static void SetOut(std::string); + static void SetIn(const std::string&); private: + using RandomEngine = std::default_random_engine; using IntRndParams = std::uniform_int_distribution<>::param_type; using SizeRndParams = std::uniform_int_distribution::param_type; using Int64RndParams = std::uniform_int_distribution::param_type; @@ -291,11 +234,16 @@ class RandomGenerator { int rndInt(IntRndParams params) { return rndInt_(gen_, params); } int64_t rndInt64(int64_t min, int64_t max) { return rndInt64_(gen_, Int64RndParams(min, max)); } size_t rndSize(size_t min, size_t max) { return rndSize_(gen_, SizeRndParams(min, max)); } - CondType rndCond(fuzzing::FieldType); + CondType rndCond(FieldType); std::string rndStrUuidValue(bool noErrors); reindexer::Uuid rndUuidValue(); + template * Availables> + IndexType rndIndexType(const std::vector&); + static std::string& out() noexcept; + static std::unique_ptr& in() noexcept; + static RandomEngine createRandomEngine(); - std::default_random_engine gen_; + RandomEngine gen_; ErrFactorType errFactor_; ErrorParams errParams_; std::uniform_int_distribution<> rndInt_; diff --git a/cpp_src/gtests/tests/fixtures/fuzzing/types.cc b/cpp_src/gtests/tests/fixtures/fuzzing/types.cc new file mode 100644 index 000000000..11b50b658 --- /dev/null +++ b/cpp_src/gtests/tests/fixtures/fuzzing/types.cc @@ -0,0 +1,90 @@ +#include "types.h" + +#include +#include +#include "core/key_value_type.h" + +namespace fuzzing { + +std::ostream& operator<<(std::ostream& os, FieldType ft) { + switch (ft) { + case FieldType::Bool: + return os << "bool"; + case FieldType::Int: + return os << "int"; + case FieldType::Int64: + return os << "int64"; + case FieldType::Double: + return os << "double"; + case FieldType::String: + return os << "string"; + case FieldType::Uuid: + return os << "uuid"; + case FieldType::Point: + return os << "point"; + case FieldType::Struct: + return os << "struct"; + default: + assertrx(0); + } + return os; +} + +reindexer::KeyValueType ToKeyValueType(FieldType ft) { + switch (ft) { + case FieldType::Bool: + return reindexer::KeyValueType::Bool{}; + case FieldType::Int: + return reindexer::KeyValueType::Int{}; + case FieldType::Int64: + return reindexer::KeyValueType::Int64{}; + case FieldType::Double: + return reindexer::KeyValueType::Double{}; + case FieldType::String: + return reindexer::KeyValueType::String{}; + case FieldType::Uuid: + return reindexer::KeyValueType::Uuid{}; + case FieldType::Point: + return reindexer::KeyValueType::Undefined{}; // TODO change to KeyValueType::Point #1352 + case FieldType::Struct: + default: + assertrx(0); + } +} + +std::ostream& operator<<(std::ostream& os, const FieldPath& fp) { + os << '['; + for (size_t i = 0, s = fp.size(); i < s; ++i) { + if (i != 0) { + os << ' '; + } + os << fp[i]; + } + return os << ']' << std::endl; +} + +std::string_view ToText(IndexType it) { + using namespace std::string_view_literals; + switch (it) { + case IndexType::Store: + return "-"sv; + case IndexType::Hash: + return "hash"sv; + case IndexType::Tree: + return "tree"sv; + case IndexType::Ttl: + return "ttl"sv; + case IndexType::FastFT: + return "text"sv; + case IndexType::FuzzyFT: + return "fuzzytext"sv; + case IndexType::RTree: + return "rtree"sv; + default: + assertrx(0); + } +} + +std::ostream& operator<<(std::ostream& os, IndexType it) { return os << ToText(it); } + +} // namespace fuzzing diff --git a/cpp_src/gtests/tests/fixtures/fuzzing/types.h b/cpp_src/gtests/tests/fixtures/fuzzing/types.h new file mode 100644 index 000000000..98ec500a8 --- /dev/null +++ b/cpp_src/gtests/tests/fixtures/fuzzing/types.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include + +namespace reindexer { + +class KeyValueType; + +} // namespace reindexer + +namespace fuzzing { + +enum class FieldType { Bool, Int, Int64, Double, String, Uuid, Point, Struct, END = Struct }; +reindexer::KeyValueType ToKeyValueType(FieldType); +std::ostream& operator<<(std::ostream&, FieldType); + +using FieldPath = std::vector; +std::ostream& operator<<(std::ostream&, const FieldPath&); + +enum class IndexType { Store, Hash, Tree, Ttl, FastFT, FuzzyFT, RTree, END = RTree }; +std::string_view ToText(IndexType); +std::ostream& operator<<(std::ostream&, IndexType); + +enum class IsArrayT : bool { Yes = true, No = false }; +enum class IsSparseT : bool { Yes = true, No = false }; + +} // namespace fuzzing diff --git a/cpp_src/gtests/tests/fixtures/get_pk_api.h b/cpp_src/gtests/tests/fixtures/get_pk_api.h index 22c4b970d..2916304d6 100644 --- a/cpp_src/gtests/tests/fixtures/get_pk_api.h +++ b/cpp_src/gtests/tests/fixtures/get_pk_api.h @@ -90,7 +90,7 @@ class ExtractPK : public testing::Test { Error err = db_->Select(query, qres); if (!err.ok()) return ResultType(err, QueryResults{}); - if (print) printQueryResults(query._namespace, qres); + if (print) printQueryResults(query.NsName(), qres); return ResultType(err, std::move(qres)); } diff --git a/cpp_src/gtests/tests/fixtures/grpcclient_api.h b/cpp_src/gtests/tests/fixtures/grpcclient_api.h index bd1d9ceb1..a95aeef3f 100644 --- a/cpp_src/gtests/tests/fixtures/grpcclient_api.h +++ b/cpp_src/gtests/tests/fixtures/grpcclient_api.h @@ -16,6 +16,7 @@ class GrpcClientApi : public ReindexerApi { public: void SetUp() { + reindexer::fs::RmDirAll(kStoragePath); YAML::Node y; y["storage"]["path"] = kStoragePath; y["logger"]["loglevel"] = "none"; @@ -26,7 +27,6 @@ class GrpcClientApi : public ReindexerApi { y["net"]["rpcaddr"] = "0.0.0.0:4443"; y["net"]["grpc"] = true; - reindexer::fs::RmDirAll(kStoragePath); auto err = srv_.InitFromYAML(YAML::Dump(y)); EXPECT_TRUE(err.ok()) << err.what(); @@ -41,7 +41,7 @@ class GrpcClientApi : public ReindexerApi { std::this_thread::sleep_for(std::chrono::milliseconds(20)); } - for (int i = 0; i < 5; i++) { + for (int i = 0; i < 10; i++) { auto channel = grpc::CreateChannel("127.0.0.1:16534", grpc::InsecureChannelCredentials()); if (channel->WaitForConnected(std::chrono::system_clock::now() + std::chrono::seconds(1))) { rx_ = reindexer::grpc::Reindexer::NewStub(channel); @@ -263,8 +263,9 @@ class GrpcClientApi : public ReindexerApi { reindexer::WrSerializer wrser; reindexer::Serializer rdser(cjson); - reindexer::CJsonDecoder decoder(const_cast(nsTypes.first)); - ASSERT_NO_THROW(decoder.Decode(pl, rdser, wrser)); + std::deque storage; + reindexer::CJsonDecoder decoder(const_cast(nsTypes.first), storage); + ASSERT_NO_THROW(decoder.Decode<>(pl, rdser, wrser)); ASSERT_TRUE(rdser.Eof()); } @@ -347,7 +348,7 @@ class GrpcClientApi : public ReindexerApi { std::unique_ptr rx_; uint64_t lastLsn_ = 0, lastRowId_ = INT_MAX; - const std::string kStoragePath = "/tmp/reindex_grpc_test/"; + const std::string kStoragePath = reindexer::fs::JoinPath(reindexer::fs::GetTempDir(), "reindex_grpc_test"); }; #endif diff --git a/cpp_src/gtests/tests/fixtures/item_move_semantics_api.h b/cpp_src/gtests/tests/fixtures/item_move_semantics_api.h index 1c0434473..ba10e1784 100644 --- a/cpp_src/gtests/tests/fixtures/item_move_semantics_api.h +++ b/cpp_src/gtests/tests/fixtures/item_move_semantics_api.h @@ -6,9 +6,6 @@ #include "gason/gason.h" #include "reindexer_api.h" -using reindexer::Item; -using reindexer::ItemImpl; - class ItemMoveSemanticsApi : public ReindexerApi { protected: const std::string pkField = "bookid"; @@ -18,14 +15,22 @@ class ItemMoveSemanticsApi : public ReindexerApi { void SetUp() override { ReindexerApi::SetUp(); - rt.reindexer->OpenNamespace(default_namespace, StorageOpts().Enabled(false)); - rt.reindexer->AddIndex(default_namespace, {"bookid", "hash", "int", IndexOpts().PK()}); - rt.reindexer->AddIndex(default_namespace, {"title", "text", "string", IndexOpts()}); - rt.reindexer->AddIndex(default_namespace, {"pages", "hash", "int", IndexOpts().PK()}); - rt.reindexer->AddIndex(default_namespace, {"price", "hash", "int", IndexOpts().PK()}); - rt.reindexer->AddIndex(default_namespace, {"genreid_fk", "hash", "int", IndexOpts().PK()}); - rt.reindexer->AddIndex(default_namespace, {"authorid_fk", "hash", "int", IndexOpts().PK()}); - rt.reindexer->Commit(default_namespace); + auto err = rt.reindexer->OpenNamespace(default_namespace, StorageOpts().Enabled(false)); + ASSERT_TRUE(err.ok()) << err.what(); + err = rt.reindexer->AddIndex(default_namespace, {"bookid", "hash", "int", IndexOpts().PK()}); + ASSERT_TRUE(err.ok()) << err.what(); + err = rt.reindexer->AddIndex(default_namespace, {"title", "text", "string", IndexOpts()}); + ASSERT_TRUE(err.ok()) << err.what(); + err = rt.reindexer->AddIndex(default_namespace, {"pages", "hash", "int", IndexOpts()}); + ASSERT_TRUE(err.ok()) << err.what(); + err = rt.reindexer->AddIndex(default_namespace, {"price", "hash", "int", IndexOpts()}); + ASSERT_TRUE(err.ok()) << err.what(); + err = rt.reindexer->AddIndex(default_namespace, {"genreid_fk", "hash", "int", IndexOpts()}); + ASSERT_TRUE(err.ok()) << err.what(); + err = rt.reindexer->AddIndex(default_namespace, {"authorid_fk", "hash", "int", IndexOpts()}); + ASSERT_TRUE(err.ok()) << err.what(); + err = rt.reindexer->Commit(default_namespace); + ASSERT_TRUE(err.ok()) << err.what(); } void prepareItems() { @@ -48,7 +53,8 @@ class ItemMoveSemanticsApi : public ReindexerApi { ASSERT_TRUE(err.ok()) << err.what(); ASSERT_NO_THROW(gason::JsonParser().Parse(item.GetJSON())); } - rt.reindexer->Commit(default_namespace); + const auto err = rt.reindexer->Commit(default_namespace); + ASSERT_TRUE(err.ok()) << err.what(); } Item getItemById(int id) { diff --git a/cpp_src/gtests/tests/fixtures/join_on_conditions_api.h b/cpp_src/gtests/tests/fixtures/join_on_conditions_api.h index 6326c8faf..5173b168d 100644 --- a/cpp_src/gtests/tests/fixtures/join_on_conditions_api.h +++ b/cpp_src/gtests/tests/fixtures/join_on_conditions_api.h @@ -4,7 +4,7 @@ class JoinOnConditionsApi : public JoinSelectsApi { public: - void SetUp() override { JoinSelectsApi::Init("/tmp/join_on_conditions_test/"); } + void SetUp() override { JoinSelectsApi::Init(reindexer::fs::JoinPath(reindexer::fs::GetTempDir(), "join_on_conditions_test")); } void CreateCondSetTable(const std::string& leftNs, const std::string& rightNs, const std::vector& leftNsData, const std::vector>& rightNsData) { @@ -23,7 +23,8 @@ class JoinOnConditionsApi : public JoinSelectsApi { builder.End(); err = item.FromJSON(ser.c_str()); ASSERT_TRUE(err.ok()) << err.what(); - rt.reindexer->Insert(leftNs, item); + err = rt.reindexer->Insert(leftNs, item); + ASSERT_TRUE(err.ok()) << err.what(); } for (unsigned int i = 0; i < rightNsData.size(); i++) { @@ -40,7 +41,8 @@ class JoinOnConditionsApi : public JoinSelectsApi { builder.End(); err = item.FromJSON(ser.c_str()); ASSERT_TRUE(err.ok()) << err.what(); - rt.reindexer->Insert(rightNs, item); + err = rt.reindexer->Insert(rightNs, item); + ASSERT_TRUE(err.ok()) << err.what(); } } diff --git a/cpp_src/gtests/tests/fixtures/join_selects_api.h b/cpp_src/gtests/tests/fixtures/join_selects_api.h index 8bcf9b644..c4ff5b4e2 100644 --- a/cpp_src/gtests/tests/fixtures/join_selects_api.h +++ b/cpp_src/gtests/tests/fixtures/join_selects_api.h @@ -19,7 +19,7 @@ class JoinSelectsApi : public ReindexerApi { using QueryResultRow = std::map; using QueryResultRows = std::map; - void Init(const std::string& dbName = "/tmp/join_test/") { + void Init(const std::string& dbName = reindexer::fs::JoinPath(reindexer::fs::GetTempDir(), "join_test")) { Error err; reindexer::fs::RmDirAll(dbName); @@ -236,7 +236,7 @@ class JoinSelectsApi : public ReindexerApi { auto joinedFieldIt = itemIt.begin(); LocalQueryResults jres = joinedFieldIt.ToQueryResults(); auto& lqr = qr.ToLocalQr(); - jres.addNSContext(lqr.getPayloadType(1), lqr.getTagsMatcher(1), lqr.getFieldsFilter(1), lqr.getSchema(1)); + jres.addNSContext(lqr.getPayloadType(1), lqr.getTagsMatcher(1), lqr.getFieldsFilter(1), lqr.getSchema(1), reindexer::lsn_t()); for (auto it : jres) { Item joinedItem = it.GetItem(false); FillQueryResultFromItem(joinedItem, resultRow); @@ -400,8 +400,7 @@ class JoinSelectsApi : public ReindexerApi { } { QueryResults qr; - Query q; - q.FromSQL(sql); + Query q = Query::FromSQL(sql); auto err = rt.reindexer->Select(q, qr); EXPECT_EQ(err.code(), expectedCode) << sql; EXPECT_EQ(err.what(), expectedText) << sql; diff --git a/cpp_src/gtests/tests/fixtures/msgpack_cproto_api.h b/cpp_src/gtests/tests/fixtures/msgpack_cproto_api.h index 999221916..17be69207 100644 --- a/cpp_src/gtests/tests/fixtures/msgpack_cproto_api.h +++ b/cpp_src/gtests/tests/fixtures/msgpack_cproto_api.h @@ -24,7 +24,7 @@ class MsgPackCprotoApi : public ReindexerApi { using reindexer::client::RPCDataFormat; reindexer::fs::RmDirAll(kDbPath); YAML::Node y; - y["storage"]["path"] = "/tmp/reindex/" + kDbPath; + y["storage"]["path"] = kDbPath; y["logger"]["loglevel"] = "none"; y["logger"]["rpclog"] = "none"; y["logger"]["serverlog"] = "none"; @@ -44,7 +44,7 @@ class MsgPackCprotoApi : public ReindexerApi { } client_.reset(new reindexer::client::RPCTestClient()); - err = client_->Connect("cproto://127.0.0.1:25677/" + kDbPath, reindexer::client::ConnectOpts().CreateDBIfMissing()); + err = client_->Connect("cproto://127.0.0.1:25677/" + kDbName, reindexer::client::ConnectOpts().CreateDBIfMissing()); ASSERT_TRUE(err.ok()) << err.what(); err = client_->OpenNamespace(default_namespace, StorageOpts().CreateIfMissing()); @@ -86,11 +86,12 @@ class MsgPackCprotoApi : public ReindexerApi { void TearDown() { if (server_.IsRunning()) { server_.Stop(); - if (serverThread_->joinable()) { - serverThread_->join(); - } } - client_->Stop(); + client_.reset(); + if (serverThread_->joinable()) { + serverThread_->join(); + } + serverThread_.reset(); } void checkItem(reindexer::client::QueryResults::Iterator& it) { @@ -110,7 +111,8 @@ class MsgPackCprotoApi : public ReindexerApi { } protected: - const std::string kDbPath = "cproto_msgpack_test"; + const std::string kDbName = "cproto_msgpack_test"; + const std::string kDbPath = reindexer::fs::JoinPath(reindexer::fs::GetTempDir(), "reindex/" + kDbName); const std::string kFieldId = "id"; const std::string kFieldA1 = "a1"; const std::string kFieldA2 = "a2"; diff --git a/cpp_src/gtests/tests/fixtures/queries_api.cc b/cpp_src/gtests/tests/fixtures/queries_api.cc index 90ed9ab18..f429d4ab0 100644 --- a/cpp_src/gtests/tests/fixtures/queries_api.cc +++ b/cpp_src/gtests/tests/fixtures/queries_api.cc @@ -115,13 +115,11 @@ void QueriesApi::CheckMergeQueriesWithAggregation() { double c4; AggSelect(Query{testSimpleNs}.Aggregate(AggCount, {}), AggCount, c4); double c3; - Query q3; - q3.FromSQL(fmt::sprintf("SELECT count(*) FROM %s MERGE (SELECT * FROM %s)", default_namespace, joinNs)); + Query q3 = Query::FromSQL(fmt::sprintf("SELECT count(*) FROM %s MERGE (SELECT * FROM %s)", default_namespace, joinNs)); AggSelect(q3, AggCount, c3); double c5; - Query q5; - q5.FromSQL(fmt::sprintf("SELECT count(*) FROM %s MERGE (SELECT * FROM %s) MERGE (SELECT * FROM %s)", default_namespace, joinNs, - testSimpleNs)); + Query q5 = Query::FromSQL(fmt::sprintf("SELECT count(*) FROM %s MERGE (SELECT * FROM %s) MERGE (SELECT * FROM %s)", + default_namespace, joinNs, testSimpleNs)); AggSelect(q5, AggCount, c5); { QueryResults qr; @@ -214,13 +212,13 @@ void QueriesApi::CheckMergeQueriesWithAggregation() { QueryResults qr; Error err = rt.reindexer->Select(Query{default_namespace}.Merge(Query{joinNs}.ReqTotal()), qr); EXPECT_FALSE(err.ok()); - EXPECT_EQ(err.what(), "Aggregations in inner merge query is not allowed"); + EXPECT_EQ(err.what(), "Aggregations in inner merge query are not allowed"); } { QueryResults qr; Error err = rt.reindexer->Select(Query{default_namespace}.Merge(Query{joinNs}.CachedTotal()), qr); EXPECT_FALSE(err.ok()); - EXPECT_EQ(err.what(), "Aggregations in inner merge query is not allowed"); + EXPECT_EQ(err.what(), "Aggregations in inner merge query are not allowed"); } // checking the work of several aggregate functions with the merge query { @@ -284,6 +282,7 @@ void QueriesApi::initConditionsNs() { ASSERT_TRUE(err.ok()) << err.what(); err = rt.reindexer->AddIndex(conditionsNs, {kFieldNameId, "hash", "int", IndexOpts{}.PK()}); ASSERT_TRUE(err.ok()) << err.what(); + addIndexFields(conditionsNs, kFieldNameId, {{kFieldNameId, reindexer::KeyValueType::Int{}}}); for (const auto& fit : fieldIndexTypes) { for (const auto& it : fit.indexTypes) { for (const bool isArray : {true, false}) { @@ -294,8 +293,8 @@ void QueriesApi::initConditionsNs() { const std::string fieldType{fit.fieldType.Name()}; const std::string indexName{createIndexName(fieldType, it, isArray, isSparse)}; err = rt.reindexer->AddIndex(conditionsNs, {indexName, it, fieldType, IndexOpts{}.Array(isArray).Sparse(isSparse)}); - addIndexFields(conditionsNs, indexName, {{indexName, fit.fieldType}}); ASSERT_TRUE(err.ok()) << err.what(); + addIndexFields(conditionsNs, indexName, {{indexName, fit.fieldType}}); } } } @@ -303,6 +302,27 @@ void QueriesApi::initConditionsNs() { setPkFields(conditionsNs, {kFieldNameId}); } +void QueriesApi::initUUIDNs() { + const auto err = rt.reindexer->OpenNamespace(uuidNs); + ASSERT_TRUE(err.ok()) << err.what(); + DefineNamespaceDataset( + uuidNs, + { + IndexDeclaration{kFieldNameId, "hash", "int", IndexOpts{}.PK(), 0}, + IndexDeclaration{kFieldNameUuid, "hash", "uuid", IndexOpts{}, 0}, + /*IndexDeclaration{kFieldNameUuidSparse, "hash", "uuid", IndexOpts{}.Sparse(), 0}, // TODO uncomment this #1470 + IndexDeclaration{kFieldNameUuidNotIndex2, "hash", "uuid", IndexOpts{}, 0}, + IndexDeclaration{kFieldNameUuidNotIndex3, "hash", "uuid", IndexOpts{}.Sparse(), 0},*/ + IndexDeclaration{kFieldNameUuidArr, "hash", "uuid", IndexOpts{}.Array(), 0}, + // IndexDeclaration{kFieldNameUuidArrSparse, "hash", "uuid", IndexOpts{}.Array().Sparse(), 0} // TODO uncomment this #1470 + }); + for (const auto& idx : + {kFieldNameUuid, kFieldNameUuidArr /*, kFieldNameUuidSparse, kFieldNameUuidArrSparse*/}) { // TODO uncomment this #1470 + addIndexFields(uuidNs, idx, {{idx, reindexer::KeyValueType::Uuid{}}}); + } + setPkFields(uuidNs, {kFieldNameId}); +} + static reindexer::Variant createRandValue(int id, reindexer::KeyValueType fieldType) { using namespace reindexer; return fieldType.EvaluateOneOf(overloaded{ @@ -379,41 +399,19 @@ static reindexer::VariantArray createRandArrValues(size_t min, size_t max, int i } void QueriesApi::checkAllConditions(const std::string& fieldName, reindexer::KeyValueType fieldType, NullAllowed nullAllowed) { - for (const auto cond : {CondEq, CondSet, CondAllSet, CondLt, CondLe, CondGt, CondGe, CondRange, CondAny, CondEmpty, CondLike}) { - size_t min = 0, max = rand() % kMaxArraySize; - switch (cond) { - case CondEq: - case CondSet: - case CondAllSet: - break; - case CondLike: - if (!fieldType.Is()) { - continue; - } - [[fallthrough]]; - case CondLt: - case CondLe: - case CondGt: - case CondGe: - min = max = 1; - break; - case CondRange: - min = max = 2; - break; - case CondAny: - case CondEmpty: - if (nullAllowed == NullAllowed::No) { - continue; - } - min = max = 0; - break; - case CondDWithin: // TODO #1352 - assert(0); + for (const auto cond : {CondEq, CondSet, CondAllSet, CondLt, CondLe, CondGt, CondGe, CondRange, CondAny, CondEmpty, + CondLike}) { // TODO CondDWithin #1352 + if (cond == CondLike && !fieldType.Is()) { + continue; } + if (nullAllowed == NullAllowed::No && (cond == CondAny || cond == CondEmpty)) { + continue; + } + const auto argsCount = minMaxArgs(cond, 20); for (size_t i = 0; i < 3; ++i) { - ExecuteAndVerify( - reindexer::Query{conditionsNs}.Where(fieldName, cond, createRandArrValues(min, max, rand() % conditionsNsSize, fieldType))); - if (min <= 1 && max >= 1) { + ExecuteAndVerify(reindexer::Query{conditionsNs}.Where( + fieldName, cond, createRandArrValues(argsCount.min, argsCount.max, rand() % conditionsNsSize, fieldType))); + if (argsCount.min <= 1 && argsCount.max >= 1) { ExecuteAndVerify( reindexer::Query{conditionsNs}.Where(fieldName, cond, createRandValue(rand() % conditionsNsSize, fieldType))); } @@ -442,3 +440,1179 @@ void QueriesApi::CheckConditions() { checkAllConditions(fieldType + "_array", fit.fieldType, NullAllowed::Yes); } } + +void QueriesApi::FillUUIDNs() { + static size_t lastId = 0; + reindexer::WrSerializer ser; + for (size_t i = lastId; i < uuidNsSize + lastId; ++i) { + Item item = rt.reindexer->NewItem(uuidNs); + ASSERT_TRUE(item.Status().ok()) << item.Status().what(); + if (rand() % 2) { + ser.Reset(); + { + reindexer::JsonBuilder json{ser}; + json.Put(kFieldNameId, i); + json.Put(kFieldNameUuid, randStrUuid()); + /*if (rand() % 2) { + json.Put(kFieldNameUuidSparse, randStrUuid()); // TODO uncomment this #1470 + }*/ + { + auto arr = json.Array(kFieldNameUuidArr); + for (size_t j = 0, s = rand() % 10; j < s; ++j) { + arr.Put({}, randStrUuid()); + } + } + /*if (rand() % 2) { + auto arr = json.Array(kFieldNameUuidArrSparse); // TODO uncomment this #1470 + for (size_t j = 0, s = rand() % 10; j < s; ++j) { + arr.Put({}, randStrUuid()); + } + }*/ + if (rand() % 2) { + json.Put(kFieldNameUuidNotIndex, randStrUuid()); + } + /*json.Put(kFieldNameUuidNotIndex2, randStrUuid()); // TODO uncomment this #1470 + if (rand() % 2) { + json.Put(kFieldNameUuidNotIndex3, randStrUuid()); + }*/ + if (rand() % 2) { + json.Put(kFieldNameRndString, RandString()); + } + } + const auto err = item.FromJSON(ser.Slice()); + ASSERT_TRUE(err.ok()) << err.what(); + } else { + item[kFieldNameId] = int(i); + if (rand() % 2) { + item[kFieldNameUuid] = randUuid(); + } else { + item[kFieldNameUuid] = randStrUuid(); + } + /*if (rand() % 2) { + item[kFieldNameUuidSparse] = randUuid(); // TODO uncomment this #1470 + }*/ + item[kFieldNameUuidArr] = randHeterogeneousUuidArray(0, 20); + /*if (rand() % 2) { + item[kFieldNameUuidArrSparse] = randHeterogeneousUuidArray(0, 20); // TODO uncomment this #1470 + } + if (rand() % 2) { + item[kFieldNameUuidNotIndex2] = randUuid(); + } else { + item[kFieldNameUuidNotIndex2] = randStrUuid(); + } + if (rand() % 2) { + if (rand() % 2) { + item[kFieldNameUuidNotIndex3] = randUuid(); + } else { + item[kFieldNameUuidNotIndex3] = randStrUuid(); + } + }*/ + } + ASSERT_TRUE(item.Status().ok()) << item.Status().what(); + Upsert(uuidNs, item); + saveItem(std::move(item), uuidNs); + } + const auto err = Commit(uuidNs); + ASSERT_TRUE(err.ok()) << err.what(); + lastId += uuidNsSize; +} + +void QueriesApi::CheckUUIDQueries() { + for (size_t i = 0; i < 10; ++i) { + for (const auto& field : { + kFieldNameUuid, kFieldNameUuidArr, kFieldNameUuidNotIndex, kFieldNameRndString /*, + kFieldNameUuidSparse, kFieldNameUuidArrSparse, kFieldNameUuidNotIndex2, kFieldNameUuidNotIndex3*/ + }) { // TODO uncomment this #1470 + for (auto cond : {CondEq, CondLe, CondLt, CondSet, CondGe, CondGt, CondAllSet, CondRange}) { + const auto argsCount = minMaxArgs(cond, 20); + if (argsCount.min <= 1 && argsCount.max >= 1) { + ExecuteAndVerify(Query(uuidNs).Where(field, cond, randUuid())); + ExecuteAndVerify(Query(uuidNs).Where(field, cond, randStrUuid())); + } + ExecuteAndVerify(Query(uuidNs).Where(field, cond, randUuidArray(argsCount.min, argsCount.max))); + ExecuteAndVerify(Query(uuidNs).Where(field, cond, randStrUuidArray(argsCount.min, argsCount.max))); + ExecuteAndVerify(Query(uuidNs).Where(field, cond, randHeterogeneousUuidArray(argsCount.min, argsCount.max))); + } + } + } +} + +void QueriesApi::checkSqlQuery(std::string_view sqlQuery, Query&& checkQuery) { + QueryResults sqlQr; + Error err = rt.reindexer->Select(sqlQuery, sqlQr); + ASSERT_TRUE(err.ok()) << err.what(); + + QueryResults checkQr; + err = rt.reindexer->Select(checkQuery, checkQr); + ASSERT_TRUE(err.ok()) << err.what(); + + CompareQueryResults(sqlQuery, sqlQr, checkQr); + Verify(checkQr, std::move(checkQuery), *rt.reindexer); +} + +void QueriesApi::CheckSqlQueries() { + using namespace std::string_literals; + using namespace std::string_view_literals; + using reindexer::randPoint; + using reindexer::randBinDouble; + + checkSqlQuery("SELECT ID, Year, Genre FROM test_namespace WHERE year > '2016' ORDER BY year DESC LIMIT 10000000"sv, + Query(default_namespace, 0, 10000000).Where(kFieldNameYear, CondGt, 2016).Sort(kFieldNameYear, true)); + + checkSqlQuery("SELECT ID, Year, Genre FROM test_namespace WHERE genre IN ('1',2,'3') ORDER BY year DESC LIMIT 10000000"sv, + Query(default_namespace, 0, 10000000).Where(kFieldNameGenre, CondSet, {1, 2, 3}).Sort(kFieldNameYear, true)); + + const std::string likePattern = RandLikePattern(); + checkSqlQuery("SELECT ID, Year, Genre FROM test_namespace WHERE name LIKE '"s + likePattern + "' ORDER BY year DESC LIMIT 10000000"s, + Query(default_namespace, 0, 10000000).Where(kFieldNameName, CondLike, likePattern).Sort(kFieldNameYear, true)); + + checkSqlQuery("SELECT FACET(ID, Year ORDER BY ID DESC ORDER BY Year ASC LIMIT 20 OFFSET 1) FROM test_namespace LIMIT 10000000"sv, + Query(default_namespace, 0, 10000000) + .Aggregate(AggFacet, {kFieldNameId, kFieldNameYear}, {{kFieldNameId, true}, {kFieldNameYear, false}}, 20, 1)); + + checkSqlQuery("SELECT ID FROM test_namespace WHERE name LIKE '"s + likePattern + + "' AND (genre IN ('1', '2', '3') AND year > '2016' ) OR age IN ('1', '2', '3', '4') LIMIT 10000000"s, + Query(default_namespace, 0, 10000000) + .Where(kFieldNameName, CondLike, likePattern) + .OpenBracket() + .Where(kFieldNameGenre, CondSet, {1, 2, 3}) + .Where(kFieldNameYear, CondGt, 2016) + .CloseBracket() + .Or() + .Where(kFieldNameAge, CondSet, {1, 2, 3, 4})); + + checkSqlQuery(fmt::sprintf("SELECT ID FROM test_namespace ORDER BY '%s + %s * 5' DESC LIMIT 10000000", kFieldNameYear, kFieldNameId), + Query(default_namespace, 0, 10000000).Sort(kFieldNameYear + std::string(" + ") + kFieldNameId + " * 5", true)); + + checkSqlQuery(fmt::sprintf("SELECT ID FROM test_namespace ORDER BY '%s + %s * 5' DESC ORDER BY '2 * %s / (1 + %s)' ASC LIMIT 10000000", + kFieldNameYear, kFieldNameId, kFieldNameGenre, kFieldNameIsDeleted), + Query(default_namespace, 0, 10000000) + .Sort(kFieldNameYear + std::string(" + ") + kFieldNameId + " * 5", true) + .Sort(std::string("2 * ") + kFieldNameGenre + " / (1 + " + kFieldNameIsDeleted + ')', false)); + + // Checks that SQL queries with DWithin and sort by Distance work and compares the result with the result of corresponding C++ query + reindexer::Point point = randPoint(10); + double distance = randBinDouble(0, 1); + checkSqlQuery(fmt::sprintf("SELECT * FROM %s WHERE ST_DWithin(%s, %s, %s);", geomNs, kFieldNamePointNonIndex, pointToSQL(point), + toString(distance)), + Query(geomNs).DWithin(kFieldNamePointNonIndex, point, distance)); + + point = randPoint(10); + distance = randBinDouble(0, 1); + checkSqlQuery(fmt::sprintf("SELECT * FROM %s WHERE ST_DWithin(%s, %s, %s) ORDER BY 'ST_Distance(%s, %s)';", geomNs, pointToSQL(point), + kFieldNamePointNonIndex, toString(distance), kFieldNamePointLinearRTree, pointToSQL(point, true)), + Query(geomNs) + .DWithin(kFieldNamePointNonIndex, point, distance) + .Sort(std::string("ST_Distance(") + kFieldNamePointLinearRTree + ", " + pointToSQL(point) + ')', false)); + + checkSqlQuery(fmt::sprintf("SELECT * FROM %s WHERE %s >= %s;", default_namespace, kFieldNameGenre, kFieldNameRate), + Query(default_namespace).WhereBetweenFields(kFieldNameGenre, CondGe, kFieldNameRate)); +} + +void QueriesApi::checkDslQuery(std::string_view dslQuery, Query&& checkQuery) { + Query parsedQuery; + Error err = parsedQuery.FromJSON(dslQuery); + ASSERT_TRUE(err.ok()) << "Query: " << dslQuery << "; err: " << err.what(); + + QueryResults dslQr; + err = rt.reindexer->Select(parsedQuery, dslQr); + ASSERT_TRUE(err.ok()) << "Query: " << dslQuery << "; err: " << err.what(); + + QueryResults checkQr; + err = rt.reindexer->Select(checkQuery, checkQr); + ASSERT_TRUE(err.ok()) << "Query: " << dslQuery << "; err: " << err.what(); + + CompareQueryResults(dslQuery, dslQr, checkQr); + Verify(checkQr, std::move(checkQuery), *rt.reindexer); +} + +// Checks that DSL queries works and compares the result with the result of corresponding C++ query +void QueriesApi::CheckDslQueries() { + using namespace std::string_literals; + using reindexer::randPoint; + using reindexer::randBinDouble; + using reindexer::double_to_str; + + auto point{randPoint(10)}; + auto distance = randBinDouble(0, 1); + checkDslQuery( + fmt::sprintf( + R"({"namespace":"%s","limit":-1,"offset":0,"req_total":"disabled","explain":false,"type":"select","select_with_rank":false,"select_filter":[],"select_functions":[],"sort":[],"filters":[{"op":"and","cond":"dwithin","field":"%s","value":[[%s, %s], %s]}],"merge_queries":[],"aggregations":[]})", + geomNs, kFieldNamePointLinearRTree, double_to_str(point.X()), double_to_str(point.Y()), double_to_str(distance)), + Query(geomNs).DWithin(kFieldNamePointLinearRTree, point, distance)); + + point = randPoint(10); + distance = randBinDouble(0, 1); + checkDslQuery( + fmt::sprintf( + R"({"namespace":"%s","limit":-1,"offset":0,"req_total":"disabled","explain":false,"type":"select","select_with_rank":false,"select_filter":[],"select_functions":[],"sort":[],"filters":[{"op":"and","cond":"dwithin","field":"%s","value":[%s,[%s,%s]]}],"merge_queries":[],"aggregations":[]})", + geomNs, kFieldNamePointLinearRTree, double_to_str(distance), double_to_str(point.X()), double_to_str(point.Y())), + Query(geomNs).DWithin(kFieldNamePointLinearRTree, point, distance)); + + checkDslQuery( + fmt::sprintf( + R"({"namespace":"%s","limit":-1,"offset":0,"req_total":"disabled","explain":false,"type":"select","select_with_rank":false,"select_filter":[],"select_functions":[],"sort":[],"filters":[{"op":"and","cond":"gt","first_field":"%s","second_field":"%s"}],"merge_queries":[],"aggregations":[]})", + default_namespace, kFieldNameStartTime, kFieldNamePackages), + Query{default_namespace}.WhereBetweenFields(kFieldNameStartTime, CondGt, kFieldNamePackages)); + + checkDslQuery( + fmt::sprintf( + R"({"namespace":"%s","limit":-1,"offset":0,"req_total":"disabled","explain":false,"type":"select","select_with_rank":false,"select_filter":[],"select_functions":[],"sort":[],"filters":[{"op":"and","cond":"SET","field":"%s","Value":["1", " 10 ", "100 ", " 1000"]}],"merge_queries":[],"aggregations":[]})", + default_namespace, kFieldNameId), + Query{default_namespace}.Where(kFieldNameId, CondSet, {1, 10, 100, 1000})); +} + +void QueriesApi::CheckStandartQueries() { + try { + using namespace std::string_literals; + static const std::vector sortIdxs = { + "", + kFieldNameName, + kFieldNameYear, + kFieldNameRate, + kFieldNameBtreeIdsets, + std::string{"-2.5 * "} + kFieldNameRate + " / (" + kFieldNameYear + " + " + kFieldNameId + ')'}; + static const std::vector distincts = {"", kFieldNameYear, kFieldNameRate}; + static const std::vector sortOrders = {true, false}; + + for ([[maybe_unused]] const bool sortOrder : sortOrders) { + for ([[maybe_unused]] const auto& sortIdx : sortIdxs) { + for ([[maybe_unused]] const std::string& distinct : distincts) { + [[maybe_unused]] const int randomAge = rand() % 50; + [[maybe_unused]] const int randomGenre = rand() % 50; + [[maybe_unused]] const int randomGenreUpper = rand() % 100; + [[maybe_unused]] const int randomGenreLower = rand() % 100; + + ExecuteAndVerify(Query(default_namespace).Distinct(distinct.c_str()).Sort(sortIdx, sortOrder).Limit(1)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameGenre, CondEq, randomGenre) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameGenre, CondEq, std::to_string(randomGenre)) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameName, CondEq, RandString()) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameRate, CondEq, (rand() % 100) / 10.0) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameRate, CondEq, std::to_string((rand() % 100) / 10.0)) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameGenre, CondGt, randomGenre) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder) + .Debug(LogTrace)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameName, CondGt, RandString()) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameRate, CondGt, (rand() % 100) / 10.0) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameGenre, CondLt, randomGenre) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameGenre, CondLt, std::to_string(randomGenre)) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameName, CondLt, RandString()) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameRate, CondLt, (rand() % 100) / 10.0) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameRate, CondLt, std::to_string((rand() % 100) / 10.0)) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameBtreeIdsets, CondLt, static_cast(rand() % 10000)) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameBtreeIdsets, CondGt, static_cast(rand() % 10000)) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameBtreeIdsets, CondEq, static_cast(rand() % 10000)) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameGenre, CondRange, {randomGenreLower, randomGenreUpper}) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder) + .Debug(LogTrace)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameName, CondRange, {RandString(), RandString()}) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameName, CondLike, RandLikePattern()) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameRate, CondRange, + {static_cast(rand() % 100) / 10, static_cast(rand() % 100) / 10}) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNamePackages, CondSet, RandIntVector(10, 10000, 50)) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNamePackages, CondAllSet, RandIntVector(2, 10000, 50)) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNamePackages, CondAllSet, 10000 + rand() % 50) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + // check substituteCompositIndexes + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameAge, CondEq, randomAge) + .Where(kFieldNameGenre, CondEq, randomGenre) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameAge, CondSet, RandIntVector(10, 0, 50)) + .Where(kFieldNameGenre, CondEq, randomGenre) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameAge, CondAllSet, RandIntVector(1, 0, 50)) + .Where(kFieldNameGenre, CondEq, randomGenre) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameAge, CondSet, RandIntVector(10, 0, 50)) + .Where(kFieldNameGenre, CondSet, RandIntVector(10, 0, 50)) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameAge, CondSet, RandIntVector(10, 0, 20)) + .Where(kFieldNameGenre, CondSet, RandIntVector(10, 0, 50)) + .Where(kFieldNameAge, CondSet, RandIntVector(10, 30, 50)) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameAge, CondSet, RandIntVector(10, 0, 20)) + .Where(kFieldNameGenre, CondEq, randomGenre) + .Where(kFieldNameAge, CondSet, RandIntVector(10, 30, 50)) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + // end of check substituteCompositIndexes + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNamePackages, CondEmpty, VariantArray{}) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameName, CondRange, {RandString(), RandString()}) + .Distinct(distinct.c_str()) + .Sort(kFieldNameYear, true) + .Sort(kFieldNameName, false) + .Sort(kFieldNameLocation, true)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameName, CondRange, {RandString(), RandString()}) + .Distinct(distinct.c_str()) + .Sort(kFieldNameGenre, true) + .Sort(kFieldNameActor, false) + .Sort(kFieldNameRate, true) + .Sort(kFieldNameLocation, false)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameName, CondLike, RandLikePattern()) + .Distinct(distinct.c_str()) + .Sort(kFieldNameGenre, true) + .Sort(kFieldNameActor, false) + .Sort(kFieldNameRate, true) + .Sort(kFieldNameLocation, false)); + + ExecuteAndVerify(Query(default_namespace).Sort(kFieldNameGenre, true, {10, 20, 30})); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNamePackages, CondAny, VariantArray{}) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify( + Query(default_namespace).Where(kFieldNameIsDeleted, CondEq, 1).Distinct(distinct.c_str()).Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder) + .Where(kFieldNameGenre, CondEq, 5) + .Where(kFieldNameAge, CondEq, 3) + .Where(kFieldNameYear, CondGe, 2010) + .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) + .Debug(LogTrace)); + + ExecuteAndVerify(Query(default_namespace) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder) + .Debug(LogTrace) + .Where(kFieldNameGenre, CondEq, 5) + .Where(kFieldNameAge, CondEq, 3) + .Where(kFieldNameGenre, CondEq, 5) + .Where(kFieldNameAge, CondEq, 3) + .OpenBracket() + .Where(kFieldNameYear, CondGe, 2010) + .CloseBracket() + .Or() + .Where(kFieldNameYear, CondGe, 2010)); + + ExecuteAndVerify(Query(default_namespace) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder) + .Debug(LogTrace) + .Where(kFieldNameYear, CondGt, 2002) + .Where(kFieldNameGenre, CondEq, 4) + .Where(kFieldNameAge, CondEq, 3) + .Where(kFieldNameIsDeleted, CondEq, 3) + .Or() + .Where(kFieldNameYear, CondGt, 2001) + .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) + .Debug(LogTrace)); + + ExecuteAndVerify(Query(default_namespace) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder) + .Debug(LogTrace) + .Where(kFieldNameAge, CondSet, {1, 2, 3, 4}) + .Where(kFieldNameId, CondEq, rand() % 5000) + .Where(kFieldNameTemp, CondEq, "") + .Where(kFieldNameIsDeleted, CondEq, 1) + .Or() + .Where(kFieldNameYear, CondGt, 2001) + .Debug(LogTrace)); + + ExecuteAndVerify(Query(default_namespace) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder) + .Debug(LogTrace) + .Where(kFieldNameGenre, CondSet, {5, 1, 7}) + .Where(kFieldNameYear, CondLt, 2010) + .Where(kFieldNameGenre, CondEq, 3) + .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) + .Or() + .Where(kFieldNamePackages, CondEmpty, VariantArray{}) + .Debug(LogTrace)); + + ExecuteAndVerify(Query(default_namespace) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder) + .Debug(LogTrace) + .Where(kFieldNameGenre, CondSet, {5, 1, 7}) + .Where(kFieldNameYear, CondLt, 2010) + .Or() + .Where(kFieldNamePackages, CondAny, VariantArray{}) + .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) + .Debug(LogTrace)); + + ExecuteAndVerify(Query(default_namespace) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder) + .Debug(LogTrace) + .Where(kFieldNameGenre, CondEq, 5) + .Or() + .Where(kFieldNameGenre, CondEq, 6) + .Where(kFieldNameYear, CondRange, {2001, 2020}) + .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50))); + + ExecuteAndVerify(Query(default_namespace) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder) + .Debug(LogTrace) + .Where(kFieldNameGenre, CondEq, 5) + .Or() + .Where(kFieldNameGenre, CondEq, 6) + .Not() + .Where(kFieldNameName, CondLike, RandLikePattern()) + .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50))); + + ExecuteAndVerify(Query(default_namespace) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder) + .Debug(LogTrace) + .Where(kFieldNameActor, CondEq, RandString())); + + ExecuteAndVerify(Query(default_namespace) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder) + .Debug(LogTrace) + .Not() + .Where(kFieldNameGenre, CondEq, 5) + .Where(kFieldNameYear, CondRange, {2001, 2020}) + .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50))); + + ExecuteAndVerify(Query(default_namespace) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder) + .Debug(LogTrace) + .Where(kFieldNameGenre, CondEq, 5) + .Not() + .Where(kFieldNameYear, CondRange, {2001, 2020}) + .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50))); + + ExecuteAndVerify(Query(default_namespace) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder) + .Debug(LogTrace) + .Not() + .Where(kFieldNameYear, CondEq, 10)); + + ExecuteAndVerify(Query(default_namespace) + .Distinct(distinct.c_str()) + .Sort(kFieldNameNumeric, sortOrder) + .Debug(LogTrace) + .Where(kFieldNameNumeric, CondGt, std::to_string(5))); + + ExecuteAndVerify(Query(default_namespace) + .Distinct(distinct.c_str()) + .Sort(kFieldNameNumeric, sortOrder) + .Debug(LogTrace) + .Where(kFieldNameNumeric, CondLt, std::to_string(600))); + + ExecuteAndVerify(Query(default_namespace) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder) + .Debug(LogTrace) + .Where(kFieldNameGenre, CondEq, 5) + .Or() + .OpenBracket() + .Where(kFieldNameGenre, CondLt, 6) + .Where(kFieldNameYear, CondRange, {2001, 2020}) + .CloseBracket() + .Not() + .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) + .OpenBracket() + .Where(kFieldNameNumeric, CondLt, std::to_string(600)) + .Or() + .OpenBracket() + .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) + .Where(kFieldNameName, CondLike, RandLikePattern()) + .CloseBracket() + .Or() + .Where(kFieldNameYear, CondEq, 10) + .CloseBracket()); + + ExecuteAndVerify(Query(default_namespace) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder) + .Debug(LogTrace) + .Where(kFieldNameGenre, CondEq, 5) + .Not() + .OpenBracket() + .Where(kFieldNameYear, CondRange, {2001, 2020}) + .Or() + .Where(kFieldNameName, CondLike, RandLikePattern()) + .CloseBracket() + .Or() + .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) + .OpenBracket() + .Where(kFieldNameNumeric, CondLt, std::to_string(600)) + .Not() + .OpenBracket() + .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) + .Where(kFieldNameGenre, CondLt, 6) + .CloseBracket() + .Or() + .Where(kFieldNameYear, CondEq, 10) + .CloseBracket()); + + ExecuteAndVerify( + Query(default_namespace) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder) + .Debug(LogTrace) + .Where(kFieldNameNumeric, CondRange, {std::to_string(rand() % 100), std::to_string(rand() % 100 + 500)})); + + ExecuteAndVerify(Query(testSimpleNs).Where(kFieldNameName, CondEq, "SSS")); + ExecuteAndVerify(Query(testSimpleNs).Where(kFieldNameYear, CondEq, 2002)); + ExecuteAndVerify(Query(testSimpleNs).Where(kFieldNameYear, CondEq, 2002).Not().Where(kFieldNameName, CondEq, 2002)); + ExecuteAndVerify(Query(testSimpleNs).Where(kFieldNameName, CondEq, "SSS").Not().Where(kFieldNameYear, CondEq, 2002)); + ExecuteAndVerify(Query(testSimpleNs).Where(kFieldNameName, CondEq, "SSS").Not().Where(kFieldNameYear, CondEq, 1989)); + ExecuteAndVerify(Query(testSimpleNs).Where(kFieldNameYear, CondEq, 2002).Not().Where(kFieldNameName, CondEq, "MMM")); + + ExecuteAndVerify(Query(default_namespace) + .ReqTotal() + .Distinct(distinct) + .Sort(sortIdx, sortOrder) + .WhereComposite(kCompositeFieldAgeGenre, CondLe, {{Variant(27), Variant(10000)}})); + + ExecuteAndVerify(Query(default_namespace) + .ReqTotal() + .Distinct(distinct) + .Sort(kFieldNameAge + " + "s + kFieldNameId, sortOrder) + .Sort(kFieldNameRate + " * "s + kFieldNameGenre, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .ReqTotal() + .Distinct(distinct) + .Sort(sortIdx, sortOrder) + .WhereComposite(kCompositeFieldAgeGenre, CondEq, {{Variant(rand() % 10), Variant(rand() % 50)}})); + + ExecuteAndVerify(Query(default_namespace) + .InnerJoin(kFieldNameYear, kFieldNameYear, CondEq, Query(joinNs)) + .Distinct(distinct) + .Sort(joinNs + '.' + kFieldNameId, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .InnerJoin(kFieldNameYear, kFieldNameYear, CondEq, Query(joinNs)) + .Distinct(distinct) + .Sort(joinNs + '.' + kFieldNameName, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .InnerJoin(kFieldNameYear, kFieldNameYear, CondEq, Query(joinNs)) + .Distinct(distinct) + .Sort(joinNs + '.' + kFieldNameId + " * " + joinNs + '.' + kFieldNameGenre + + (sortIdx.empty() || (sortIdx == kFieldNameName) ? "" : (" + " + sortIdx)), + sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .InnerJoin(kFieldNameYear, kFieldNameYear, CondEq, + Query(joinNs) + .Where(kFieldNameId, CondSet, RandIntVector(20, 0, 100)) + .Sort(kFieldNameId + " + "s + kFieldNameYear, sortOrder)) + .Distinct(distinct)); + + ExecuteAndVerify( + Query(default_namespace) + .InnerJoin( + kFieldNameYear, kFieldNameYear, CondEq, + Query(joinNs).Where(kFieldNameYear, CondGe, 1925).Sort(kFieldNameId + " + "s + kFieldNameYear, sortOrder)) + .Distinct(distinct)); + ExecuteAndVerify(Query(default_namespace) + .InnerJoin(kFieldNameYear, kFieldNameYear, randCond(), + Query(joinNs).Where(kFieldNameYear, CondGe, 2000 + rand() % 210)) + .Distinct(distinct)); + ExecuteAndVerify(Query(default_namespace) + .InnerJoin(kFieldNameYear, kFieldNameYear, randCond(), + Query(joinNs).Where(kFieldNameYear, CondLe, 2000 + rand() % 210).Limit(rand() % 10)) + .Distinct(distinct)); + ExecuteAndVerify( + Query(default_namespace) + .Join(InnerJoin, Query(joinNs).Where(kFieldNameYear, CondGt, 2000 + rand() % 210) /*.Offset(rand() % 10)*/) + .On(kFieldNameYear, randCond(), kFieldNameYear) + .Distinct(distinct)); + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameYearSparse, CondEq, std::to_string(2000 + rand() % 60)) + .Distinct(distinct)); + ExecuteAndVerify( + Query(default_namespace) + .InnerJoin(kFieldNameYear, kFieldNameYear, randCond(), + Query(joinNs).Where(kFieldNameYear, CondLt, 2000 + rand() % 210).Sort(kFieldNameName, sortOrder)) + .Distinct(distinct)); + ExecuteAndVerify(Query(default_namespace) + .Join(InnerJoin, Query(joinNs).Where(kFieldNameYear, CondGt, 2000 + rand() % 210)) + .OpenBracket() + .On(kFieldNameYear, randCond(), kFieldNameYear) + .Or() + .On(kFieldNameName, CondEq, kFieldNameName) + .CloseBracket() + .Distinct(distinct)); + ExecuteAndVerify(Query(default_namespace) + .Join(InnerJoin, Query(joinNs).Where(kFieldNameYear, CondGt, 2000 + rand() % 210)) + .OpenBracket() + .On(kFieldNameYear, randCond(), kFieldNameYear) + .Or() + .On(kFieldNameName, CondEq, kFieldNameName) + .On(kFieldNameAge, randCond(), kFieldNameAge) + .Not() + .On(kFieldNameYear, randCond(), kFieldNameYear) + .CloseBracket() + .Distinct(distinct)); + ExecuteAndVerify(Query(default_namespace) + .Join(InnerJoin, Query(joinNs).Where(kFieldNameYear, CondGt, 2000 + rand() % 210)) + .OpenBracket() + .On(kFieldNameYearSparse, CondLe, kFieldNameYearSparse) + .Or() + .On(kFieldNameName, CondEq, kFieldNameName) + .Or() + .On(kFieldNameAge, randCond(), kFieldNameAge) + .Not() + .On(kFieldNameYear, randCond(), kFieldNameYear) + .CloseBracket() + .Distinct(distinct)); + ExecuteAndVerify( + Query(default_namespace) + .Join(InnerJoin, Query(joinNs).Where(kFieldNameYear, CondGt, 2000 + rand() % 210).Limit(rand() % 100)) + .OpenBracket() + .On(kFieldNameYear, randCond(), kFieldNameYear) + .Or() + .On(kFieldNameName, CondEq, kFieldNameName) + .Or() + .On(kFieldNameAge, randCond(), kFieldNameAge) + .CloseBracket() + .Or() + .Join(InnerJoin, Query(joinNs).Where(kFieldNameYear, CondLt, 2000 + rand() % 210).Limit(rand() % 100)) + .OpenBracket() + .On(kFieldNameYear, randCond(), kFieldNameYear) + .Or() + .On(kFieldNameName, CondEq, kFieldNameName) + .Or() + .On(kFieldNameAge, randCond(), kFieldNameAge) + .CloseBracket() + .Join(OrInnerJoin, Query(joinNs).Where(kFieldNameYear, CondEq, 2000 + rand() % 210).Limit(rand() % 100)) + .OpenBracket() + .On(kFieldNameYear, randCond(), kFieldNameYear) + .Or() + .On(kFieldNameName, CondEq, kFieldNameName) + .Or() + .On(kFieldNameAge, randCond(), kFieldNameAge) + .CloseBracket() + .Or() + .Where(kFieldNameId, CondSet, RandIntVector(20, 0, 100)) + .Distinct(distinct)); + ExecuteAndVerify(Query(default_namespace) + .WhereBetweenFields(kFieldNameGenre, CondEq, kFieldNameAge) + .Sort(sortIdx, sortOrder) + .Distinct(distinct)); + ExecuteAndVerify(Query(default_namespace) + .WhereBetweenFields(kFieldNameName, CondLike, kFieldNameActor) + .Sort(sortIdx, sortOrder) + .Distinct(distinct)); + ExecuteAndVerify(Query(default_namespace) + .WhereBetweenFields(kFieldNamePackages, CondGt, kFieldNameStartTime) + .Sort(sortIdx, sortOrder) + .Distinct(distinct)); + ExecuteAndVerify( + Query(compositeIndexesNs).WhereBetweenFields(kCompositeFieldPriceTitle, CondEq, kCompositeFieldPagesTitle)); + ExecuteAndVerify(Query(default_namespace) + .Not() + .Where(kFieldNameIsDeleted, CondEq, true) + .Or() + .Where(kFieldNameYear, CondGt, 2001) + .Sort(sortIdx, sortOrder) + .Distinct(distinct)); + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameGenre, CondEq, 5) + .Or() + .OpenBracket() + .Where(kFieldNameGenre, CondEq, 4) + .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) + .CloseBracket() + .Not() + .Where(kFieldNameYear, CondRange, {2001, 2010}) + .OpenBracket() + .Where(kFieldNameRate, CondRange, + {static_cast(rand() % 100) / 10, static_cast(rand() % 100) / 10}) + .Or() + .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) + .Not() + .OpenBracket() + .OpenBracket() + .OpenBracket() + .Where(kFieldNameAge, CondSet, RandIntVector(10, 0, 50)) + .Or() + .Where(kFieldNameId, CondEq, rand() % 5000) + .Where(kFieldNameTemp, CondEq, "") + .CloseBracket() + .Or() + .OpenBracket() + .Where(kFieldNameTemp, CondEq, "") + .Not() + .Where(kFieldNameIsDeleted, CondEq, true) + .Or() + .Where(kFieldNameYear, CondGt, 2001) + .CloseBracket() + .CloseBracket() + .CloseBracket() + .CloseBracket() + .Sort(sortIdx, sortOrder) + .Distinct(distinct)); + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameGenre, CondEq, 5) + .Or() + .OpenBracket() + .Where(kFieldNameGenre, CondEq, 4) + .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) + .CloseBracket() + .Not() + .Where(kFieldNameYear, CondRange, {2001, 2010}) + .OpenBracket() + .Where(kFieldNameRate, CondRange, + {static_cast(rand() % 100) / 10, static_cast(rand() % 100) / 10}) + .Or() + .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) + .Not() + .OpenBracket() + .OpenBracket() + .OpenBracket() + .Where(kFieldNameAge, CondSet, RandIntVector(10, 0, 50)) + .Or() + .Where(kFieldNameId, CondEq, rand() % 5000) + .Where(kFieldNameTemp, CondEq, "") + .CloseBracket() + .Or() + .OpenBracket() + .Where(kFieldNameTemp, CondEq, "") + .Not() + .OpenBracket() + .Where(kFieldNameIsDeleted, CondEq, true) + .CloseBracket() + .Or() + .Where(kFieldNameYear, CondGt, 2001) + .CloseBracket() + .CloseBracket() + .CloseBracket() + .CloseBracket() + .Sort(sortIdx, sortOrder) + .Distinct(distinct)); + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameGenre, CondEq, 5) + .Or() + .OpenBracket() + .Where(kFieldNameGenre, CondEq, 4) + .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) + .CloseBracket() + .Not() + .Where(kFieldNameYear, CondRange, {2001, 2010}) + .OpenBracket() + .Where(kFieldNameRate, CondRange, + {static_cast(rand() % 100) / 10, static_cast(rand() % 100) / 10}) + .Or() + .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) + .Not() + .OpenBracket() + .OpenBracket() + .OpenBracket() + .Where(kFieldNameAge, CondSet, RandIntVector(10, 0, 50)) + .Or() + .Where(kFieldNameId, CondEq, rand() % 5000) + .Where(kFieldNameTemp, CondEq, "") + .CloseBracket() + .Or() + .OpenBracket() + .Where(kFieldNameTemp, CondEq, "") + .OpenBracket() + .Not() + .Where(kFieldNameIsDeleted, CondEq, true) + .CloseBracket() + .Or() + .Where(kFieldNameYear, CondGt, 2001) + .CloseBracket() + .CloseBracket() + .CloseBracket() + .CloseBracket() + .Sort(sortIdx, sortOrder) + .Distinct(distinct)); + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameGenre, CondEq, 5) + .Or() + .OpenBracket() + .Where(kFieldNameGenre, CondEq, 4) + .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) + .CloseBracket() + .Not() + .Where(kFieldNameYear, CondRange, {2001, 2010}) + .OpenBracket() + .Where(kFieldNameRate, CondRange, + {static_cast(rand() % 100) / 10, static_cast(rand() % 100) / 10}) + .Or() + .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) + .Not() + .OpenBracket() + .OpenBracket() + .OpenBracket() + .Where(kFieldNameAge, CondSet, RandIntVector(10, 0, 50)) + .Or() + .Where(kFieldNameId, CondEq, rand() % 5000) + .Where(kFieldNameTemp, CondEq, "") + .CloseBracket() + .Or() + .OpenBracket() + .Where(kFieldNameTemp, CondEq, "") + .OpenBracket() + .Not() + .Where(kFieldNameIsDeleted, CondEq, true) + .Or() + .Where(kFieldNameYear, CondGt, 2001) + .CloseBracket() + .CloseBracket() + .CloseBracket() + .CloseBracket() + .CloseBracket() + .Sort(sortIdx, sortOrder) + .Distinct(distinct)); + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameGenre, CondEq, 5) + .Or() + .OpenBracket() + .Where(kFieldNameGenre, CondEq, 4) + .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) + .CloseBracket() + .Not() + .Where(kFieldNameYear, CondRange, {2001, 2010}) + .OpenBracket() + .Where(kFieldNameRate, CondRange, + {static_cast(rand() % 100) / 10, static_cast(rand() % 100) / 10}) + .Or() + .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) + .Not() + .OpenBracket() + .OpenBracket() + .OpenBracket() + .Where(kFieldNameAge, CondSet, RandIntVector(10, 0, 50)) + .Or() + .Where(kFieldNameId, CondEq, rand() % 5000) + .Where(kFieldNameTemp, CondEq, "") + .CloseBracket() + .Or() + .OpenBracket() + .Where(kFieldNameTemp, CondEq, "") + .Not() + .OpenBracket() + .Where(kFieldNameIsDeleted, CondEq, true) + .Or() + .Where(kFieldNameYear, CondGt, 2001) + .CloseBracket() + .CloseBracket() + .CloseBracket() + .CloseBracket() + .CloseBracket() + .Sort(sortIdx, sortOrder) + .Distinct(distinct)); + ExecuteAndVerify( + Query(default_namespace) + .Join(InnerJoin, Query(joinNs).Where(kFieldNameYear, CondGt, 2000 + rand() % 210).Limit(rand() % 100)) + .OpenBracket() + .Not() + .On(kFieldNameYear, randCond(), kFieldNameYear) + .Or() + .On(kFieldNameName, CondEq, kFieldNameName) + .Or() + .On(kFieldNameAge, randCond(), kFieldNameAge) + .CloseBracket() + .Distinct(distinct)); + + for (CondType cond : {CondEq, CondSet, CondLt, CondLe, CondGt, CondGe, CondRange}) { + const auto argsCount = minMaxArgs(cond, 20); + if (argsCount.min <= 1 && argsCount.max >= 1) { + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameUuid, cond, randUuid()) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameUuid, cond, randStrUuid()) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameUuidArr, cond, randUuid()) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameUuidArr, cond, randStrUuid()) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + } + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameUuid, cond, randHeterogeneousUuidArray(argsCount.min, argsCount.max)) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameUuidArr, cond, randHeterogeneousUuidArray(argsCount.min, argsCount.max)) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + std::vector compositeKeyValues; + VariantArray hetUuidArray = randHeterogeneousUuidArray(argsCount.min, argsCount.max); + compositeKeyValues.reserve(hetUuidArray.size()); + std::transform(std::make_move_iterator(hetUuidArray.begin()), std::make_move_iterator(hetUuidArray.end()), + std::back_inserter(compositeKeyValues), + [this](Variant&& uuid) { return VariantArray::Create(std::move(uuid), RandString()); }); + ExecuteAndVerify(Query(default_namespace) + .WhereComposite(kCompositeFieldUuidName, cond, compositeKeyValues) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + } + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameUuid, CondRange, randHeterogeneousUuidArray(2, 2)) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameUuidArr, CondRange, randHeterogeneousUuidArray(2, 2)) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify( + Query(default_namespace) + .WhereComposite(kCompositeFieldUuidName, CondRange, + {VariantArray::Create(nilUuid(), RandString()), VariantArray::Create(randUuid(), RandString())}) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(Query(default_namespace).Where(kFieldNameId, CondEq, 10), CondAny, {}) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Not() + .Where(Query(default_namespace), CondEmpty, {}) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameId, CondLt, Query(default_namespace).Aggregate(AggAvg, {kFieldNameId})) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(kFieldNameGenre, CondSet, + Query(joinNs).Select({kFieldNameGenre}).Where(kFieldNameId, CondSet, {10, 20, 30, 40})) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify( + Query(default_namespace) + .Where(Query(joinNs).Select({kFieldNameGenre}).Where(kFieldNameId, CondGt, 10), CondSet, {10, 20, 30, 40}) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(Query(joinNs).Select({kFieldNameGenre}).Where(kFieldNameId, CondGt, 10).Offset(1), CondSet, + {10, 20, 30, 40}) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify( + Query(default_namespace) + .Where(Query(joinNs).Where(kFieldNameId, CondGt, 10).Aggregate(AggMax, {kFieldNameGenre}), CondRange, {48, 50}) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(Query(joinNs).Where(kFieldNameId, CondGt, 10).ReqTotal(), CondGt, {50}) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify( + Query(default_namespace) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder) + .Debug(LogTrace) + .Where(kFieldNameGenre, CondEq, 5) + .Not() + .Where(Query(default_namespace).Where(kFieldNameGenre, CondEq, 5), CondAny, {}) + .Or() + .Where(kFieldNameGenre, CondSet, + Query(joinNs).Select({kFieldNameGenre}).Where(kFieldNameId, CondSet, {10, 20, 30, 40})) + .Not() + .OpenBracket() + .Where(kFieldNameYear, CondRange, {2001, 2020}) + .Or() + .Where(kFieldNameName, CondLike, RandLikePattern()) + .Or() + .Where(Query(joinNs).Where(kFieldNameYear, CondEq, 2000 + rand() % 210), CondEmpty, {}) + .CloseBracket() + .Or() + .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) + .OpenBracket() + .Where(kFieldNameNumeric, CondLt, std::to_string(600)) + .Not() + .OpenBracket() + .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) + .Where(kFieldNameGenre, CondLt, 6) + .Or() + .Where(kFieldNameId, CondLt, Query(default_namespace).Aggregate(AggAvg, {kFieldNameId})) + .CloseBracket() + .Not() + .Where(Query(joinNs).Where(kFieldNameId, CondGt, 10).Aggregate(AggMax, {kFieldNameGenre}), CondRange, {48, 50}) + .Or() + .Where(kFieldNameYear, CondEq, 10) + .CloseBracket()); + + ExecuteAndVerify(Query(default_namespace) + .Where(kCompositeFieldIdTemp, CondEq, + Query(default_namespace).Select({kCompositeFieldIdTemp}).Where(kFieldNameId, CondGt, 10)) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify( + Query(default_namespace) + .Where(Query(default_namespace).Select({kCompositeFieldUuidName}).Where(kFieldNameId, CondGt, 10), CondRange, + {VariantArray::Create(nilUuid(), RandString()), VariantArray::Create(randUuid(), RandString())}) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(Query(default_namespace) + .Select({kCompositeFieldAgeGenre}) + .Where(kFieldNameId, CondGt, 10) + .Sort(kCompositeFieldAgeGenre, false) + .Limit(10), + CondLe, {Variant(VariantArray::Create(rand() % 50, rand() % 50))}) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(Query(default_namespace).Where(kFieldNameId, CondGt, 10).ReqTotal(), CondGe, {10}) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + + ExecuteAndVerify(Query(default_namespace) + .Where(Query(default_namespace).Where(kFieldNameId, CondGt, 10).CachedTotal(), CondGe, {10}) + .Distinct(distinct.c_str()) + .Sort(sortIdx, sortOrder)); + } + } + } + } catch (const reindexer::Error& err) { + ASSERT_TRUE(false) << err.what(); + } catch (const std::exception& err) { + ASSERT_TRUE(false) << err.what(); + } catch (...) { + ASSERT_TRUE(false); + } +} diff --git a/cpp_src/gtests/tests/fixtures/queries_api.h b/cpp_src/gtests/tests/fixtures/queries_api.h index db85bc0bc..24477dd31 100644 --- a/cpp_src/gtests/tests/fixtures/queries_api.h +++ b/cpp_src/gtests/tests/fixtures/queries_api.h @@ -11,13 +11,10 @@ #include "core/keyvalue/geometry.h" #include "core/nsselecter/joinedselectormock.h" #include "core/queryresults/joinresults.h" -#include "core/type_consts_helpers.h" -#include "core/sorting/sortexpression.h" #include "gtests/tools.h" #include "queries_verifier.h" #include "reindexer_api.h" -#include "tools/random.h" -#include "tools/stringstools.h" +#include "tools/randompoint.h" class QueriesApi : public ReindexerApi, public QueriesVerifier { public: @@ -67,12 +64,33 @@ class QueriesApi : public ReindexerApi, public QueriesVerifier { IndexDeclaration{kCompositeFieldUuidName, "hash", "composite", IndexOpts{}, 0}, IndexDeclaration{kFieldNameYearSparse, "hash", "string", IndexOpts{}.Sparse(), 0}, }); + addIndexFields(default_namespace, kFieldNameId, {{kFieldNameId, reindexer::KeyValueType::Int{}}}); + addIndexFields(default_namespace, kFieldNameGenre, {{kFieldNameGenre, reindexer::KeyValueType::Int{}}}); + addIndexFields(default_namespace, kFieldNameYear, {{kFieldNameYear, reindexer::KeyValueType::Int{}}}); + addIndexFields(default_namespace, kFieldNamePackages, {{kFieldNamePackages, reindexer::KeyValueType::Int{}}}); + addIndexFields(default_namespace, kFieldNameName, {{kFieldNameName, reindexer::KeyValueType::String{}}}); + addIndexFields(default_namespace, kFieldNameCountries, {{kFieldNameCountries, reindexer::KeyValueType::String{}}}); + addIndexFields(default_namespace, kFieldNameAge, {{kFieldNameAge, reindexer::KeyValueType::Int{}}}); + addIndexFields(default_namespace, kFieldNameDescription, {{kFieldNameDescription, reindexer::KeyValueType::String{}}}); + addIndexFields(default_namespace, kFieldNameRate, {{kFieldNameRate, reindexer::KeyValueType::Double{}}}); + addIndexFields(default_namespace, kFieldNameIsDeleted, {{kFieldNameIsDeleted, reindexer::KeyValueType::Bool{}}}); + addIndexFields(default_namespace, kFieldNameActor, {{kFieldNameActor, reindexer::KeyValueType::String{}}}); + addIndexFields(default_namespace, kFieldNamePriceId, {{kFieldNamePriceId, reindexer::KeyValueType::Int{}}}); + addIndexFields(default_namespace, kFieldNameLocation, {{kFieldNameLocation, reindexer::KeyValueType::String{}}}); + addIndexFields(default_namespace, kFieldNameEndTime, {{kFieldNameEndTime, reindexer::KeyValueType::Int{}}}); + addIndexFields(default_namespace, kFieldNameStartTime, {{kFieldNameStartTime, reindexer::KeyValueType::Int{}}}); + addIndexFields(default_namespace, kFieldNameBtreeIdsets, {{kFieldNameBtreeIdsets, reindexer::KeyValueType::Int{}}}); + addIndexFields(default_namespace, kFieldNameTemp, {{kFieldNameTemp, reindexer::KeyValueType::String{}}}); + addIndexFields(default_namespace, kFieldNameNumeric, {{kFieldNameNumeric, reindexer::KeyValueType::String{}}}); + addIndexFields(default_namespace, kFieldNameUuid, {{kFieldNameUuid, reindexer::KeyValueType::Uuid{}}}); + addIndexFields(default_namespace, kFieldNameUuidArr, {{kFieldNameUuidArr, reindexer::KeyValueType::Uuid{}}}); addIndexFields(default_namespace, kCompositeFieldIdTemp, {{kFieldNameId, reindexer::KeyValueType::Int{}}, {kFieldNameTemp, reindexer::KeyValueType::String{}}}); addIndexFields(default_namespace, kCompositeFieldAgeGenre, {{kFieldNameAge, reindexer::KeyValueType::Int{}}, {kFieldNameGenre, reindexer::KeyValueType::Int{}}}); addIndexFields(default_namespace, kCompositeFieldUuidName, {{kFieldNameUuid, reindexer::KeyValueType::Uuid{}}, {kFieldNameName, reindexer::KeyValueType::String{}}}); + addIndexFields(default_namespace, kFieldNameYearSparse, {{kFieldNameYearSparse, reindexer::KeyValueType::String{}}}); err = rt.reindexer->OpenNamespace(joinNs); ASSERT_TRUE(err.ok()) << err.what(); @@ -82,6 +100,12 @@ class QueriesApi : public ReindexerApi, public QueriesVerifier { IndexDeclaration{kFieldNameName, "tree", "string", IndexOpts(), 0}, IndexDeclaration{kFieldNameDescription, "text", "string", IndexOpts{}, 0}, IndexDeclaration{kFieldNameYearSparse, "hash", "string", IndexOpts().Sparse(), 0}}); + addIndexFields(joinNs, kFieldNameId, {{kFieldNameId, reindexer::KeyValueType::Int{}}}); + addIndexFields(joinNs, kFieldNameYear, {{kFieldNameYear, reindexer::KeyValueType::Int{}}}); + addIndexFields(joinNs, kFieldNameAge, {{kFieldNameAge, reindexer::KeyValueType::Int{}}}); + addIndexFields(joinNs, kFieldNameName, {{kFieldNameName, reindexer::KeyValueType::String{}}}); + addIndexFields(joinNs, kFieldNameDescription, {{kFieldNameDescription, reindexer::KeyValueType::String{}}}); + addIndexFields(joinNs, kFieldNameYearSparse, {{kFieldNameYearSparse, reindexer::KeyValueType::String{}}}); err = rt.reindexer->OpenNamespace(testSimpleNs); ASSERT_TRUE(err.ok()) << err.what(); @@ -91,6 +115,10 @@ class QueriesApi : public ReindexerApi, public QueriesVerifier { IndexDeclaration{kFieldNameName, "hash", "string", IndexOpts(), 0}, IndexDeclaration{kFieldNamePhone, "hash", "string", IndexOpts(), 0}, }); + addIndexFields(testSimpleNs, kFieldNameId, {{kFieldNameId, reindexer::KeyValueType::Int{}}}); + addIndexFields(testSimpleNs, kFieldNameYear, {{kFieldNameYear, reindexer::KeyValueType::Int{}}}); + addIndexFields(testSimpleNs, kFieldNameName, {{kFieldNameName, reindexer::KeyValueType::String{}}}); + addIndexFields(testSimpleNs, kFieldNamePhone, {{kFieldNamePhone, reindexer::KeyValueType::String{}}}); err = rt.reindexer->OpenNamespace(compositeIndexesNs); ASSERT_TRUE(err.ok()) << err.what(); @@ -106,6 +134,12 @@ class QueriesApi : public ReindexerApi, public QueriesVerifier { IndexDeclaration{kCompositeFieldPriceTitle, "hash", "composite", IndexOpts(), 0}, IndexDeclaration{kCompositeFieldPagesTitle, "hash", "composite", IndexOpts(), 0}, IndexDeclaration{kCompositeFieldBookidBookid2, "hash", "composite", IndexOpts().PK(), 0}}); + addIndexFields(compositeIndexesNs, kFieldNameBookid, {{kFieldNameBookid, reindexer::KeyValueType::Int{}}}); + addIndexFields(compositeIndexesNs, kFieldNameBookid2, {{kFieldNameBookid2, reindexer::KeyValueType::Int{}}}); + addIndexFields(compositeIndexesNs, kFieldNameTitle, {{kFieldNameTitle, reindexer::KeyValueType::String{}}}); + addIndexFields(compositeIndexesNs, kFieldNamePages, {{kFieldNamePages, reindexer::KeyValueType::Int{}}}); + addIndexFields(compositeIndexesNs, kFieldNamePrice, {{kFieldNamePrice, reindexer::KeyValueType::Int{}}}); + addIndexFields(compositeIndexesNs, kFieldNameName, {{kFieldNameName, reindexer::KeyValueType::String{}}}); addIndexFields(compositeIndexesNs, kCompositeFieldPricePages, {{kFieldNamePrice, reindexer::KeyValueType::Int{}}, {kFieldNamePages, reindexer::KeyValueType::Int{}}}); addIndexFields(compositeIndexesNs, kCompositeFieldTitleName, @@ -128,12 +162,22 @@ class QueriesApi : public ReindexerApi, public QueriesVerifier { IndexDeclaration{kFieldNameColumnString, "-", "string", IndexOpts(), 0}, IndexDeclaration{kFieldNameColumnFullText, "text", "string", IndexOpts().SetConfig(R"xxx({"stemmers":[]})xxx"), 0}, IndexDeclaration{kFieldNameColumnStringNumeric, "-", "string", IndexOpts().SetCollateMode(CollateNumeric), 0}}); + addIndexFields(comparatorsNs, kFieldNameId, {{kFieldNameId, reindexer::KeyValueType::Int{}}}); + addIndexFields(comparatorsNs, kFieldNameColumnInt, {{kFieldNameColumnInt, reindexer::KeyValueType::Int{}}}); + addIndexFields(comparatorsNs, kFieldNameColumnInt64, {{kFieldNameColumnInt64, reindexer::KeyValueType::Int64{}}}); + addIndexFields(comparatorsNs, kFieldNameColumnDouble, {{kFieldNameColumnDouble, reindexer::KeyValueType::Double{}}}); + addIndexFields(comparatorsNs, kFieldNameColumnString, {{kFieldNameColumnString, reindexer::KeyValueType::String{}}}); + addIndexFields(comparatorsNs, kFieldNameColumnFullText, {{kFieldNameColumnFullText, reindexer::KeyValueType::String{}}}); + addIndexFields(comparatorsNs, kFieldNameColumnStringNumeric, {{kFieldNameColumnStringNumeric, reindexer::KeyValueType::String{}}}); err = rt.reindexer->OpenNamespace(forcedSortOffsetNs); ASSERT_TRUE(err.ok()) << err.what(); DefineNamespaceDataset(forcedSortOffsetNs, {IndexDeclaration{kFieldNameId, "hash", "int", IndexOpts().PK(), 0}, IndexDeclaration{kFieldNameColumnHash, "hash", "int", IndexOpts(), 0}, IndexDeclaration{kFieldNameColumnTree, "tree", "int", IndexOpts(), 0}}); + addIndexFields(forcedSortOffsetNs, kFieldNameId, {{kFieldNameId, reindexer::KeyValueType::Int{}}}); + addIndexFields(forcedSortOffsetNs, kFieldNameColumnHash, {{kFieldNameColumnHash, reindexer::KeyValueType::Int{}}}); + addIndexFields(forcedSortOffsetNs, kFieldNameColumnTree, {{kFieldNameColumnTree, reindexer::KeyValueType::Int{}}}); err = rt.reindexer->OpenNamespace(geomNs); ASSERT_TRUE(err.ok()) << err.what(); @@ -143,12 +187,20 @@ class QueriesApi : public ReindexerApi, public QueriesVerifier { IndexDeclaration{kFieldNamePointLinearRTree, "rtree", "point", IndexOpts{}.RTreeType(IndexOpts::Linear), 0}, IndexDeclaration{kFieldNamePointGreeneRTree, "rtree", "point", IndexOpts{}.RTreeType(IndexOpts::Greene), 0}, IndexDeclaration{kFieldNamePointRStarRTree, "rtree", "point", IndexOpts{}.RTreeType(IndexOpts::RStar), 0}}); + addIndexFields(geomNs, kFieldNameId, {{kFieldNameId, reindexer::KeyValueType::Int{}}}); + addIndexFields(geomNs, kFieldNamePointQuadraticRTree, {{kFieldNamePointQuadraticRTree, reindexer::KeyValueType::Double{}}}); + addIndexFields(geomNs, kFieldNamePointLinearRTree, {{kFieldNamePointLinearRTree, reindexer::KeyValueType::Double{}}}); + addIndexFields(geomNs, kFieldNamePointGreeneRTree, {{kFieldNamePointGreeneRTree, reindexer::KeyValueType::Double{}}}); + addIndexFields(geomNs, kFieldNamePointRStarRTree, {{kFieldNamePointRStarRTree, reindexer::KeyValueType::Double{}}}); err = rt.reindexer->OpenNamespace(btreeIdxOptNs); ASSERT_TRUE(err.ok()) << err.what(); DefineNamespaceDataset(btreeIdxOptNs, {IndexDeclaration{kFieldNameId, "tree", "int", IndexOpts().PK(), 0}, IndexDeclaration{kFieldNameStartTime, "tree", "int", IndexOpts(), 0}}); + addIndexFields(btreeIdxOptNs, kFieldNameId, {{kFieldNameId, reindexer::KeyValueType::Int{}}}); + addIndexFields(btreeIdxOptNs, kFieldNameStartTime, {{kFieldNameStartTime, reindexer::KeyValueType::Int{}}}); initConditionsNs(); + initUUIDNs(); } void initConditionsNs(); @@ -156,51 +208,67 @@ class QueriesApi : public ReindexerApi, public QueriesVerifier { void CheckConditions(); enum class NullAllowed : bool { Yes = true, No = false }; void checkAllConditions(const std::string& fieldName, reindexer::KeyValueType fieldType, NullAllowed); - - template - void ExecuteAndVerify(const Query& query, T... args) { + void initUUIDNs(); + void FillUUIDNs(); + void CheckUUIDQueries(); + void CheckStandartQueries(); + void CheckDslQueries(); + void checkDslQuery(std::string_view dsl, Query&& checkQuery); + void CheckSqlQueries(); + void checkSqlQuery(std::string_view sql, Query&& checkQuery); + + template + void ExecuteAndVerify(Q&& query, Args&&... args) { reindexer::QueryResults qr; - const_cast(query).Explain(); + query.Explain(); Error err = rt.reindexer->Select(query, qr); - ASSERT_TRUE(err.ok()) << err.what(); - Verify(qr, query, *rt.reindexer); - Verify(qr, args...); + ASSERT_TRUE(err.ok()) << err.what() << '\n' << query.GetSQL(); + if constexpr (std::is_rvalue_reference_v) { + Verify(qr, std::move(query), *rt.reindexer); + } else { + Verify(qr, reindexer::Query(query), *rt.reindexer); + } + Verify(qr, std::forward(args)...); } - void ExecuteAndVerifyWithSql(const Query& query) { + template + void ExecuteAndVerifyWithSql(Q&& query) { ExecuteAndVerify(query); - Query queryFromSql; - queryFromSql.FromSQL(query.GetSQL()); - ExecuteAndVerify(queryFromSql); + Query queryFromSql = Query::FromSQL(query.GetSQL()); + ExecuteAndVerify(std::move(queryFromSql)); } - template - void ExecuteAndVerify(const Query& query, QueryResults& qr, T... args) { - const_cast(query).Explain(); + template + void ExecuteAndVerify(Q&& query, QueryResults& qr, Args&&... args) { + query.Explain(); Error err = rt.reindexer->Select(query, qr); ASSERT_TRUE(err.ok()) << err.what(); - Verify(qr, query, *rt.reindexer); - Verify(qr, args...); + if constexpr (std::is_rvalue_reference_v) { + Verify(qr, std::move(query), *rt.reindexer); + } else { + Verify(qr, reindexer::Query(query), *rt.reindexer); + } + Verify(qr, std::forward(args)...); } - void ExecuteAndVerifyWithSql(const Query& query, QueryResults& qr) { + template + void ExecuteAndVerifyWithSql(Q&& query, QueryResults& qr) { ExecuteAndVerify(query, qr); - Query queryFromSql; - queryFromSql.FromSQL(query.GetSQL()); + Query queryFromSql = Query::FromSQL(query.GetSQL()); qr.Clear(); - ExecuteAndVerify(queryFromSql, qr); + ExecuteAndVerify(std::move(queryFromSql), qr); } void Verify(const reindexer::QueryResults&) const noexcept {} void Verify(const LocalQueryResults&) const noexcept {} - template - void Verify(const QueryResults& qr, const char* fieldName, const std::vector& expectedValues, T... args) { - Verify(qr.ToLocalQr(), fieldName, expectedValues, std::forward(args)...); + template + void Verify(const QueryResults& qr, const char* fieldName, const std::vector& expectedValues, Args&&... args) { + Verify(qr.ToLocalQr(), fieldName, expectedValues, std::forward(args)...); } - template + template void Verify(const reindexer::LocalQueryResults& qr, const char* fieldName, const std::vector& expectedValues, - T... args) { + Args&&... args) { reindexer::WrSerializer ser; if (qr.Count() != expectedValues.size()) { ser << "Sizes different: expected size " << expectedValues.size() << ", obtained size " << qr.Count() << '\n'; @@ -232,7 +300,7 @@ class QueriesApi : public ReindexerApi, public QueriesVerifier { } FAIL() << ser.Slice() << std::endl; } - Verify(qr, args...); + Verify(qr, std::forward(args)...); } using QueriesVerifier::Verify; @@ -336,26 +404,26 @@ class QueriesApi : public ReindexerApi, public QueriesVerifier { const size_t id = i + lastId; bld.Put(kFieldNameId, id); - reindexer::Point point{randPoint(10)}; + reindexer::Point point{reindexer::randPoint(10)}; double arr[]{point.X(), point.Y()}; bld.Array(kFieldNamePointQuadraticRTree, reindexer::span{arr, 2}); - point = randPoint(10); + point = reindexer::randPoint(10); arr[0] = point.X(); arr[1] = point.Y(); bld.Array(kFieldNamePointLinearRTree, reindexer::span{arr, 2}); - point = randPoint(10); + point = reindexer::randPoint(10); arr[0] = point.X(); arr[1] = point.Y(); bld.Array(kFieldNamePointGreeneRTree, reindexer::span{arr, 2}); - point = randPoint(10); + point = reindexer::randPoint(10); arr[0] = point.X(); arr[1] = point.Y(); bld.Array(kFieldNamePointRStarRTree, reindexer::span{arr, 2}); - point = randPoint(10); + point = reindexer::randPoint(10); arr[0] = point.X(); arr[1] = point.Y(); bld.Array(kFieldNamePointNonIndex, reindexer::span{arr, 2}); @@ -521,8 +589,9 @@ class QueriesApi : public ReindexerApi, public QueriesVerifier { tr.Insert(std::move(item)); } QueryResults res; - rt.reindexer->CommitTransaction(tr, res); - const auto err = Commit(default_namespace); + auto err = rt.reindexer->CommitTransaction(tr, res); + ASSERT_TRUE(err.ok()) << err.what(); + err = Commit(default_namespace); ASSERT_TRUE(err.ok()) << err.what(); } @@ -605,6 +674,8 @@ class QueriesApi : public ReindexerApi, public QueriesVerifier { void CheckMergeQueriesWithAggregation(); void CheckGeomQueries() { + using reindexer::randPoint; + using reindexer::randBinDouble; for (size_t i = 0; i < 10; ++i) { // Checks that DWithin and sort by Distance work and verifies the result ExecuteAndVerify(Query(geomNs).DWithin(kFieldNamePointQuadraticRTree, randPoint(10), randBinDouble(0, 1))); @@ -692,873 +763,6 @@ class QueriesApi : public ReindexerApi, public QueriesVerifier { } } - void CheckStandartQueries() { - try { - using namespace std::string_literals; - static const std::vector sortIdxs = { - "", - kFieldNameName, - kFieldNameYear, - kFieldNameRate, - kFieldNameBtreeIdsets, - std::string{"-2.5 * "} + kFieldNameRate + " / (" + kFieldNameYear + " + " + kFieldNameId + ')'}; - static const std::vector distincts = {"", kFieldNameYear, kFieldNameRate}; - static const std::vector sortOrders = {true, false}; - - static const std::string compositeIndexName(kFieldNameAge + compositePlus + kFieldNameGenre); - - for (const bool sortOrder : sortOrders) { - for (const auto& sortIdx : sortIdxs) { - for (const std::string& distinct : distincts) { - const int randomAge = rand() % 50; - const int randomGenre = rand() % 50; - const int randomGenreUpper = rand() % 100; - const int randomGenreLower = rand() % 100; - - ExecuteAndVerify(Query(default_namespace).Distinct(distinct.c_str()).Sort(sortIdx, sortOrder).Limit(1)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameGenre, CondEq, randomGenre) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameGenre, CondEq, std::to_string(randomGenre)) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameName, CondEq, RandString()) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameRate, CondEq, (rand() % 100) / 10.0) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameRate, CondEq, std::to_string((rand() % 100) / 10.0)) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameGenre, CondGt, randomGenre) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder) - .Debug(LogTrace)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameName, CondGt, RandString()) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameRate, CondGt, (rand() % 100) / 10.0) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameGenre, CondLt, randomGenre) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameGenre, CondLt, std::to_string(randomGenre)) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameName, CondLt, RandString()) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameRate, CondLt, (rand() % 100) / 10.0) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameRate, CondLt, std::to_string((rand() % 100) / 10.0)) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameBtreeIdsets, CondLt, static_cast(rand() % 10000)) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameBtreeIdsets, CondGt, static_cast(rand() % 10000)) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameBtreeIdsets, CondEq, static_cast(rand() % 10000)) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameGenre, CondRange, {randomGenreLower, randomGenreUpper}) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder) - .Debug(LogTrace)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameName, CondRange, {RandString(), RandString()}) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameName, CondLike, RandLikePattern()) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameRate, CondRange, - {static_cast(rand() % 100) / 10, static_cast(rand() % 100) / 10}) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNamePackages, CondSet, RandIntVector(10, 10000, 50)) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNamePackages, CondAllSet, RandIntVector(2, 10000, 50)) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNamePackages, CondAllSet, 10000 + rand() % 50) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - // check substituteCompositIndexes - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameAge, CondEq, randomAge) - .Where(kFieldNameGenre, CondEq, randomGenre) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameAge, CondSet, RandIntVector(10, 0, 50)) - .Where(kFieldNameGenre, CondEq, randomGenre) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameAge, CondAllSet, RandIntVector(1, 0, 50)) - .Where(kFieldNameGenre, CondEq, randomGenre) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameAge, CondSet, RandIntVector(10, 0, 50)) - .Where(kFieldNameGenre, CondSet, RandIntVector(10, 0, 50)) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameAge, CondSet, RandIntVector(10, 0, 20)) - .Where(kFieldNameGenre, CondSet, RandIntVector(10, 0, 50)) - .Where(kFieldNameAge, CondSet, RandIntVector(10, 30, 50)) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameAge, CondSet, RandIntVector(10, 0, 20)) - .Where(kFieldNameGenre, CondEq, randomGenre) - .Where(kFieldNameAge, CondSet, RandIntVector(10, 30, 50)) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - // end of check substituteCompositIndexes - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNamePackages, CondEmpty, 0) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameName, CondRange, {RandString(), RandString()}) - .Distinct(distinct.c_str()) - .Sort(kFieldNameYear, true) - .Sort(kFieldNameName, false) - .Sort(kFieldNameLocation, true)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameName, CondRange, {RandString(), RandString()}) - .Distinct(distinct.c_str()) - .Sort(kFieldNameGenre, true) - .Sort(kFieldNameActor, false) - .Sort(kFieldNameRate, true) - .Sort(kFieldNameLocation, false)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameName, CondLike, RandLikePattern()) - .Distinct(distinct.c_str()) - .Sort(kFieldNameGenre, true) - .Sort(kFieldNameActor, false) - .Sort(kFieldNameRate, true) - .Sort(kFieldNameLocation, false)); - - ExecuteAndVerify(Query(default_namespace).Sort(kFieldNameGenre, true, {10, 20, 30})); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNamePackages, CondAny, 0) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameIsDeleted, CondEq, 1) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder) - .Where(kFieldNameGenre, CondEq, 5) - .Where(kFieldNameAge, CondEq, 3) - .Where(kFieldNameYear, CondGe, 2010) - .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) - .Debug(LogTrace)); - - ExecuteAndVerify(Query(default_namespace) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder) - .Debug(LogTrace) - .Where(kFieldNameGenre, CondEq, 5) - .Where(kFieldNameAge, CondEq, 3) - .Where(kFieldNameGenre, CondEq, 5) - .Where(kFieldNameAge, CondEq, 3) - .OpenBracket() - .Where(kFieldNameYear, CondGe, 2010) - .CloseBracket() - .Or() - .Where(kFieldNameYear, CondGe, 2010)); - - ExecuteAndVerify(Query(default_namespace) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder) - .Debug(LogTrace) - .Where(kFieldNameYear, CondGt, 2002) - .Where(kFieldNameGenre, CondEq, 4) - .Where(kFieldNameAge, CondEq, 3) - .Where(kFieldNameIsDeleted, CondEq, 3) - .Or() - .Where(kFieldNameYear, CondGt, 2001) - .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) - .Debug(LogTrace)); - - ExecuteAndVerify(Query(default_namespace) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder) - .Debug(LogTrace) - .Where(kFieldNameAge, CondSet, {1, 2, 3, 4}) - .Where(kFieldNameId, CondEq, rand() % 5000) - .Where(kFieldNameTemp, CondEq, "") - .Where(kFieldNameIsDeleted, CondEq, 1) - .Or() - .Where(kFieldNameYear, CondGt, 2001) - .Debug(LogTrace)); - - ExecuteAndVerify(Query(default_namespace) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder) - .Debug(LogTrace) - .Where(kFieldNameGenre, CondSet, {5, 1, 7}) - .Where(kFieldNameYear, CondLt, 2010) - .Where(kFieldNameGenre, CondEq, 3) - .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) - .Or() - .Where(kFieldNamePackages, CondEmpty, 0) - .Debug(LogTrace)); - - ExecuteAndVerify(Query(default_namespace) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder) - .Debug(LogTrace) - .Where(kFieldNameGenre, CondSet, {5, 1, 7}) - .Where(kFieldNameYear, CondLt, 2010) - .Or() - .Where(kFieldNamePackages, CondAny, 0) - .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) - .Debug(LogTrace)); - - ExecuteAndVerify(Query(default_namespace) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder) - .Debug(LogTrace) - .Where(kFieldNameGenre, CondEq, 5) - .Or() - .Where(kFieldNameGenre, CondEq, 6) - .Where(kFieldNameYear, CondRange, {2001, 2020}) - .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50))); - - ExecuteAndVerify(Query(default_namespace) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder) - .Debug(LogTrace) - .Where(kFieldNameGenre, CondEq, 5) - .Or() - .Where(kFieldNameGenre, CondEq, 6) - .Not() - .Where(kFieldNameName, CondLike, RandLikePattern()) - .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50))); - - ExecuteAndVerify(Query(default_namespace) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder) - .Debug(LogTrace) - .Where(kFieldNameActor, CondEq, RandString())); - - ExecuteAndVerify(Query(default_namespace) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder) - .Debug(LogTrace) - .Not() - .Where(kFieldNameGenre, CondEq, 5) - .Where(kFieldNameYear, CondRange, {2001, 2020}) - .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50))); - - ExecuteAndVerify(Query(default_namespace) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder) - .Debug(LogTrace) - .Where(kFieldNameGenre, CondEq, 5) - .Not() - .Where(kFieldNameYear, CondRange, {2001, 2020}) - .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50))); - - ExecuteAndVerify(Query(default_namespace) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder) - .Debug(LogTrace) - .Not() - .Where(kFieldNameYear, CondEq, 10)); - - ExecuteAndVerify(Query(default_namespace) - .Distinct(distinct.c_str()) - .Sort(kFieldNameNumeric, sortOrder) - .Debug(LogTrace) - .Where(kFieldNameNumeric, CondGt, std::to_string(5))); - - ExecuteAndVerify(Query(default_namespace) - .Distinct(distinct.c_str()) - .Sort(kFieldNameNumeric, sortOrder) - .Debug(LogTrace) - .Where(kFieldNameNumeric, CondLt, std::to_string(600))); - - ExecuteAndVerify(Query(default_namespace) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder) - .Debug(LogTrace) - .Where(kFieldNameGenre, CondEq, 5) - .Or() - .OpenBracket() - .Where(kFieldNameGenre, CondLt, 6) - .Where(kFieldNameYear, CondRange, {2001, 2020}) - .CloseBracket() - .Not() - .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) - .OpenBracket() - .Where(kFieldNameNumeric, CondLt, std::to_string(600)) - .Or() - .OpenBracket() - .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) - .Where(kFieldNameName, CondLike, RandLikePattern()) - .CloseBracket() - .Or() - .Where(kFieldNameYear, CondEq, 10) - .CloseBracket()); - - ExecuteAndVerify(Query(default_namespace) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder) - .Debug(LogTrace) - .Where(kFieldNameGenre, CondEq, 5) - .Not() - .OpenBracket() - .Where(kFieldNameYear, CondRange, {2001, 2020}) - .Or() - .Where(kFieldNameName, CondLike, RandLikePattern()) - .CloseBracket() - .Or() - .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) - .OpenBracket() - .Where(kFieldNameNumeric, CondLt, std::to_string(600)) - .Not() - .OpenBracket() - .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) - .Where(kFieldNameGenre, CondLt, 6) - .CloseBracket() - .Or() - .Where(kFieldNameYear, CondEq, 10) - .CloseBracket()); - - ExecuteAndVerify( - Query(default_namespace) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder) - .Debug(LogTrace) - .Where(kFieldNameNumeric, CondRange, {std::to_string(rand() % 100), std::to_string(rand() % 100 + 500)})); - - ExecuteAndVerify(Query(testSimpleNs).Where(kFieldNameName, CondEq, "SSS")); - ExecuteAndVerify(Query(testSimpleNs).Where(kFieldNameYear, CondEq, 2002)); - ExecuteAndVerify(Query(testSimpleNs).Where(kFieldNameYear, CondEq, 2002).Not().Where(kFieldNameName, CondEq, 2002)); - ExecuteAndVerify( - Query(testSimpleNs).Where(kFieldNameName, CondEq, "SSS").Not().Where(kFieldNameYear, CondEq, 2002)); - ExecuteAndVerify( - Query(testSimpleNs).Where(kFieldNameName, CondEq, "SSS").Not().Where(kFieldNameYear, CondEq, 1989)); - ExecuteAndVerify( - Query(testSimpleNs).Where(kFieldNameYear, CondEq, 2002).Not().Where(kFieldNameName, CondEq, "MMM")); - - ExecuteAndVerify(Query(default_namespace) - .ReqTotal() - .Distinct(distinct) - .Sort(sortIdx, sortOrder) - .WhereComposite(compositeIndexName.c_str(), CondLe, {{Variant(27), Variant(10000)}})); - - ExecuteAndVerify(Query(default_namespace) - .ReqTotal() - .Distinct(distinct) - .Sort(kFieldNameAge + " + "s + kFieldNameId, sortOrder) - .Sort(kFieldNameRate + " * "s + kFieldNameGenre, sortOrder)); - - ExecuteAndVerify( - Query(default_namespace) - .ReqTotal() - .Distinct(distinct) - .Sort(sortIdx, sortOrder) - .WhereComposite(compositeIndexName.c_str(), CondEq, {{Variant(rand() % 10), Variant(rand() % 50)}})); - - ExecuteAndVerify(Query(default_namespace) - .InnerJoin(kFieldNameYear, kFieldNameYear, CondEq, Query(joinNs)) - .Distinct(distinct) - .Sort(joinNs + '.' + kFieldNameId, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .InnerJoin(kFieldNameYear, kFieldNameYear, CondEq, Query(joinNs)) - .Distinct(distinct) - .Sort(joinNs + '.' + kFieldNameName, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .InnerJoin(kFieldNameYear, kFieldNameYear, CondEq, Query(joinNs)) - .Distinct(distinct) - .Sort(joinNs + '.' + kFieldNameId + " * " + joinNs + '.' + kFieldNameGenre + - (sortIdx.empty() || (sortIdx == kFieldNameName) ? "" : (" + " + sortIdx)), - sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .InnerJoin(kFieldNameYear, kFieldNameYear, CondEq, - Query(joinNs) - .Where(kFieldNameId, CondSet, RandIntVector(20, 0, 100)) - .Sort(kFieldNameId + " + "s + kFieldNameYear, sortOrder)) - .Distinct(distinct)); - - ExecuteAndVerify(Query(default_namespace) - .InnerJoin(kFieldNameYear, kFieldNameYear, CondEq, - Query(joinNs) - .Where(kFieldNameYear, CondGe, 1925) - .Sort(kFieldNameId + " + "s + kFieldNameYear, sortOrder)) - .Distinct(distinct)); - ExecuteAndVerify(Query(default_namespace) - .InnerJoin(kFieldNameYear, kFieldNameYear, randCond(), - Query(joinNs).Where(kFieldNameYear, CondGe, 2000 + rand() % 210)) - .Distinct(distinct)); - ExecuteAndVerify(Query(default_namespace) - .InnerJoin(kFieldNameYear, kFieldNameYear, randCond(), - Query(joinNs).Where(kFieldNameYear, CondLe, 2000 + rand() % 210).Limit(rand() % 10)) - .Distinct(distinct)); - ExecuteAndVerify( - Query(default_namespace) - .Join(InnerJoin, Query(joinNs).Where(kFieldNameYear, CondGt, 2000 + rand() % 210) /*.Offset(rand() % 10)*/) - .On(kFieldNameYear, randCond(), kFieldNameYear) - .Distinct(distinct)); - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameYearSparse, CondEq, std::to_string(2000 + rand() % 60)) - .Distinct(distinct)); - ExecuteAndVerify( - Query(default_namespace) - .InnerJoin(kFieldNameYear, kFieldNameYear, randCond(), - Query(joinNs).Where(kFieldNameYear, CondLt, 2000 + rand() % 210).Sort(kFieldNameName, sortOrder)) - .Distinct(distinct)); - ExecuteAndVerify(Query(default_namespace) - .Join(InnerJoin, Query(joinNs).Where(kFieldNameYear, CondGt, 2000 + rand() % 210)) - .OpenBracket() - .On(kFieldNameYear, randCond(), kFieldNameYear) - .Or() - .On(kFieldNameName, CondEq, kFieldNameName) - .CloseBracket() - .Distinct(distinct)); - ExecuteAndVerify(Query(default_namespace) - .Join(InnerJoin, Query(joinNs).Where(kFieldNameYear, CondGt, 2000 + rand() % 210)) - .OpenBracket() - .On(kFieldNameYear, randCond(), kFieldNameYear) - .Or() - .On(kFieldNameName, CondEq, kFieldNameName) - .On(kFieldNameAge, randCond(), kFieldNameAge) - .Not() - .On(kFieldNameYear, randCond(), kFieldNameYear) - .CloseBracket() - .Distinct(distinct)); - ExecuteAndVerify(Query(default_namespace) - .Join(InnerJoin, Query(joinNs).Where(kFieldNameYear, CondGt, 2000 + rand() % 210)) - .OpenBracket() - .On(kFieldNameYearSparse, CondLe, kFieldNameYearSparse) - .Or() - .On(kFieldNameName, CondEq, kFieldNameName) - .Or() - .On(kFieldNameAge, randCond(), kFieldNameAge) - .Not() - .On(kFieldNameYear, randCond(), kFieldNameYear) - .CloseBracket() - .Distinct(distinct)); - ExecuteAndVerify( - Query(default_namespace) - .Join(InnerJoin, Query(joinNs).Where(kFieldNameYear, CondGt, 2000 + rand() % 210).Limit(rand() % 100)) - .OpenBracket() - .On(kFieldNameYear, randCond(), kFieldNameYear) - .Or() - .On(kFieldNameName, CondEq, kFieldNameName) - .Or() - .On(kFieldNameAge, randCond(), kFieldNameAge) - .CloseBracket() - .Or() - .Join(InnerJoin, Query(joinNs).Where(kFieldNameYear, CondLt, 2000 + rand() % 210).Limit(rand() % 100)) - .OpenBracket() - .On(kFieldNameYear, randCond(), kFieldNameYear) - .Or() - .On(kFieldNameName, CondEq, kFieldNameName) - .Or() - .On(kFieldNameAge, randCond(), kFieldNameAge) - .CloseBracket() - .Join(OrInnerJoin, Query(joinNs).Where(kFieldNameYear, CondEq, 2000 + rand() % 210).Limit(rand() % 100)) - .OpenBracket() - .On(kFieldNameYear, randCond(), kFieldNameYear) - .Or() - .On(kFieldNameName, CondEq, kFieldNameName) - .Or() - .On(kFieldNameAge, randCond(), kFieldNameAge) - .CloseBracket() - .Or() - .Where(kFieldNameId, CondSet, RandIntVector(20, 0, 100)) - .Distinct(distinct)); - ExecuteAndVerify(Query(default_namespace) - .WhereBetweenFields(kFieldNameGenre, CondEq, kFieldNameAge) - .Sort(sortIdx, sortOrder) - .Distinct(distinct)); - ExecuteAndVerify(Query(default_namespace) - .WhereBetweenFields(kFieldNameName, CondLike, kFieldNameActor) - .Sort(sortIdx, sortOrder) - .Distinct(distinct)); - ExecuteAndVerify(Query(default_namespace) - .WhereBetweenFields(kFieldNamePackages, CondGt, kFieldNameStartTime) - .Sort(sortIdx, sortOrder) - .Distinct(distinct)); - ExecuteAndVerify( - Query(compositeIndexesNs).WhereBetweenFields(kCompositeFieldPriceTitle, CondEq, kCompositeFieldPagesTitle)); - ExecuteAndVerify(Query(default_namespace) - .Not() - .Where(kFieldNameIsDeleted, CondEq, true) - .Or() - .Where(kFieldNameYear, CondGt, 2001) - .Sort(sortIdx, sortOrder) - .Distinct(distinct)); - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameGenre, CondEq, 5) - .Or() - .OpenBracket() - .Where(kFieldNameGenre, CondEq, 4) - .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) - .CloseBracket() - .Not() - .Where(kFieldNameYear, CondRange, {2001, 2010}) - .OpenBracket() - .Where(kFieldNameRate, CondRange, - {static_cast(rand() % 100) / 10, static_cast(rand() % 100) / 10}) - .Or() - .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) - .Not() - .OpenBracket() - .OpenBracket() - .OpenBracket() - .Where(kFieldNameAge, CondSet, RandIntVector(10, 0, 50)) - .Or() - .Where(kFieldNameId, CondEq, rand() % 5000) - .Where(kFieldNameTemp, CondEq, "") - .CloseBracket() - .Or() - .OpenBracket() - .Where(kFieldNameTemp, CondEq, "") - .Not() - .Where(kFieldNameIsDeleted, CondEq, true) - .Or() - .Where(kFieldNameYear, CondGt, 2001) - .CloseBracket() - .CloseBracket() - .CloseBracket() - .CloseBracket() - .Sort(sortIdx, sortOrder) - .Distinct(distinct)); - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameGenre, CondEq, 5) - .Or() - .OpenBracket() - .Where(kFieldNameGenre, CondEq, 4) - .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) - .CloseBracket() - .Not() - .Where(kFieldNameYear, CondRange, {2001, 2010}) - .OpenBracket() - .Where(kFieldNameRate, CondRange, - {static_cast(rand() % 100) / 10, static_cast(rand() % 100) / 10}) - .Or() - .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) - .Not() - .OpenBracket() - .OpenBracket() - .OpenBracket() - .Where(kFieldNameAge, CondSet, RandIntVector(10, 0, 50)) - .Or() - .Where(kFieldNameId, CondEq, rand() % 5000) - .Where(kFieldNameTemp, CondEq, "") - .CloseBracket() - .Or() - .OpenBracket() - .Where(kFieldNameTemp, CondEq, "") - .Not() - .OpenBracket() - .Where(kFieldNameIsDeleted, CondEq, true) - .CloseBracket() - .Or() - .Where(kFieldNameYear, CondGt, 2001) - .CloseBracket() - .CloseBracket() - .CloseBracket() - .CloseBracket() - .Sort(sortIdx, sortOrder) - .Distinct(distinct)); - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameGenre, CondEq, 5) - .Or() - .OpenBracket() - .Where(kFieldNameGenre, CondEq, 4) - .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) - .CloseBracket() - .Not() - .Where(kFieldNameYear, CondRange, {2001, 2010}) - .OpenBracket() - .Where(kFieldNameRate, CondRange, - {static_cast(rand() % 100) / 10, static_cast(rand() % 100) / 10}) - .Or() - .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) - .Not() - .OpenBracket() - .OpenBracket() - .OpenBracket() - .Where(kFieldNameAge, CondSet, RandIntVector(10, 0, 50)) - .Or() - .Where(kFieldNameId, CondEq, rand() % 5000) - .Where(kFieldNameTemp, CondEq, "") - .CloseBracket() - .Or() - .OpenBracket() - .Where(kFieldNameTemp, CondEq, "") - .OpenBracket() - .Not() - .Where(kFieldNameIsDeleted, CondEq, true) - .CloseBracket() - .Or() - .Where(kFieldNameYear, CondGt, 2001) - .CloseBracket() - .CloseBracket() - .CloseBracket() - .CloseBracket() - .Sort(sortIdx, sortOrder) - .Distinct(distinct)); - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameGenre, CondEq, 5) - .Or() - .OpenBracket() - .Where(kFieldNameGenre, CondEq, 4) - .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) - .CloseBracket() - .Not() - .Where(kFieldNameYear, CondRange, {2001, 2010}) - .OpenBracket() - .Where(kFieldNameRate, CondRange, - {static_cast(rand() % 100) / 10, static_cast(rand() % 100) / 10}) - .Or() - .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) - .Not() - .OpenBracket() - .OpenBracket() - .OpenBracket() - .Where(kFieldNameAge, CondSet, RandIntVector(10, 0, 50)) - .Or() - .Where(kFieldNameId, CondEq, rand() % 5000) - .Where(kFieldNameTemp, CondEq, "") - .CloseBracket() - .Or() - .OpenBracket() - .Where(kFieldNameTemp, CondEq, "") - .OpenBracket() - .Not() - .Where(kFieldNameIsDeleted, CondEq, true) - .Or() - .Where(kFieldNameYear, CondGt, 2001) - .CloseBracket() - .CloseBracket() - .CloseBracket() - .CloseBracket() - .CloseBracket() - .Sort(sortIdx, sortOrder) - .Distinct(distinct)); - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameGenre, CondEq, 5) - .Or() - .OpenBracket() - .Where(kFieldNameGenre, CondEq, 4) - .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) - .CloseBracket() - .Not() - .Where(kFieldNameYear, CondRange, {2001, 2010}) - .OpenBracket() - .Where(kFieldNameRate, CondRange, - {static_cast(rand() % 100) / 10, static_cast(rand() % 100) / 10}) - .Or() - .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) - .Not() - .OpenBracket() - .OpenBracket() - .OpenBracket() - .Where(kFieldNameAge, CondSet, RandIntVector(10, 0, 50)) - .Or() - .Where(kFieldNameId, CondEq, rand() % 5000) - .Where(kFieldNameTemp, CondEq, "") - .CloseBracket() - .Or() - .OpenBracket() - .Where(kFieldNameTemp, CondEq, "") - .Not() - .OpenBracket() - .Where(kFieldNameIsDeleted, CondEq, true) - .Or() - .Where(kFieldNameYear, CondGt, 2001) - .CloseBracket() - .CloseBracket() - .CloseBracket() - .CloseBracket() - .CloseBracket() - .Sort(sortIdx, sortOrder) - .Distinct(distinct)); - ExecuteAndVerify( - Query(default_namespace) - .Join(InnerJoin, Query(joinNs).Where(kFieldNameYear, CondGt, 2000 + rand() % 210).Limit(rand() % 100)) - .OpenBracket() - .Not() - .On(kFieldNameYear, randCond(), kFieldNameYear) - .Or() - .On(kFieldNameName, CondEq, kFieldNameName) - .Or() - .On(kFieldNameAge, randCond(), kFieldNameAge) - .CloseBracket() - .Distinct(distinct)); - - for (CondType cond : {CondEq, CondSet, CondLt, CondLe, CondGt, CondGe}) { - const bool multyArgCond = cond == CondEq || cond == CondSet; - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameUuid, cond, randUuid()) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameUuid, cond, randStrUuid()) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameUuid, cond, - multyArgCond ? VariantArray::Create(randUuid(), randStrUuid(), randUuid(), - randStrUuid(), randUuid()) - : VariantArray::Create(randUuid())) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameUuidArr, cond, randUuid()) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameUuidArr, cond, randStrUuid()) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameUuidArr, cond, - multyArgCond ? VariantArray::Create(randUuid(), randStrUuid(), randUuid(), - randStrUuid(), randUuid(), randStrUuid()) - : VariantArray::Create(randUuid())) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .WhereComposite(kFieldNameUuid + compositePlus + kFieldNameName, cond, - multyArgCond - ? std::vector{VariantArray::Create(randUuid(), RandString()), - VariantArray::Create(randStrUuid(), RandString()), - VariantArray::Create(randUuid(), RandString()), - VariantArray::Create(randStrUuid(), RandString()), - VariantArray::Create(randUuid(), RandString()), - VariantArray::Create(randStrUuid(), RandString()), - VariantArray::Create(randUuid(), RandString()), - VariantArray::Create(randStrUuid(), RandString()), - VariantArray::Create(randUuid(), RandString()), - VariantArray::Create(randStrUuid(), RandString()), - VariantArray::Create(randUuid(), RandString()), - VariantArray::Create(randStrUuid(), RandString()), - VariantArray::Create(randUuid(), RandString()), - VariantArray::Create(randStrUuid(), RandString()), - VariantArray::Create(randUuid(), RandString()), - VariantArray::Create(randStrUuid(), RandString()), - VariantArray::Create(randUuid(), RandString()), - VariantArray::Create(randStrUuid(), RandString()), - VariantArray::Create(randUuid(), RandString())} - : std::vector{VariantArray::Create(randUuid(), RandString())}) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - } - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameUuid, CondRange, {nilUuid(), randUuid()}) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .Where(kFieldNameUuidArr, CondRange, {nilUuid(), randUuid()}) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - - ExecuteAndVerify(Query(default_namespace) - .WhereComposite(kFieldNameUuid + compositePlus + kFieldNameName, CondRange, - {VariantArray::Create(nilUuid(), RandString()), - VariantArray::Create(randUuid(), RandString())}) - .Distinct(distinct.c_str()) - .Sort(sortIdx, sortOrder)); - } - } - } - } catch (const reindexer::Error& err) { - ASSERT_TRUE(false) << err.what(); - } catch (const std::exception& err) { - ASSERT_TRUE(false) << err.what(); - } catch (...) { - ASSERT_TRUE(false); - } - } static CondType randCond() noexcept { constexpr static CondType conds[]{CondEq, CondSet, CondLt, CondLe, CondGe, CondGt}; return conds[rand() % (sizeof(conds) / sizeof(*conds))]; @@ -1655,13 +859,14 @@ class QueriesApi : public ReindexerApi, public QueriesVerifier { constexpr size_t facetLimit = 10; constexpr size_t facetOffset = 10; - EXPECT_THROW(Query(default_namespace).Aggregate(AggAvg, {}), reindexer::Error); + Query q(default_namespace); + EXPECT_THROW(q.Aggregate(AggAvg, {}), reindexer::Error); - EXPECT_THROW(Query(default_namespace).Aggregate(AggAvg, {kFieldNameYear, kFieldNameName}), reindexer::Error); + EXPECT_THROW(q.Aggregate(AggAvg, {kFieldNameYear, kFieldNameName}), reindexer::Error); - EXPECT_THROW(Query(default_namespace).Aggregate(AggAvg, {kFieldNameYear}, {{kFieldNameYear, true}}), reindexer::Error); + EXPECT_THROW(q.Aggregate(AggAvg, {kFieldNameYear}, {{kFieldNameYear, true}}), reindexer::Error); - EXPECT_THROW(Query(default_namespace).Aggregate(AggAvg, {kFieldNameYear}, {}, 10), reindexer::Error); + EXPECT_THROW(q.Aggregate(AggAvg, {kFieldNameYear}, {}, 10), reindexer::Error); const Query wrongQuery1{Query(default_namespace).Aggregate(AggFacet, {kFieldNameYear}, {{kFieldNameName, true}})}; reindexer::QueryResults wrongQr1; @@ -1762,11 +967,11 @@ class QueriesApi : public ReindexerApi, public QueriesVerifier { checkFacet(testQr.GetAggregationResults()[9].facets, multifieldFacet, "Multifield"); } - void CompareQueryResults(const std::string& serializedQuery, const QueryResults& lhs, const QueryResults& rhs) { + void CompareQueryResults(std::string_view serializedQuery, const QueryResults& lhs, const QueryResults& rhs) { CompareQueryResults(serializedQuery, lhs.ToLocalQr(), rhs.ToLocalQr()); } - void CompareQueryResults(const std::string& serializedQuery, const LocalQueryResults& lhs, const LocalQueryResults& rhs) { + void CompareQueryResults(std::string_view serializedQuery, const LocalQueryResults& lhs, const LocalQueryResults& rhs) { EXPECT_EQ(lhs.Count(), rhs.Count()); if (lhs.Count() == rhs.Count()) { for (size_t i = 0; i < lhs.Count(); ++i) { @@ -1825,234 +1030,12 @@ class QueriesApi : public ReindexerApi, public QueriesVerifier { } } - void checkDslQuery(const std::string& dslQuery, const Query& checkQuery) { - Query parsedQuery; - Error err = parsedQuery.FromJSON(dslQuery); - ASSERT_TRUE(err.ok()) << "Query: " << dslQuery << "; err: " << err.what(); - - QueryResults dslQr; - err = rt.reindexer->Select(parsedQuery, dslQr); - ASSERT_TRUE(err.ok()) << "Query: " << dslQuery << "; err: " << err.what(); - - QueryResults checkQr; - err = rt.reindexer->Select(checkQuery, checkQr); - ASSERT_TRUE(err.ok()) << "Query: " << dslQuery << "; err: " << err.what(); - - CompareQueryResults(dslQuery, dslQr, checkQr); - Verify(checkQr, checkQuery, *rt.reindexer); - } - static std::string toString(double v) { std::ostringstream res; res.precision(std::numeric_limits::digits10 + 1); res << v; return res.str(); } - // Checks that DSL queries works and compares the result with the result of corresponding C++ query - void CheckDslQueries() { - using namespace std::string_literals; - // ---------- - reindexer::Point point{randPoint(10)}; - double distance = randBinDouble(0, 1); - std::string dslQuery = fmt::sprintf( - R"({"namespace":"%s","limit":-1,"offset":0,"req_total":"disabled","explain":false,"type":"select","select_with_rank":false,"select_filter":[],"select_functions":[],"sort":[],"filters":[{"op":"and","cond":"dwithin","field":"%s","value":[[%f, %f], %f]}],"merge_queries":[],"aggregations":[]})", - geomNs, kFieldNamePointLinearRTree, point.X(), point.Y(), distance); - const Query checkQuery1{Query(geomNs).DWithin(kFieldNamePointLinearRTree, point, distance)}; - checkDslQuery(dslQuery, checkQuery1); - - // ---------- - point = randPoint(10); - distance = randBinDouble(0, 1); - dslQuery = fmt::sprintf( - R"({"namespace":"%s","limit":-1,"offset":0,"req_total":"disabled","explain":false,"type":"select","select_with_rank":false,"select_filter":[],"select_functions":[],"sort":[],"filters":[{"op":"and","cond":"dwithin","field":"%s","value":[%f,[%f,%f]]}],"merge_queries":[],"aggregations":[]})", - geomNs, kFieldNamePointLinearRTree, distance, point.X(), point.Y()); - const Query checkQuery2{Query(geomNs).DWithin(kFieldNamePointLinearRTree, point, distance)}; - checkDslQuery(dslQuery, checkQuery2); - - // ---------- - dslQuery = fmt::sprintf( - R"({"namespace":"%s","limit":-1,"offset":0,"req_total":"disabled","explain":false,"type":"select","select_with_rank":false,"select_filter":[],"select_functions":[],"sort":[],"filters":[{"op":"and","cond":"gt","first_field":"%s","second_field":"%s"}],"merge_queries":[],"aggregations":[]})", - default_namespace, kFieldNameStartTime, kFieldNamePackages); - const Query checkQuery3{Query{default_namespace}.WhereBetweenFields(kFieldNameStartTime, CondGt, kFieldNamePackages)}; - checkDslQuery(dslQuery, checkQuery3); - - // ------- - dslQuery = fmt::sprintf( - R"({"namespace":"%s","limit":-1,"offset":0,"req_total":"disabled","explain":false,"type":"select","select_with_rank":false,"select_filter":[],"select_functions":[],"sort":[],"filters":[{"op":"and","cond":"SET","field":"%s","Value":["1", " 10 ", "100 ", " 1000"]}],"merge_queries":[],"aggregations":[]})", - default_namespace, kFieldNameId); - const Query checkQuery4{Query{default_namespace}.Where(kFieldNameId, CondSet, {1, 10, 100, 1000})}; - checkDslQuery(dslQuery, checkQuery4); - } - - void CheckSqlQueries() { - using namespace std::string_literals; - std::string sqlQuery = "SELECT ID, Year, Genre FROM test_namespace WHERE year > '2016' ORDER BY year DESC LIMIT 10000000"; - const Query checkQuery1{Query(default_namespace, 0, 10000000).Where(kFieldNameYear, CondGt, 2016).Sort(kFieldNameYear, true)}; - - QueryResults sqlQr1; - Error err = rt.reindexer->Select(sqlQuery, sqlQr1); - ASSERT_TRUE(err.ok()) << err.what(); - - QueryResults checkQr1; - err = rt.reindexer->Select(checkQuery1, checkQr1); - ASSERT_TRUE(err.ok()) << err.what(); - - CompareQueryResults(sqlQuery, sqlQr1, checkQr1); - Verify(checkQr1, checkQuery1, *rt.reindexer); - - sqlQuery = "SELECT ID, Year, Genre FROM test_namespace WHERE genre IN ('1',2,'3') ORDER BY year DESC LIMIT 10000000"; - const Query checkQuery2{ - Query(default_namespace, 0, 10000000).Where(kFieldNameGenre, CondSet, {1, 2, 3}).Sort(kFieldNameYear, true)}; - - QueryResults sqlQr2; - err = rt.reindexer->Select(sqlQuery, sqlQr2); - ASSERT_TRUE(err.ok()) << err.what(); - - QueryResults checkQr2; - err = rt.reindexer->Select(checkQuery2, checkQr2); - ASSERT_TRUE(err.ok()) << err.what(); - - CompareQueryResults(sqlQuery, sqlQr2, checkQr2); - Verify(checkQr2, checkQuery2, *rt.reindexer); - - const std::string likePattern = RandLikePattern(); - sqlQuery = "SELECT ID, Year, Genre FROM test_namespace WHERE name LIKE '" + likePattern + "' ORDER BY year DESC LIMIT 10000000"; - const Query checkQuery3{ - Query(default_namespace, 0, 10000000).Where(kFieldNameName, CondLike, likePattern).Sort(kFieldNameYear, true)}; - - QueryResults sqlQr3; - err = rt.reindexer->Select(sqlQuery, sqlQr3); - ASSERT_TRUE(err.ok()) << err.what(); - - QueryResults checkQr3; - err = rt.reindexer->Select(checkQuery3, checkQr3); - ASSERT_TRUE(err.ok()) << err.what(); - - CompareQueryResults(sqlQuery, sqlQr3, checkQr3); - Verify(checkQr3, checkQuery3, *rt.reindexer); - - sqlQuery = "SELECT FACET(ID, Year ORDER BY ID DESC ORDER BY Year ASC LIMIT 20 OFFSET 1) FROM test_namespace LIMIT 10000000"; - const Query checkQuery4{ - Query(default_namespace, 0, 10000000) - .Aggregate(AggFacet, {kFieldNameId, kFieldNameYear}, {{kFieldNameId, true}, {kFieldNameYear, false}}, 20, 1)}; - - QueryResults sqlQr4; - err = rt.reindexer->Select(sqlQuery, sqlQr4); - ASSERT_TRUE(err.ok()) << err.what(); - - QueryResults checkQr4; - err = rt.reindexer->Select(checkQuery4, checkQr4); - ASSERT_TRUE(err.ok()) << err.what(); - - CompareQueryResults(sqlQuery, sqlQr4, checkQr4); - Verify(checkQr4, checkQuery4, *rt.reindexer); - - sqlQuery = "SELECT ID FROM test_namespace WHERE name LIKE '" + likePattern + - "' AND (genre IN ('1', '2', '3') AND year > '2016' ) OR age IN ('1', '2', '3', '4') LIMIT 10000000"; - const Query checkQuery5{Query(default_namespace, 0, 10000000) - .Where(kFieldNameName, CondLike, likePattern) - .OpenBracket() - .Where(kFieldNameGenre, CondSet, {1, 2, 3}) - .Where(kFieldNameYear, CondGt, 2016) - .CloseBracket() - .Or() - .Where(kFieldNameAge, CondSet, {1, 2, 3, 4})}; - - QueryResults sqlQr5; - err = rt.reindexer->Select(sqlQuery, sqlQr5); - ASSERT_TRUE(err.ok()) << err.what(); - - QueryResults checkQr5; - err = rt.reindexer->Select(checkQuery5, checkQr5); - ASSERT_TRUE(err.ok()) << err.what(); - - CompareQueryResults(sqlQuery, sqlQr5, checkQr5); - Verify(checkQr5, checkQuery5, *rt.reindexer); - - sqlQuery = fmt::sprintf("SELECT ID FROM test_namespace ORDER BY '%s + %s * 5' DESC LIMIT 10000000", kFieldNameYear, kFieldNameId); - const Query checkQuery6{ - Query(default_namespace, 0, 10000000).Sort(kFieldNameYear + std::string(" + ") + kFieldNameId + " * 5", true)}; - - QueryResults sqlQr6; - err = rt.reindexer->Select(sqlQuery, sqlQr6); - ASSERT_TRUE(err.ok()) << err.what(); - - QueryResults checkQr6; - err = rt.reindexer->Select(checkQuery6, checkQr6); - ASSERT_TRUE(err.ok()) << err.what(); - - CompareQueryResults(sqlQuery, sqlQr6, checkQr6); - Verify(checkQr6, checkQuery6, *rt.reindexer); - - sqlQuery = fmt::sprintf("SELECT ID FROM test_namespace ORDER BY '%s + %s * 5' DESC ORDER BY '2 * %s / (1 + %s)' ASC LIMIT 10000000", - kFieldNameYear, kFieldNameId, kFieldNameGenre, kFieldNameIsDeleted); - const Query checkQuery7{Query(default_namespace, 0, 10000000) - .Sort(kFieldNameYear + std::string(" + ") + kFieldNameId + " * 5", true) - .Sort(std::string("2 * ") + kFieldNameGenre + " / (1 + " + kFieldNameIsDeleted + ')', false)}; - - QueryResults sqlQr7; - err = rt.reindexer->Select(sqlQuery, sqlQr7); - ASSERT_TRUE(err.ok()) << err.what(); - - QueryResults checkQr7; - err = rt.reindexer->Select(checkQuery7, checkQr7); - ASSERT_TRUE(err.ok()) << err.what(); - - CompareQueryResults(sqlQuery, sqlQr7, checkQr7); - Verify(checkQr7, checkQuery7, *rt.reindexer); - - // Checks that SQL queries with DWithin and sort by Distance work and compares the result with the result of corresponding C++ query - reindexer::Point point = randPoint(10); - double distance = randBinDouble(0, 1); - sqlQuery = fmt::sprintf("SELECT * FROM %s WHERE ST_DWithin(%s, %s, %s);", geomNs, kFieldNamePointNonIndex, pointToSQL(point), - toString(distance)); - const Query checkQuery8{Query(geomNs).DWithin(kFieldNamePointNonIndex, point, distance)}; - - QueryResults sqlQr8; - err = rt.reindexer->Select(sqlQuery, sqlQr8); - ASSERT_TRUE(err.ok()) << err.what(); - - QueryResults checkQr8; - err = rt.reindexer->Select(checkQuery8, checkQr8); - ASSERT_TRUE(err.ok()) << err.what(); - - CompareQueryResults(sqlQuery, sqlQr8, checkQr8); - Verify(checkQr8, checkQuery8, *rt.reindexer); - - point = randPoint(10); - distance = randBinDouble(0, 1); - sqlQuery = fmt::sprintf("SELECT * FROM %s WHERE ST_DWithin(%s, %s, %s) ORDER BY 'ST_Distance(%s, %s)';", geomNs, pointToSQL(point), - kFieldNamePointNonIndex, toString(distance), kFieldNamePointLinearRTree, pointToSQL(point, true)); - const Query checkQuery9{ - Query(geomNs) - .DWithin(kFieldNamePointNonIndex, point, distance) - .Sort(std::string("ST_Distance(") + kFieldNamePointLinearRTree + ", " + pointToSQL(point) + ')', false)}; - - QueryResults sqlQr9; - err = rt.reindexer->Select(sqlQuery, sqlQr9); - ASSERT_TRUE(err.ok()) << err.what(); - - QueryResults checkQr9; - err = rt.reindexer->Select(checkQuery9, checkQr9); - ASSERT_TRUE(err.ok()) << err.what(); - - CompareQueryResults(sqlQuery, sqlQr9, checkQr9); - Verify(checkQr9, checkQuery9, *rt.reindexer); - - sqlQuery = fmt::sprintf("SELECT * FROM %s WHERE %s >= %s;", default_namespace, kFieldNameGenre, kFieldNameRate); - const Query checkQuery10{Query(default_namespace).WhereBetweenFields(kFieldNameGenre, CondGe, kFieldNameRate)}; - - QueryResults sqlQr10; - err = rt.reindexer->Select(sqlQuery, sqlQr10); - ASSERT_TRUE(err.ok()) << err.what(); - - QueryResults checkQr10; - err = rt.reindexer->Select(checkQuery10, checkQr10); - ASSERT_TRUE(err.ok()) << err.what(); - - CompareQueryResults(sqlQuery, sqlQr10, checkQr10); - Verify(checkQr10, checkQuery10, *rt.reindexer); - } void CheckCompositeIndexesQueries() { int priceValue = 77777; @@ -2188,7 +1171,13 @@ class QueriesApi : public ReindexerApi, public QueriesVerifier { const char* kFieldNamePages = "pages"; const char* kFieldNamePrice = "price"; const char* kFieldNameUuid = "uuid"; + const char* kFieldNameUuidSparse = "uuid_sparse"; const char* kFieldNameUuidArr = "uuid_arr"; + const char* kFieldNameUuidArrSparse = "uuid_arr_sparse"; + const char* kFieldNameUuidNotIndex = "uuid_not_index"; + const char* kFieldNameUuidNotIndex2 = "uuid_not_index_2"; + const char* kFieldNameUuidNotIndex3 = "uuid_not_index_3"; + const char* kFieldNameRndString = "rndString"; const char* kFieldNameBtreeIdsets = "btree_idsets"; const char* kFieldNamePointQuadraticRTree = "point_quadratic_rtree"; const char* kFieldNamePointLinearRTree = "point_linear_rtree"; @@ -2214,6 +1203,7 @@ class QueriesApi : public ReindexerApi, public QueriesVerifier { const std::string forcedSortOffsetNs = "forced_sort_offset_namespace"; const std::string nsWithObject = "namespace_with_object"; const std::string geomNs = "geom_namespace"; + const std::string uuidNs = "uuid_namespace"; const std::string btreeIdxOptNs = "btree_idx_opt_namespace"; const std::string conditionsNs = "conditions_namespace"; @@ -2231,6 +1221,7 @@ class QueriesApi : public ReindexerApi, public QueriesVerifier { static constexpr size_t forcedSortOffsetNsSize = 1000; static constexpr int forcedSortOffsetMaxValue = 1000; static constexpr size_t geomNsSize = 10000; + static constexpr size_t uuidNsSize = 10000; static constexpr int btreeIdxOptNsSize = 10000; size_t conditionsNsSize = 0; std::vector> forcedSortOffsetValues; diff --git a/cpp_src/gtests/tests/fixtures/queries_verifier.h b/cpp_src/gtests/tests/fixtures/queries_verifier.h index 4dbb2435e..dc68e83df 100644 --- a/cpp_src/gtests/tests/fixtures/queries_verifier.h +++ b/cpp_src/gtests/tests/fixtures/queries_verifier.h @@ -26,10 +26,13 @@ class QueriesVerifier : public virtual ::testing::Test { std::string name; reindexer::KeyValueType type; }; - void Verify(const reindexer::QueryResults& qr, const reindexer::Query& query, reindexer::Reindexer& rx) { - Verify(qr.ToLocalQr(), query, rx); + using IndexesData = std::unordered_map>; + + void Verify(const reindexer::QueryResults& qr, reindexer::Query&& q, reindexer::Reindexer& rx) { + Verify(qr.ToLocalQr(), std::move(q), rx); } - void Verify(const reindexer::LocalQueryResults& qr, const reindexer::Query& query, reindexer::Reindexer& rx) { + void Verify(const reindexer::LocalQueryResults& qr, reindexer::Query&& q, reindexer::Reindexer& rx) { + auto query = std::move(q); std::unordered_set, PkHash> pks; std::unordered_map> distincts; QueryWatcher watcher{query}; @@ -37,20 +40,88 @@ class QueriesVerifier : public virtual ::testing::Test { reindexer::VariantArray lastSortedColumnValues; lastSortedColumnValues.resize(query.sortingEntries_.size()); + for (size_t i = 0; i < query.Entries().Size(); ++i) { + query.Entries().InvokeAppropriate( + i, + reindexer::Skip{}, + [&](const reindexer::SubQueryEntry& sqe) { + auto subQuery = query.GetSubQuery(sqe.QueryIndex()); + if (sqe.Condition() == CondAny || sqe.Condition() == CondEmpty) { + subQuery.Limit(1); + } + reindexer::QueryResults qr; + const auto err = rx.Select(subQuery, qr); + ASSERT_TRUE(err.ok()) << err.what(); + bool res = false; + if (sqe.Condition() == CondAny || sqe.Condition() == CondEmpty) { + res = ((qr.Count() != 0) == (sqe.Condition() == CondAny)); + } else if (qr.GetAggregationResults().empty()) { + assert(!subQuery.SelectFilters().empty()); + reindexer::QueryEntry qe{subQuery.SelectFilters()[0], sqe.Condition(), reindexer::VariantArray(sqe.Values())}; + const auto& indexesFields = indexesFields_[subQuery.NsName()]; + for (auto it : qr) { + ASSERT_TRUE(it.Status().ok()) << it.Status().what(); + if (checkCondition(it.GetItem(), qe, indexesFields)) { + res = true; + break; + } + } + } else { + const auto aggRes = qr.GetAggregationResults()[0].GetValue(); + if (aggRes) { + res = compareValue(reindexer::Variant(*aggRes), sqe.Condition(), sqe.Values(), CollateOpts(), + reindexer::KeyValueType::Double{}); + } + } + if (res) { + query.SetEntry(i); + } else { + query.SetEntry(i); + } + }, + [&](const reindexer::SubQueryFieldEntry& sqe) { + auto& subQuery = query.GetSubQuery(sqe.QueryIndex()); + reindexer::QueryResults qr; + const auto err = rx.Select(subQuery, qr); + ASSERT_TRUE(err.ok()) << err.what(); + reindexer::VariantArray values; + if (qr.GetAggregationResults().empty()) { + ASSERT_FALSE(subQuery.SelectFilters().empty()); + const auto& indexesFields = indexesFields_[subQuery.NsName()]; + if (isIndexComposite(subQuery.SelectFilters()[0], indexesFields)) { + const auto fields = getCompositeFields(subQuery.SelectFilters()[0], indexesFields); + for (auto it : qr) { + ASSERT_TRUE(it.Status().ok()) << it.Status().what(); + values.emplace_back(getValues(it.GetItem(), fields)); + } + } else { + for (auto it : qr) { + ASSERT_TRUE(it.Status().ok()) << it.Status().what(); + values.emplace_back(it.GetItem()[subQuery.SelectFilters()[0]]); + } + } + } else { + ASSERT_TRUE(qr.GetAggregationResults()[0].GetValue().has_value()); + values.emplace_back(*qr.GetAggregationResults()[0].GetValue()); + } + query.SetEntry(i, sqe.FieldName(), sqe.Condition(), std::move(values)); + }); + } auto joinedSelectors = getJoinedSelectors(query); for (auto& js : joinedSelectors) { const reindexer::Error err = rx.Select(js.JoinQuery(), js.QueryResults()); ASSERT_TRUE(err.ok()) << err.what(); - Verify(js.QueryResults().ToLocalQr(), js.JoinQuery(), rx); + Verify(js.QueryResults().ToLocalQr(), reindexer::Query(static_cast(js.JoinQuery())), rx); } - const auto& indexesFields = indexesFields_[query._namespace]; + const auto& indexesFields = indexesFields_[query.NsName()]; for (size_t i = 0; i < qr.Count(); ++i) { reindexer::Item itemr(qr[i].GetItem(false)); - auto pk = getPk(itemr, query._namespace); + auto pk = getPk(itemr, query.NsName()); EXPECT_TRUE(pks.insert(pk).second) << "Duplicated primary key: " + getPkString(pk); - InsertedItemsByPk& insertedItemsByPk = insertedItems_[query._namespace]; + InsertedItemsByPk& insertedItemsByPk = insertedItems_[query.NsName()]; auto itInsertedItem = insertedItemsByPk.find(pk); EXPECT_NE(itInsertedItem, insertedItemsByPk.end()) << "Item with such PK has not been inserted yet: " + getPkString(pk); if (itInsertedItem != insertedItemsByPk.end()) { @@ -63,7 +134,8 @@ class QueriesVerifier : public virtual ::testing::Test { << "explain: " << qr.GetExplainResults(); } - bool conditionsSatisfied = checkConditions(itemr, query.entries.cbegin(), query.entries.cend(), joinedSelectors, indexesFields); + bool conditionsSatisfied = + checkConditions(itemr, query.Entries().cbegin(), query.Entries().cend(), joinedSelectors, indexesFields); if (!conditionsSatisfied) { std::stringstream ss; ss << "Item doesn't match conditions: " << itemr.GetJSON() << std::endl; @@ -80,7 +152,7 @@ class QueriesVerifier : public virtual ::testing::Test { ss << "explain: " << qr.GetExplainResults(); EXPECT_TRUE(conditionsSatisfied) << ss.str(); TEST_COUT << query.GetSQL() << std::endl; - printFailedQueryEntries(query.entries, joinedSelectors); + printFailedQueryEntries(query.Entries(), joinedSelectors, query.GetSubQueries()); } EXPECT_TRUE(checkDistincts(itemr, query, distincts, indexesFields)) << "Distinction check failed"; @@ -145,17 +217,17 @@ class QueriesVerifier : public virtual ::testing::Test { // Check non found items, to not match conditions // If query has limit and offset, skip verification - if (query.start != 0 || query.count != reindexer::QueryEntry::kDefaultLimit) return; + if (query.HasOffset() || query.HasLimit()) return; // If query has distinct, skip verification for (const auto& agg : query.aggregations_) { if (agg.Type() == AggDistinct) return; } - for (auto& insertedItem : insertedItems_[query._namespace]) { + for (auto& insertedItem : insertedItems_[query.NsName()]) { if (pks.find(insertedItem.first) != pks.end()) continue; bool conditionsSatisfied = - checkConditions(insertedItem.second, query.entries.cbegin(), query.entries.cend(), joinedSelectors, indexesFields); + checkConditions(insertedItem.second, query.Entries().cbegin(), query.Entries().cend(), joinedSelectors, indexesFields); EXPECT_FALSE(conditionsSatisfied) << "Item match conditions (found " << qr.Count() << " items), but not found: " << insertedItem.second.GetJSON() << std::endl @@ -164,7 +236,7 @@ class QueriesVerifier : public virtual ::testing::Test { } auto aggResults = qr.GetAggregationResults(); - if (query.calcTotal != ModeNoTotal) { + if (query.HasCalcTotal()) { // calcTotal from version 3.0.2 also return total count in aggregations, so we have remove it from here for // clean compare aggresults with aggregations aggResults.pop_back(); @@ -235,13 +307,15 @@ class QueriesVerifier : public virtual ::testing::Test { private: bool checkConditions(const reindexer::Item& item, reindexer::QueryEntries::const_iterator it, reindexer::QueryEntries::const_iterator to, const std::vector& joinedSelectors, - const std::unordered_map>& indexesFields) { + const IndexesData& indexesFields) { bool result = true; for (; it != to; ++it) { OpType op = it->operation; if (op != OpOr && !result) return false; bool skip = false; bool const iterationResult = it->InvokeAppropriate( + [](const reindexer::SubQueryEntry&) -> bool { assertrx(0); }, + [](const reindexer::SubQueryFieldEntry&) -> bool { assertrx(0); }, [&](const reindexer::QueryEntriesBracket&) { if (op == OpOr && result && !containsJoins(it.cbegin(), it.cend())) { skip = true; @@ -250,7 +324,7 @@ class QueriesVerifier : public virtual ::testing::Test { return checkConditions(item, it.cbegin(), it.cend(), joinedSelectors, indexesFields); }, [&](const reindexer::QueryEntry& qe) { - if ((op == OpOr && result) || qe.distinct) { + if ((op == OpOr && result) || qe.Distinct()) { skip = true; return false; } @@ -273,7 +347,7 @@ class QueriesVerifier : public virtual ::testing::Test { } return checkCondition(item, qe, indexesFields); }, - [](const reindexer::AlwaysFalse&) { return false; }); + [](const reindexer::AlwaysFalse&) noexcept { return false; }, [](const reindexer::AlwaysTrue&) noexcept { return true; }); if (skip) continue; switch (op) { case OpNot: @@ -290,8 +364,7 @@ class QueriesVerifier : public virtual ::testing::Test { return result; } - static std::string getFieldName(const std::string& indexName, - const std::unordered_map>& indexesFields) { + static std::string getFieldName(const std::string& indexName, const IndexesData& indexesFields) { if (const auto it = indexesFields.find(indexName); it == indexesFields.end()) { return indexName; } else { @@ -303,15 +376,15 @@ class QueriesVerifier : public virtual ::testing::Test { static bool checkDistincts(reindexer::Item& item, const reindexer::Query& qr, std::unordered_map>& distincts, - const std::unordered_map>& indexesFields) { + const IndexesData& indexesFields) { bool result = true; // check only on root level - for (auto it = qr.entries.cbegin(); it != qr.entries.cend(); ++it) { - if (!it->HoldsOrReferTo()) continue; + for (auto it = qr.Entries().cbegin(); it != qr.Entries().cend(); ++it) { + if (!it->Is()) continue; const reindexer::QueryEntry& qentry = it->Value(); - if (!qentry.distinct) continue; + if (!qentry.Distinct()) continue; - const std::string fieldName = getFieldName(qentry.index, indexesFields); + const std::string fieldName = getFieldName(qentry.FieldName(), indexesFields); reindexer::VariantArray fieldValue = item[fieldName]; EXPECT_EQ(fieldValue.size(), 1) << "Distinct field's size cannot be > 1"; if (fieldValue.empty()) return false; @@ -319,30 +392,30 @@ class QueriesVerifier : public virtual ::testing::Test { std::unordered_set& values = distincts[fieldName]; reindexer::Variant keyValue(fieldValue[0]); bool inserted = values.insert(keyValue.As()).second; - EXPECT_TRUE(inserted) << "Duplicate distinct item for index: " << keyValue.As() << ", " << qentry.idxNo; + EXPECT_TRUE(inserted) << "Duplicate distinct item for index: " << keyValue.As() << ", " << qentry.FieldName() + << " (" << qentry.IndexNo() << ')'; result &= inserted; } return result; } - bool checkCondition(const reindexer::Item& item, const JoinedSelectorMock& joinedSelector, - const std::unordered_map>& leftIndexesFields, - const std::unordered_map>& rightIndexesFields) { + bool checkCondition(const reindexer::Item& item, const JoinedSelectorMock& joinedSelector, const IndexesData& leftIndexesFields, + const IndexesData& rightIndexesFields) { for (auto it : joinedSelector.QueryResults()) { const reindexer::Item& rightItem = it.GetItem(false); bool result = true; const auto& joinEntries{joinedSelector.JoinQuery().joinEntries_}; assertrx(!joinEntries.empty()); - assertrx(joinEntries[0].op_ != OpOr); + assertrx(joinEntries[0].Operation() != OpOr); for (const auto& je : joinEntries) { - if (je.op_ == OpOr) { + if (je.Operation() == OpOr) { if (result) continue; } else if (!result) { break; } - const bool curResult = - checkOnCondition(item, rightItem, je.index_, je.joinIndex_, je.condition_, leftIndexesFields, rightIndexesFields); - switch (je.op_) { + const bool curResult = checkOnCondition(item, rightItem, je.LeftFieldName(), je.RightFieldName(), je.Condition(), + leftIndexesFields, rightIndexesFields); + switch (je.Operation()) { case OpAnd: result = curResult; break; @@ -362,9 +435,8 @@ class QueriesVerifier : public virtual ::testing::Test { } bool checkOnCondition(const reindexer::Item& leftItem, const reindexer::Item& rightItem, const std::string& leftIndexName, - const std::string& rightIndexName, CondType cond, - const std::unordered_map>& leftIndexesFields, - const std::unordered_map>& rightIndexesFields) { + const std::string& rightIndexName, CondType cond, const IndexesData& leftIndexesFields, + const IndexesData& rightIndexesFields) { const CollateOpts& collate = indexesCollates[leftIndexName]; const std::string leftFieldName = getFieldName(leftIndexName, leftIndexesFields); const std::string rightFieldName = getFieldName(rightIndexName, rightIndexesFields); @@ -378,21 +450,20 @@ class QueriesVerifier : public virtual ::testing::Test { return false; } - bool checkCondition(const reindexer::Item& item, const reindexer::QueryEntry& qentry, - const std::unordered_map>& indexesFields) { + bool checkCondition(const reindexer::Item& item, const reindexer::QueryEntry& qentry, const IndexesData& indexesFields) { EXPECT_GT(item.NumFields(), 0); - if (isGeomConditions(qentry.condition)) { + if (isGeomConditions(qentry.Condition())) { return checkGeomConditions(item, qentry, indexesFields); } - const CollateOpts& collate = indexesCollates[qentry.index]; + const CollateOpts& collate = indexesCollates[qentry.FieldName()]; - if (isIndexComposite(item, qentry)) { - return checkCompositeValues(item, qentry, collate, indexesFields); + if (isIndexComposite(qentry.FieldName(), indexesFields)) { + return checkCompositeCondition(item, qentry, collate, indexesFields); } else { std::string fieldName; reindexer::KeyValueType fieldType = reindexer::KeyValueType::Undefined{}; - if (const auto it = indexesFields.find(qentry.index); it == indexesFields.end()) { - fieldName = qentry.index; + if (const auto it = indexesFields.find(qentry.FieldName()); it == indexesFields.end()) { + fieldName = qentry.FieldName(); } else { EXPECT_EQ(it->second.size(), 1); assertrx(!it->second.empty()); @@ -400,13 +471,13 @@ class QueriesVerifier : public virtual ::testing::Test { fieldType = it->second[0].type; } reindexer::VariantArray fieldValues = item[fieldName]; - switch (qentry.condition) { + switch (qentry.Condition()) { case CondEmpty: return fieldValues.size() == 0; case CondAny: return fieldValues.size() > 0; case CondAllSet: - return checkAllSet(fieldValues, qentry.values, collate, fieldType); + return checkAllSet(fieldValues, qentry.Values(), collate, fieldType); case CondEq: case CondLt: case CondLe: @@ -417,7 +488,9 @@ class QueriesVerifier : public virtual ::testing::Test { case CondLike: case CondDWithin: for (const reindexer::Variant& fieldValue : fieldValues) { - if (compareValue(fieldValue, qentry.condition, qentry.values, collate, fieldType)) return true; + if (compareValue(fieldValue, qentry.Condition(), qentry.Values(), collate, fieldType)) { + return true; + } } } } @@ -427,16 +500,15 @@ class QueriesVerifier : public virtual ::testing::Test { static bool isGeomConditions(CondType cond) noexcept { return cond == CondType::CondDWithin; } - static bool checkGeomConditions(const reindexer::Item& item, const reindexer::QueryEntry& qentry, - const std::unordered_map>& indexesFields) { - assertrx(qentry.values.size() == 2); - const reindexer::VariantArray coordinates = item[getFieldName(qentry.index, indexesFields)]; + static bool checkGeomConditions(const reindexer::Item& item, const reindexer::QueryEntry& qentry, const IndexesData& indexesFields) { + assertrx(qentry.Values().size() == 2); + const reindexer::VariantArray coordinates = item[getFieldName(qentry.FieldName(), indexesFields)]; if (coordinates.empty()) return false; assertrx(coordinates.size() == 2); const double x = coordinates[0].As(); const double y = coordinates[1].As(); - if (qentry.condition == CondDWithin) { - return DWithin(reindexer::Point{x, y}, qentry.values[0].As(), qentry.values[1].As()); + if (qentry.Condition() == CondDWithin) { + return DWithin(reindexer::Point{x, y}, qentry.Values()[0].As(), qentry.Values()[1].As()); } else { assertrx(0); abort(); @@ -451,25 +523,24 @@ class QueriesVerifier : public virtual ::testing::Test { return kvalues; } - static std::vector getCompositeFields(const std::string& indexName, - const std::unordered_map>& indexesFields) { + static const std::vector& getCompositeFields(const std::string& indexName, const IndexesData& indexesFields) { const auto it = indexesFields.find(indexName); assert(it != indexesFields.end()); return it->second; } - static bool checkCompositeValues(const reindexer::Item& item, const reindexer::QueryEntry& qentry, const CollateOpts& opts, - const std::unordered_map>& indexesFields) { - const auto fields = getCompositeFields(qentry.index, indexesFields); + static bool checkCompositeCondition(const reindexer::Item& item, const reindexer::QueryEntry& qentry, const CollateOpts& opts, + const IndexesData& indexesFields) { + const auto fields = getCompositeFields(qentry.FieldName(), indexesFields); const reindexer::VariantArray& indexesValues = getValues(item, fields); - const reindexer::VariantArray& keyValues = qentry.values; + const reindexer::VariantArray& keyValues = qentry.Values(); - switch (qentry.condition) { + switch (qentry.Condition()) { case CondEmpty: - return indexesValues.size() == 0; + return indexesValues.empty(); case CondAny: - return indexesValues.size() > 0; + return !indexesValues.empty(); case CondGe: assert(!keyValues.empty()); return compareCompositeValues(indexesValues, keyValues[0], opts) >= 0; @@ -493,10 +564,10 @@ class QueriesVerifier : public virtual ::testing::Test { } return false; case CondAllSet: - for (const reindexer::Variant& kv : indexesValues) { + for (const reindexer::Variant& kv : keyValues) { if (compareCompositeValues(indexesValues, kv, opts) != 0) return false; } - return !indexesValues.empty(); + return !keyValues.empty(); case CondLike: case CondDWithin: default: @@ -643,51 +714,42 @@ class QueriesVerifier : public virtual ::testing::Test { } bool checkCompositeCondition(const reindexer::Item& item, const reindexer::BetweenFieldsQueryEntry& qentry, - const std::unordered_map>& indexesFields) { - const auto firstFields = getCompositeFields(qentry.firstIndex, indexesFields); - const auto secondFields = getCompositeFields(qentry.secondIndex, indexesFields); + const IndexesData& indexesFields) { + const auto& firstFields = getCompositeFields(qentry.LeftFieldName(), indexesFields); + const auto& secondFields = getCompositeFields(qentry.RightFieldName(), indexesFields); assertrx(firstFields.size() == secondFields.size()); - reindexer::BetweenFieldsQueryEntry qe{qentry}; for (size_t i = 0; i < firstFields.size(); ++i) { - qe.firstIndex = firstFields[i].name; - qe.secondIndex = secondFields[i].name; - if (!checkCondition(item, qe, indexesFields)) return false; + if (!checkCondition(item, + reindexer::BetweenFieldsQueryEntry{std::string{firstFields[i].name}, qentry.Condition(), + std::string{secondFields[i].name}}, + indexesFields)) + return false; } return !firstFields.empty(); } - static bool isIndexComposite(const reindexer::BetweenFieldsQueryEntry& qe, - const std::unordered_map>& indexesFields) { - if (qe.firstIndex.find('+') != std::string::npos || qe.secondIndex.find('+') != std::string::npos) return true; - if (const auto it = indexesFields.find(qe.firstIndex); it != indexesFields.end() && it->second.size() > 1) return true; - if (const auto it = indexesFields.find(qe.secondIndex); it != indexesFields.end() && it->second.size() > 1) return true; - return false; + static bool isIndexComposite(const std::string& indexName, const IndexesData& indexesFields) { + const auto it = indexesFields.find(indexName); + return it != indexesFields.end() && it->second.size() > 1; } - static bool isIndexComposite(const reindexer::Item& item, const reindexer::QueryEntry& qentry) { - if (qentry.values.empty()) return false; - if (qentry.idxNo < 0) { - return qentry.values.size() && (qentry.values[0].Type().Is() || - qentry.values[0].Type().Is()); - } - const auto indexType = item.GetIndexType(qentry.idxNo); - return indexType.Is() || indexType.Is(); + static bool isIndexComposite(const reindexer::BetweenFieldsQueryEntry& qe, const IndexesData& indexesFields) { + return isIndexComposite(qe.LeftFieldName(), indexesFields) || isIndexComposite(qe.RightFieldName(), indexesFields); } - bool checkCondition(const reindexer::Item& item, const reindexer::BetweenFieldsQueryEntry& qentry, - const std::unordered_map>& indexesFields) { + bool checkCondition(const reindexer::Item& item, const reindexer::BetweenFieldsQueryEntry& qentry, const IndexesData& indexesFields) { EXPECT_GT(item.NumFields(), 0); assertrx(!isGeomConditions(qentry.Condition())); - const CollateOpts& collate = indexesCollates[qentry.firstIndex]; + const CollateOpts& collate = indexesCollates[qentry.LeftFieldName()]; if (isIndexComposite(qentry, indexesFields)) { return checkCompositeCondition(item, qentry, indexesFields); } - const std::string firstField = getFieldName(qentry.firstIndex, indexesFields); - const std::string secondField = getFieldName(qentry.secondIndex, indexesFields); + const std::string firstField = getFieldName(qentry.LeftFieldName(), indexesFields); + const std::string secondField = getFieldName(qentry.RightFieldName(), indexesFields); reindexer::VariantArray lValues = item[firstField]; reindexer::VariantArray rValues = item[secondField]; switch (qentry.Condition()) { @@ -801,9 +863,14 @@ class QueriesVerifier : public virtual ::testing::Test { static bool containsJoins(reindexer::QueryEntries::const_iterator it, reindexer::QueryEntries::const_iterator end) noexcept { for (; it != end; ++it) { if (it->InvokeAppropriate( - [&it](const reindexer::QueryEntriesBracket&) { return containsJoins(it.cbegin(), it.cend()); }, - [](const reindexer::JoinQueryEntry&) { return true; }, [](const reindexer::QueryEntry&) { return false; }, - [](const reindexer::BetweenFieldsQueryEntry&) { return false; }, [](const reindexer::AlwaysFalse&) { return false; })) { + [&it](const reindexer::QueryEntriesBracket&) noexcept { return containsJoins(it.cbegin(), it.cend()); }, + [](const reindexer::JoinQueryEntry&) noexcept { return true; }, + [](const reindexer::QueryEntry&) noexcept { return false; }, + [](const reindexer::BetweenFieldsQueryEntry&) noexcept { return false; }, + [](const reindexer::AlwaysFalse&) noexcept { return false; }, + [](const reindexer::AlwaysTrue&) noexcept { return true; }, + [](const reindexer::SubQueryEntry&) noexcept { return false; }, + [](const reindexer::SubQueryFieldEntry&) noexcept { return false; })) { return true; } } @@ -812,10 +879,10 @@ class QueriesVerifier : public virtual ::testing::Test { static std::vector getJoinedSelectors(const reindexer::Query& query) { std::vector result; - result.reserve(query.joinQueries_.size()); - for (auto jq : query.joinQueries_) { - jq.count = reindexer::QueryEntry::kDefaultLimit; - jq.start = reindexer::QueryEntry::kDefaultOffset; + result.reserve(query.GetJoinQueries().size()); + for (auto jq : query.GetJoinQueries()) { + jq.Limit(reindexer::QueryEntry::kDefaultLimit); + jq.Offset(reindexer::QueryEntry::kDefaultOffset); jq.sortingEntries_.clear(); jq.forcedSortOrder_.clear(); result.emplace_back(InnerJoin, std::move(jq)); @@ -856,23 +923,31 @@ class QueriesVerifier : public virtual ::testing::Test { return it->second; } - static void printFailedQueryEntries(const reindexer::QueryEntries& failedEntries, const std::vector& js) { + static void printFailedQueryEntries(const reindexer::QueryEntries& failedEntries, const std::vector& js, + const std::vector& subQueries) { TestCout() << "Failed entries: "; - printQueryEntries(failedEntries.cbegin(), failedEntries.cend(), js); + printQueryEntries(failedEntries.cbegin(), failedEntries.cend(), js, subQueries); TestCout() << std::endl << std::endl; } static void printQueryEntries(reindexer::QueryEntries::const_iterator it, reindexer::QueryEntries::const_iterator to, - const std::vector& js) { + const std::vector& js, const std::vector& subQueries) { TestCout() << "("; for (; it != to; ++it) { TestCout() << (it->operation == OpAnd ? "AND" : (it->operation == OpOr ? "OR" : "NOT")); it->InvokeAppropriate( - [&it, &js](const reindexer::QueryEntriesBracket&) { printQueryEntries(it.cbegin(), it.cend(), js); }, + [&](const reindexer::QueryEntriesBracket&) { printQueryEntries(it.cbegin(), it.cend(), js, subQueries); }, [](const reindexer::QueryEntry& qe) { TestCout() << qe.Dump(); }, [&js](const reindexer::JoinQueryEntry& jqe) { TestCout() << jqe.Dump(js); }, [](const reindexer::BetweenFieldsQueryEntry& qe) { TestCout() << qe.Dump(); }, - [](const reindexer::AlwaysFalse&) { TestCout() << "Always False"; }); + [&subQueries](const reindexer::SubQueryEntry& sqe) { + TestCout() << '(' << subQueries.at(sqe.QueryIndex()).GetSQL() << ") " << sqe.Condition(); + }, + [&subQueries](const reindexer::SubQueryFieldEntry& sqe) { + TestCout() << sqe.FieldName() << ' ' << sqe.Condition() << " (" << subQueries.at(sqe.QueryIndex()).GetSQL() << ')'; + }, + [](const reindexer::AlwaysFalse&) { TestCout() << "Always False"; }, + [](const reindexer::AlwaysTrue&) { TestCout() << "Always True"; }); } TestCout() << ")"; } @@ -918,5 +993,5 @@ class QueriesVerifier : public virtual ::testing::Test { } std::unordered_map> ns2pk_; - std::unordered_map>> indexesFields_; + std::unordered_map indexesFields_; }; diff --git a/cpp_src/gtests/tests/fixtures/reindexer_api.h b/cpp_src/gtests/tests/fixtures/reindexer_api.h index 0cb53d3a7..440702192 100644 --- a/cpp_src/gtests/tests/fixtures/reindexer_api.h +++ b/cpp_src/gtests/tests/fixtures/reindexer_api.h @@ -6,16 +6,12 @@ #include #include -#include "core/keyvalue/key_string.h" #include "core/keyvalue/variant.h" #include "core/query/query.h" #include "core/reindexer.h" #include "reindexertestapi.h" #include "servercontrol.h" #include "tools/errors.h" -#include "tools/serializer.h" -#include "tools/stringstools.h" -#include "vendor/utf8cpp/utf8.h" using reindexer::Error; using reindexer::Variant; diff --git a/cpp_src/gtests/tests/fixtures/replication_api.cc b/cpp_src/gtests/tests/fixtures/replication_api.cc index d9cad0788..be94e655b 100644 --- a/cpp_src/gtests/tests/fixtures/replication_api.cc +++ b/cpp_src/gtests/tests/fixtures/replication_api.cc @@ -6,7 +6,6 @@ #include "tools/fsops.h" #include "vendor/gason/gason.h" -const std::string ReplicationApi::kStoragePath = "/tmp/reindex_repl_test/"; const std::string ReplicationApi::kConfigNs = "#config"; bool ReplicationApi::StopServer(size_t id) { diff --git a/cpp_src/gtests/tests/fixtures/replication_api.h b/cpp_src/gtests/tests/fixtures/replication_api.h index 2b59c6ac3..cabce7453 100644 --- a/cpp_src/gtests/tests/fixtures/replication_api.h +++ b/cpp_src/gtests/tests/fixtures/replication_api.h @@ -23,7 +23,6 @@ const auto kMaxForceSyncCmdTime = std::chrono::seconds(10); class ReplicationApi : public ::testing::Test { public: - static const std::string kStoragePath; static const std::string kConfigNs; void SetUp(); @@ -55,6 +54,7 @@ class ReplicationApi : public ::testing::Test { shared_timed_mutex restartMutex_; private: + const std::string kStoragePath = reindexer::fs::JoinPath(reindexer::fs::GetTempDir(), "reindex_repl_test/"); std::vector svc_; mutable std::mutex m_; }; diff --git a/cpp_src/gtests/tests/fixtures/replication_load_api.h b/cpp_src/gtests/tests/fixtures/replication_load_api.h index 4db2cdeb0..909b0f3b7 100644 --- a/cpp_src/gtests/tests/fixtures/replication_load_api.h +++ b/cpp_src/gtests/tests/fixtures/replication_load_api.h @@ -105,7 +105,7 @@ class ReplicationLoadApi : public ReplicationApi { SCOPED_TRACE("Checking config from file"); auto srv = GetSrv(num); auto curConfig = srv->GetServerConfig(ServerControl::ConfigType::File); - EXPECT_EQ(expConfig, curConfig); + EXPECT_EQ(expConfig, curConfig) << "expConfig:\n" << expConfig.GetJSON() << "\ncurConfig:\n" << curConfig.GetJSON(); } void CheckReplicationConfigNamespace(size_t num, const AsyncReplicationConfigTest &expConfig, std::chrono::seconds awaitTime) { SCOPED_TRACE("Checking config from namespace with timeout"); @@ -117,7 +117,8 @@ class ReplicationLoadApi : public ReplicationApi { } std::this_thread::sleep_for(std::chrono::seconds(1)); } - EXPECT_EQ(expConfig, srv->GetServerConfig(ServerControl::ConfigType::Namespace)); + auto curConfig = srv->GetServerConfig(ServerControl::ConfigType::Namespace); + EXPECT_EQ(expConfig, curConfig) << "config:\n" << expConfig.GetJSON() << "\ncurConfig:\n" << curConfig.GetJSON(); } void CheckReplicationConfigNamespace(size_t num, const AsyncReplicationConfigTest &expConfig) { SCOPED_TRACE("Checking config from namespace"); diff --git a/cpp_src/gtests/tests/fixtures/rpcclient_api.cc b/cpp_src/gtests/tests/fixtures/rpcclient_api.cc index 8113c5648..38bbd9594 100644 --- a/cpp_src/gtests/tests/fixtures/rpcclient_api.cc +++ b/cpp_src/gtests/tests/fixtures/rpcclient_api.cc @@ -4,7 +4,6 @@ #include "tools/stringstools.h" #include "yaml-cpp/yaml.h" -const std::string RPCClientTestApi::kDbPrefix = "/tmp/reindex/rpc_client_test"; const std::string RPCClientTestApi::kDefaultRPCServerAddr = "127.0.0.1:" + std::to_string(RPCClientTestApi::kDefaultRPCPort); void RPCClientTestApi::TestServer::Start(const std::string& addr, Error errOnLogin) { diff --git a/cpp_src/gtests/tests/fixtures/rpcclient_api.h b/cpp_src/gtests/tests/fixtures/rpcclient_api.h index 4d9aaa8c0..e9d441fdd 100644 --- a/cpp_src/gtests/tests/fixtures/rpcclient_api.h +++ b/cpp_src/gtests/tests/fixtures/rpcclient_api.h @@ -5,6 +5,7 @@ #include "client/cororeindexer.h" #include "rpcserver_fake.h" #include "server/server.h" +#include "tools/fsops.h" class RPCClientTestApi : public ::testing::Test { public: @@ -66,7 +67,7 @@ class RPCClientTestApi : public ::testing::Test { void CreateNamespace(reindexer::client::CoroReindexer& rx, std::string_view nsName); void FillData(reindexer::client::CoroReindexer& rx, std::string_view nsName, int from, int count); - static const std::string kDbPrefix; + const std::string kDbPrefix{reindexer::fs::JoinPath(reindexer::fs::GetTempDir(), "reindex/rpc_client_test")}; static const uint16_t kDefaultRPCPort = 25673; static const std::string kDefaultRPCServerAddr; static const uint16_t kDefaultHttpPort = 33333; diff --git a/cpp_src/gtests/tests/fixtures/runtime_indexes_api.h b/cpp_src/gtests/tests/fixtures/runtime_indexes_api.h index f7358208f..bffeb84bc 100644 --- a/cpp_src/gtests/tests/fixtures/runtime_indexes_api.h +++ b/cpp_src/gtests/tests/fixtures/runtime_indexes_api.h @@ -2,7 +2,7 @@ #include "gtests/tools.h" #include "reindexer_api.h" -#include "tools/random.h" +#include "tools/randompoint.h" class RuntimeIndexesApi : public ReindexerApi { public: @@ -32,6 +32,7 @@ class RuntimeIndexesApi : public ReindexerApi { protected: void FillNamespaces(size_t since, size_t till) { + using reindexer::randPoint; for (size_t i = since; i < till; ++i) { int id = static_cast(i); @@ -245,7 +246,7 @@ class RuntimeIndexesApi : public ReindexerApi { std::string indexName = getRuntimeQPointIndexName(indexNumber); for (size_t i = 0; i < 10; ++i) { Item item = NewItem(geom_namespace); - item[indexName] = randPoint(10); + item[indexName] = reindexer::randPoint(10); Upsert(geom_namespace, item); } auto err = Commit(geom_namespace); @@ -256,7 +257,7 @@ class RuntimeIndexesApi : public ReindexerApi { std::string indexName = getRuntimeLPointIndexName(indexNumber); for (size_t i = 0; i < 10; ++i) { Item item = NewItem(geom_namespace); - item[indexName] = randPoint(10); + item[indexName] = reindexer::randPoint(10); Upsert(geom_namespace, item); } auto err = Commit(geom_namespace); @@ -267,7 +268,7 @@ class RuntimeIndexesApi : public ReindexerApi { std::string indexName = getRuntimeGPointIndexName(indexNumber); for (size_t i = 0; i < 10; ++i) { Item item = NewItem(geom_namespace); - item[indexName] = randPoint(10); + item[indexName] = reindexer::randPoint(10); Upsert(geom_namespace, item); } auto err = Commit(geom_namespace); @@ -278,7 +279,7 @@ class RuntimeIndexesApi : public ReindexerApi { std::string indexName = getRuntimeSPointIndexName(indexNumber); for (size_t i = 0; i < 10; ++i) { Item item = NewItem(geom_namespace); - item[indexName] = randPoint(10); + item[indexName] = reindexer::randPoint(10); Upsert(geom_namespace, item); } auto err = Commit(geom_namespace); diff --git a/cpp_src/gtests/tests/fixtures/servercontrol.cc b/cpp_src/gtests/tests/fixtures/servercontrol.cc index df0a45217..1fa9e0cb6 100644 --- a/cpp_src/gtests/tests/fixtures/servercontrol.cc +++ b/cpp_src/gtests/tests/fixtures/servercontrol.cc @@ -159,10 +159,32 @@ void ServerControl::Interface::WriteShardingConfig(const std::string& configYaml void ServerControl::Interface::SetWALSize(int64_t size, std::string_view nsName) { setNamespaceConfigItem(nsName, "wal_size", size); } +void ServerControl::Interface::SetTxAlwaysCopySize(int64_t size, std::string_view nsName) { + setNamespaceConfigItem(nsName, "tx_size_to_always_copy", size); +} + void ServerControl::Interface::SetOptmizationSortWorkers(size_t cnt, std::string_view nsName) { setNamespaceConfigItem(nsName, "optimization_sort_workers", cnt); } +void ServerControl::Interface::EnableAllProfilings() { + constexpr std::string_view kJsonCfgProfiling = R"json({ + "type":"profiling", + "profiling":{ + "queriesperfstats":true, + "queries_threshold_us":0, + "perfstats":true, + "memstats":true + } + })json"; + auto item = api.reindexer->NewItem(kConfigNs); + ASSERT_TRUE(item.Status().ok()) << item.Status().what(); + auto err = item.FromJSON(kJsonCfgProfiling); + ASSERT_TRUE(err.ok()) << err.what(); + err = api.reindexer->Upsert(kConfigNs, item); + ASSERT_TRUE(err.ok()) << err.what(); +} + cluster::ReplicationStats ServerControl::Interface::GetReplicationStats(std::string_view type) { Query qr = Query("#replicationstats").Where("type", CondEq, Variant(type)); BaseApi::QueryResultsType res; diff --git a/cpp_src/gtests/tests/fixtures/servercontrol.h b/cpp_src/gtests/tests/fixtures/servercontrol.h index b7d0badad..5def1d6f7 100644 --- a/cpp_src/gtests/tests/fixtures/servercontrol.h +++ b/cpp_src/gtests/tests/fixtures/servercontrol.h @@ -36,7 +36,7 @@ struct AsyncReplicationConfigTest { if (nsList) { for (const auto& ns : *nsList) arrNode.Put(nullptr, ns); } - }; + } std::string dsn; std::optional nsList; @@ -73,6 +73,12 @@ struct AsyncReplicationConfigTest { } bool operator!=(const AsyncReplicationConfigTest& config) const { return !(this->operator==(config)); } + std::string GetJSON() const { + reindexer::WrSerializer wser; + reindexer::JsonBuilder jb(wser); + GetJSON(jb); + return std::string(wser.Slice()); + } void GetJSON(reindexer::JsonBuilder& jb) const { jb.Put("app_name", appName); jb.Put("server_id", serverId); @@ -94,7 +100,7 @@ struct AsyncReplicationConfigTest { node.GetJSON(obj); } } - }; + } std::string role; std::string mode; @@ -157,7 +163,7 @@ struct ServerControlConfig { class ServerControl { public: const std::string kConfigNs = "#config"; - const std::string kStoragePath = "/tmp/reindex_repl_test/"; + const std::string kStoragePath = reindexer::fs::JoinPath(reindexer::fs::GetTempDir(), "reindex_repl_test"); const unsigned short kDefaultHttpPort = 5555; const size_t kMaxServerStartTimeSec = 20; @@ -221,6 +227,8 @@ class ServerControl { void SetWALSize(int64_t size, std::string_view nsName); // set optimization sort workers count void SetOptmizationSortWorkers(size_t cnt, std::string_view nsName); + void SetTxAlwaysCopySize(int64_t size, std::string_view nsName); + void EnableAllProfilings(); // get replication stats for specified replication type reindexer::cluster::ReplicationStats GetReplicationStats(std::string_view type); diff --git a/cpp_src/gtests/tests/fixtures/sharding_api.h b/cpp_src/gtests/tests/fixtures/sharding_api.h index 5e823b5a7..bb8f369bb 100644 --- a/cpp_src/gtests/tests/fixtures/sharding_api.h +++ b/cpp_src/gtests/tests/fixtures/sharding_api.h @@ -7,14 +7,15 @@ #include "yaml-cpp/yaml.h" struct InitShardingConfig { + using ShardingConfig = reindexer::cluster::ShardingConfig; class Namespace { public: Namespace(std::string n, bool wd = false) : name(std::move(n)), withData(wd) {} std::string name; bool withData; - std::function keyValuesNodeCreation = [](int shard) { - reindexer::cluster::ShardingConfig::Key key; + std::function keyValuesNodeCreation = [](int shard) { + ShardingConfig::Key key; key.values.emplace_back(Variant(std::string("key") + std::to_string(shard))); key.values.emplace_back(Variant(std::string("key") + std::to_string(shard) + "_" + std::to_string(shard))); key.shardId = shard; @@ -40,7 +41,9 @@ struct InitShardingConfig { class ShardingApi : public ReindexerApi { public: + using ShardingConfig = reindexer::cluster::ShardingConfig; static const std::string configTemplate; + enum class ApplyType : bool { Shared, Local }; void Init(InitShardingConfig c = InitShardingConfig()) { std::vector namespaces = std::move(c.additionalNss); @@ -107,6 +110,7 @@ class ShardingApi : public ReindexerApi { config_.proxyConnThreads = 3; config_.proxyConnConcurrency = 8; config_.reconnectTimeout = std::chrono::milliseconds(6000); + config_.sourceId = 999; // Some test value for (size_t idx = startId, node = 0; idx < startId + kNodesInCluster; ++idx, ++node) { YAML::Node replConf; replConf["cluster_id"] = shard; @@ -383,6 +387,8 @@ class ShardingApi : public ReindexerApi { } size_t NodesCount() const { return kShards * kNodesInCluster; } + void MultyThreadApplyConfigTest(ApplyType type); + protected: class CompareShardId; struct Defaults { @@ -390,6 +396,7 @@ class ShardingApi : public ReindexerApi { size_t defaultHttpPort; std::string baseTestsetDbPath; }; + virtual const Defaults& GetDefaults() const { static Defaults def{19000, 20000, fs::JoinPath(fs::GetTempDir(), "rx_test/ShardingBaseApi")}; return def; @@ -411,7 +418,7 @@ class ShardingApi : public ReindexerApi { assert(j < svc_[i].size()); return svc_[i][j].Get(); } - void CheckTransactionErrors(client::Reindexer& rx, std::string_view nsName); + void checkTransactionErrors(client::Reindexer& rx, std::string_view nsName); void runSelectTest(std::string_view nsName); void runUpdateTest(std::string_view nsName); @@ -428,21 +435,24 @@ class ShardingApi : public ReindexerApi { void fillWithDistribData(const std::map>& shardDataDistrib, const std::string& kNsName, const std::string& kFieldId); template - void runLocalSelectTestForRanges(std::string_view nsName, const std::map>& shardDataDistrib); + void runLocalSelectTest(std::string_view nsName, const std::map>& shardDataDistrib); template - void runSelectTestForRanges(std::string_view nsName, const std::map>& shardDataDistrib); - void runTransactionsTestForRanges(std::string_view nsName, const std::map>& shardDataDistrib); + void runSelectTest(std::string_view nsName, const std::map>& shardDataDistrib); + void runTransactionsTest(std::string_view nsName, const std::map>& shardDataDistrib); template - reindexer::cluster::ShardingConfig makeShardingConfigByDistrib(std::string_view nsName, - const std::map>& shardDataDistrib, int shards = 3, - int nodes = 3) const; + ShardingConfig makeShardingConfigByDistrib(std::string_view nsName, const std::map>& shardDataDistrib, int shards = 3, + int nodes = 3) const; + + Error applyNewShardingConfig(const std::shared_ptr& rx, const ShardingConfig& config, ApplyType type, + std::optional sourceId = std::optional()) const; - Error applyNewShardingConfig(const std::shared_ptr& rx, const reindexer::cluster::ShardingConfig& config, - bool locally = false) const; + void checkConfig(const ServerControl::Interface::Ptr& server, const cluster::ShardingConfig& config); + void checkConfigThrow(const ServerControl::Interface::Ptr& server, const cluster::ShardingConfig& config); + int64_t getSourceIdFrom(const ServerControl::Interface::Ptr& server); + std::optional getShardingConfigFrom(reindexer::client::Reindexer& rx); - void MultyThreadApplyConfigTest(bool locally = false); - void CheckConfig(const ServerControl::Interface::Ptr& server, const cluster::ShardingConfig& config); + void changeClusterLeader(int shardId); size_t kShards = 3; size_t kNodesInCluster = 3; @@ -468,6 +478,6 @@ class ShardingApi : public ReindexerApi { const std::string kSparseFieldDataString = "sparse_data_string"; const std::string kSparseIndexDataString = "sparse_data_string_index"; - reindexer::cluster::ShardingConfig config_; + ShardingConfig config_; std::vector> svc_; //[shard][nodeId] }; diff --git a/cpp_src/gtests/tests/fixtures/storage_lazy_load.h b/cpp_src/gtests/tests/fixtures/storage_lazy_load.h index b42842629..d0b3ce983 100644 --- a/cpp_src/gtests/tests/fixtures/storage_lazy_load.h +++ b/cpp_src/gtests/tests/fixtures/storage_lazy_load.h @@ -86,7 +86,10 @@ class DISABLED_StorageLazyLoadApi : public ReindexerApi { ASSERT_TRUE(err.ok()) << err.what(); } - void dropNs() { rt.reindexer->DropNamespace(default_namespace); } + void dropNs() { + const auto err = rt.reindexer->DropNamespace(default_namespace); + ASSERT_TRUE(err.ok()) << err.what(); + } int64_t getItemsCount(bool& storageLoaded) { QueryResults qr; @@ -103,7 +106,7 @@ class DISABLED_StorageLazyLoadApi : public ReindexerApi { const char* kFieldId = "id"; const char* kFieldRandomName = "random_name"; const char* kConfigNamespace = "#config"; - const std::string kStoragePath = "/tmp/reindex/lazy_load_test"; + const std::string kStoragePath = reindexer::fs::JoinPath(reindexer::fs::GetTempDir(), "reindex/lazy_load_test"); const char* jsonConfigTemplate = R"json({ "type":"namespaces", "namespaces":[ diff --git a/cpp_src/gtests/tests/fixtures/systemhelpers.cc b/cpp_src/gtests/tests/fixtures/systemhelpers.cc index dbe3d3ebd..fae4110cb 100644 --- a/cpp_src/gtests/tests/fixtures/systemhelpers.cc +++ b/cpp_src/gtests/tests/fixtures/systemhelpers.cc @@ -34,7 +34,7 @@ pid_t StartProcess(const std::string& program, const std::vector& p if (isMainThread) { // prctl sends signal on thread termination, so this call may lead to unexpected process termination int r = prctl(PR_SET_PDEATHSIG, SIGTERM); if (r == -1) { - perror("prctl error\n"); + perror("prctl error"); exit(1); } } @@ -44,7 +44,7 @@ pid_t StartProcess(const std::string& program, const std::vector& p } int ret = execv(program.c_str(), ¶msPointers[0]); if (ret) { - perror("exec error\n"); + perror("exec error"); exit(1); } } diff --git a/cpp_src/gtests/tests/fixtures/transaction_api.h b/cpp_src/gtests/tests/fixtures/transaction_api.h index 87ff0483b..3643fcb7e 100644 --- a/cpp_src/gtests/tests/fixtures/transaction_api.h +++ b/cpp_src/gtests/tests/fixtures/transaction_api.h @@ -50,9 +50,9 @@ class TransactionApi : public ReindexerApi { int GetItemsCount(Reindexer& reindexer) { QueryResults qr; - Error err = reindexer.Select(Query(default_namespace), qr); + Error err = reindexer.Select(Query(default_namespace).CachedTotal().Limit(0), qr); EXPECT_TRUE(err.ok()) << err.what(); - return qr.Count(); + return qr.TotalCount(); } void SelectData(Reindexer& reindexer, int fromMax, int tillMax) { diff --git a/cpp_src/gtests/tests/fixtures/ttl_index_api.h b/cpp_src/gtests/tests/fixtures/ttl_index_api.h index 9e6768fef..5026ed6e0 100644 --- a/cpp_src/gtests/tests/fixtures/ttl_index_api.h +++ b/cpp_src/gtests/tests/fixtures/ttl_index_api.h @@ -11,7 +11,8 @@ class TtlIndexApi : public ReindexerApi { DefineNamespaceDataset(default_namespace, {IndexDeclaration{kFieldId, "hash", "int", IndexOpts().PK(), 0}, IndexDeclaration{kFieldData, "tree", "int", IndexOpts().Array(), 0}, IndexDeclaration{kFieldData, "tree", "int", IndexOpts().Array(), 0}}); - rt.reindexer->AddIndex(default_namespace, reindexer::IndexDef(kFieldDate, {kFieldDate}, "ttl", "int64", IndexOpts(), 1)); + err = rt.reindexer->AddIndex(default_namespace, reindexer::IndexDef(kFieldDate, {kFieldDate}, "ttl", "int64", IndexOpts(), 1)); + ASSERT_TRUE(err.ok()) << err.what(); AddDataToNs(3000); } diff --git a/cpp_src/gtests/tests/fuzzing/fuzzing.cc b/cpp_src/gtests/tests/fuzzing/fuzzing.cc index 39fbf607f..772fde35b 100644 --- a/cpp_src/gtests/tests/fuzzing/fuzzing.cc +++ b/cpp_src/gtests/tests/fuzzing/fuzzing.cc @@ -1,4 +1,6 @@ #include "fuzzing/fuzzing.h" +#include "args/args.hpp" +#include "fuzzing/index.h" #include "fuzzing/ns.h" #include "fuzzing/query_generator.h" @@ -6,23 +8,25 @@ TEST_F(Fuzzing, BaseTest) { try { const fuzzing::RandomGenerator::ErrFactorType errorFactor{0, 1}; reindexer::WrSerializer ser; - std::unordered_set generatedNames; - fuzzing::RandomGenerator rnd(std::cout, errorFactor); - std::vector namespaces_; + fuzzing::RandomGenerator rnd(errorFactor); + std::vector namespaces; const size_t N = 1; for (size_t i = 0; i < N; ++i) { - namespaces_.emplace_back(rnd.NsName(generatedNames), std::cout, errorFactor); - fuzzing::Ns& ns = namespaces_.back(); + namespaces.emplace_back(rnd.GenerateNsName(), errorFactor); + fuzzing::Ns& ns = namespaces.back(); auto err = rx_.OpenNamespace(ns.GetName()); EXPECT_TRUE(err.ok()) << err.what(); - if (!err.ok()) continue; + if (!err.ok()) { + continue; + } auto& indexes = ns.GetIndexes(); - for (size_t i = 0; i < indexes.size();) { - const auto idxDef = indexes[i].IndexDef(ns.GetRandomGenerator(), ns.GetScheme()); + for (size_t j = 0; j < indexes.size();) { + const fuzzing::Index& idx = indexes[j]; + const auto idxDef = idx.IndexDef(ns.GetRandomGenerator(), ns.GetScheme(), indexes); err = rx_.AddIndex(ns.GetName(), idxDef); EXPECT_TRUE(err.ok()) << err.what(); if (err.ok()) { - ns.AddIndex(indexes[i], !idxDef.opts_.IsDense() && idxDef.opts_.IsSparse() && !idxDef.opts_.IsPK()); + ns.AddIndexToScheme(idx, j); // TODO move to fuzzing::Ns std::vector fields; std::visit(reindexer::overloaded{ [&](const fuzzing::Index::Child& c) { @@ -35,23 +39,22 @@ TEST_F(Fuzzing, BaseTest) { ToKeyValueType(ns.GetScheme().GetFieldType(child.fieldPath))}); } }}, - indexes[i].content); - if (indexes[i].isPk) { + idx.Content()); + if (idx.IsPk()) { setPkFields(ns.GetName(), fields); } - - addIndexFields(ns.GetName(), indexes[i].name, std::move(fields)); - ++i; + addIndexFields(ns.GetName(), idx.Name(), std::move(fields)); + ++j; } else { - indexes.erase(indexes.begin() + i); + indexes.erase(indexes.begin() + j); } } - for (size_t i = 0, s = ns.GetRandomGenerator().RndItemsCount(); i < s; ++i) { + for (size_t j = 0, s = ns.GetRandomGenerator().RndItemsCount(); j < s; ++j) { auto item = rx_.NewItem(ns.GetName()); err = item.Status(); EXPECT_TRUE(err.ok()) << err.what(); if (!err.ok()) continue; - ns.NewItem(ser); + ns.NewItem(ser); // TODO not json err = item.FromJSON(ser.Slice()); EXPECT_TRUE(err.ok()) << err.what() << std::endl << "size: " << ser.Slice().size() << std::endl << ser.Slice(); if (!err.ok()) continue; @@ -61,26 +64,26 @@ TEST_F(Fuzzing, BaseTest) { enum Op : uint8_t { Insert, Upsert, Update, Delete, END = Delete }; switch (rnd.RndWhich()) { case Insert: - err = rx_.Insert(rnd.NsName(ns.GetName(), generatedNames), item); + err = rx_.Insert(rnd.NsName(ns.GetName()), item); if (err.ok() && item.GetID() != -1) { saveItem(std::move(item), ns.GetName()); } break; case Upsert: - err = rx_.Upsert(rnd.NsName(ns.GetName(), generatedNames), item); + err = rx_.Upsert(rnd.NsName(ns.GetName()), item); if (err.ok()) { saveItem(std::move(item), ns.GetName()); } break; case Update: - err = rx_.Update(rnd.NsName(ns.GetName(), generatedNames), item); + err = rx_.Update(rnd.NsName(ns.GetName()), item); if (err.ok() && item.GetID() != -1) { saveItem(std::move(item), ns.GetName()); } break; case Delete: { const auto id = item.GetID(); - err = rx_.Delete(rnd.NsName(ns.GetName(), generatedNames), item); + err = rx_.Delete(rnd.NsName(ns.GetName()), item); if (err.ok() && item.GetID() != id) { deleteItem(item, ns.GetName()); } @@ -94,14 +97,14 @@ TEST_F(Fuzzing, BaseTest) { err = rx_.Select(reindexer::Query(ns.GetName()).ReqTotal(), qr); EXPECT_TRUE(err.ok()) << err.what(); } - fuzzing::QueryGenerator queryGenerator{namespaces_, std::cout, errorFactor}; + fuzzing::QueryGenerator queryGenerator{namespaces, errorFactor}; for (size_t i = 0; i < 100; ++i) { auto query = queryGenerator(); reindexer::QueryResults qr; auto err = rx_.Select(query, qr); EXPECT_TRUE(err.ok()) << err.what(); if (err.ok()) { - Verify(qr.ToLocalQr(), query, rx_); + Verify(qr.ToLocalQr(), std::move(query), rx_); } } } catch (const std::exception& err) { @@ -113,5 +116,40 @@ TEST_F(Fuzzing, BaseTest) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); + + args::ArgumentParser parser("Reindexer fuzzing tests"); + args::HelpFlag help(parser, "help", "show this message", {'h', "help"}); + args::Group progOptions("options"); + args::ValueFlag dbDsn(progOptions, "DSN", + "DSN to 'reindexer'. Can be 'cproto://:/' or 'builtin://'", {'d', "dsn"}, + args::Options::Single | args::Options::Global); + args::ValueFlag output(progOptions, "FILENAME", "A file for saving initial states of random engines", {'s', "save"}, + args::Options::Single | args::Options::Global); + args::ValueFlag input(progOptions, "FILENAME", "A file for initial states of random engines recovery", {'r', "repeat"}, + args::Options::Single | args::Options::Global); + args::GlobalOptions globals(parser, progOptions); + try { + parser.ParseCLI(argc, argv); + } catch (const args::Help&) { + std::cout << parser.Help() << std::endl; + return 1; + } catch (const args::Error& e) { + std::cerr << "ERROR: " << e.what() << std::endl; + std::cout << parser.Help() << std::endl; + return 1; + } + std::string out = args::get(output); + if (!out.empty()) { + fuzzing::RandomGenerator::SetOut(std::move(out)); + } + const std::string in = args::get(input); + if (!in.empty()) { + fuzzing::RandomGenerator::SetIn(in); + } + std::string dsn = args::get(dbDsn); + if (!dsn.empty()) { + Fuzzing::SetDsn(std::move(dsn)); + } + return RUN_ALL_TESTS(); } diff --git a/cpp_src/gtests/tests/mocks/rpcserver_fake.cc b/cpp_src/gtests/tests/mocks/rpcserver_fake.cc index 81f791bd3..5e35e9561 100644 --- a/cpp_src/gtests/tests/mocks/rpcserver_fake.cc +++ b/cpp_src/gtests/tests/mocks/rpcserver_fake.cc @@ -124,8 +124,13 @@ bool RPCServerFake::Start(const std::string &addr, ev::dynamic_loop &loop, Error dispatcher_.Middleware(this, &RPCServerFake::CheckAuth); - listener_.reset(new Listener(loop, cproto::ServerConnection::NewFactory(dispatcher_, false))); - return listener_->Bind(addr); +#ifdef REINDEX_WITH_V3_FOLLOWERS + listener_ = + std::make_unique>(loop, cproto::ServerConnection::NewFactory(dispatcher_, false, 1024 * 1024 * 1024)); +#else // REINDEX_WITH_V3_FOLLOWERS + listener_ = std::make_unique>(loop, cproto::ServerConnection::NewFactory(dispatcher_, false)); +#endif // REINDEX_WITH_V3_FOLLOWERS + return listener_->Bind(addr, socket_domain::tcp); } RPCServerStatus RPCServerFake::Status() const { return state_; } diff --git a/cpp_src/gtests/tests/mocks/rpcserver_fake.h b/cpp_src/gtests/tests/mocks/rpcserver_fake.h index a039e1509..e99ee9585 100644 --- a/cpp_src/gtests/tests/mocks/rpcserver_fake.h +++ b/cpp_src/gtests/tests/mocks/rpcserver_fake.h @@ -19,7 +19,7 @@ struct RPCServerConfig { enum RPCServerStatus { Init, Connected, Stopped }; -struct RPCClientData : public cproto::ClientData { +struct RPCClientData final : public cproto::ClientData { AuthContext auth; int connID; }; diff --git a/cpp_src/gtests/tests/unit/cascade_replication_test.cc b/cpp_src/gtests/tests/unit/cascade_replication_test.cc index ff6e9e3db..7e7c55832 100644 --- a/cpp_src/gtests/tests/unit/cascade_replication_test.cc +++ b/cpp_src/gtests/tests/unit/cascade_replication_test.cc @@ -348,6 +348,105 @@ TEST_F(CascadeReplicationApi, TransactionTest) { } } +TEST_F(CascadeReplicationApi, TransactionCopyPolicyForceSync) { + // Check transactions copy policy after force sync + /* + l + | + 1 + | + 2 + */ + constexpr std::string_view kJsonCfgNss = R"=({ + "namespaces": [ + { + "namespace": "*", + "start_copy_policy_tx_size": 10000, + "copy_policy_multiplier": 5, + "tx_size_to_always_copy": 100000 + }, + { + "namespace": "ns1", + "start_copy_policy_tx_size": 10000, + "copy_policy_multiplier": 5, + "tx_size_to_always_copy": 1 + } + ], + "type": "namespaces" + })="; + constexpr int port = 9999; + const std::string kBaseDbPath(fs::JoinPath(kBaseTestsetDbPath, "TransactionCopyPolicyForceSync")); + const std::string kDbPathMaster(kBaseDbPath + "/test_"); + constexpr int serverId = 5; + constexpr size_t kRows = 100; + const std::string nsName("ns1"); + + std::vector slaveConfiguration = {-1, port, port + 1}; + std::vector nodes; + for (size_t i = 0; i < slaveConfiguration.size(); i++) { + nodes.emplace_back(); + nodes.back().InitServer( + ServerControlConfig(serverId + i, port + i, port + 1000 + i, kDbPathMaster + std::to_string(i), "db", true)); + nodes.back().Get()->EnableAllProfilings(); + } + + // Set tx copy policy for the node '2' to 'always copy' + { + auto item = nodes[2].Get()->api.reindexer->NewItem("#config"); + ASSERT_TRUE(item.Status().ok()) << item.Status().what(); + auto err = item.FromJSON(kJsonCfgNss); + ASSERT_TRUE(err.ok()) << err.what(); + err = nodes[2].Get()->api.reindexer->Upsert("#config", item); + ASSERT_TRUE(err.ok()) << err.what(); + } + + for (size_t i = 0; i < slaveConfiguration.size(); i++) { + if (i == 0) { + nodes[i].Get()->MakeLeader(); + } else { + nodes[i].Get()->MakeFollower(); + nodes[0].Get()->AddFollower(fmt::format("cproto://127.0.0.1:{}/db", nodes[i].Get()->RpcPort())); + } + } + nodes[2].Drop(); + + auto leader = nodes[0].Get(); + TestNamespace1 ns1(leader, nsName); + WaitSync(leader, nodes[1].Get(), nsName); + + // Restart node '2' + nodes[2].InitServer(ServerControlConfig(serverId + 2, port + 2, port + 1000 + 2, kDbPathMaster + std::to_string(2), "db", true)); + nodes[2].Get()->MakeFollower(); + WaitSync(leader, nodes[2].Get(), nsName); + + // Apply tx + auto tr = leader->api.reindexer->NewTransaction(nsName); + for (unsigned int i = 0; i < kRows; i++) { + reindexer::client::Item item = tr.NewItem(); + auto err = item.FromJSON("{\"id\":" + std::to_string(i + kRows * 10) + "}"); + ASSERT_TRUE(err.ok()) << err.what(); + tr.Upsert(std::move(item)); + } + client::QueryResults qr; + auto err = leader->api.reindexer->CommitTransaction(tr, qr); + ASSERT_TRUE(err.ok()) << err.what(); + WaitSync(leader, nodes[2].Get(), nsName); + + // Check copy tx event in the perfstats + qr = client::QueryResults(); + err = nodes[2].Get()->api.reindexer->Select("select * from #perfstats", qr); + ASSERT_TRUE(err.ok()) << err.what(); + ASSERT_EQ(qr.Count(), 1); + WrSerializer ser; + err = qr.begin().GetJSON(ser, false); + ASSERT_TRUE(err.ok()) << err.what(); + gason::JsonParser parser; + auto resJS = parser.Parse(ser.Slice()); + ASSERT_EQ(resJS["transactions"]["total_copy_count"].As(-1), 1) << ser.Slice(); + + for (auto& node : nodes) node.Stop(); +} + TEST_F(CascadeReplicationApi, ForceSync3Node) { // Check force-sync for cascade setup /* diff --git a/cpp_src/gtests/tests/unit/clusterization_async_test.cc b/cpp_src/gtests/tests/unit/clusterization_async_test.cc index dc6573f0c..2074c4d68 100644 --- a/cpp_src/gtests/tests/unit/clusterization_async_test.cc +++ b/cpp_src/gtests/tests/unit/clusterization_async_test.cc @@ -1,5 +1,7 @@ #include "clusterization_async_api.h" +using namespace reindexer; + TEST_F(ClusterizationAsyncApi, AsyncReplicationForClusterNamespaces) { // async1 async2 // \ / @@ -25,8 +27,6 @@ TEST_F(ClusterizationAsyncApi, AsyncReplicationForClusterNamespaces) { std::vector asyncNodes(kClusterSize); for (size_t i = 0; i < kClusterSize; ++i) { const int id = kClusterSize + 1 + i; - auto storagePath = fs::JoinPath(defaults.baseTestsetDbPath, "node/" + std::to_string(id)); - ServerControlConfig scConfig(id, defaults.defaultRpcPort + id, defaults.defaultHttpPort + id, fs::JoinPath(defaults.baseTestsetDbPath, "node/" + std::to_string(id)), "node" + std::to_string(id), true); diff --git a/cpp_src/gtests/tests/unit/clusterization_extras_test.cc b/cpp_src/gtests/tests/unit/clusterization_extras_test.cc index fd01fee3e..8a7ab5579 100644 --- a/cpp_src/gtests/tests/unit/clusterization_extras_test.cc +++ b/cpp_src/gtests/tests/unit/clusterization_extras_test.cc @@ -1,6 +1,8 @@ #include "clusterization_extras_api.h" #include "core/cjson/jsonbuilder.h" +using namespace reindexer; + TEST_F(ClusterizationExtrasApi, SpecifyClusterNamespaceList) { // Check if with specified cluster namespaces list we are able to write into follower's non-cluster namespaces net::ev::dynamic_loop loop; diff --git a/cpp_src/gtests/tests/unit/clusterization_test.cc b/cpp_src/gtests/tests/unit/clusterization_test.cc index 80bb77605..ebe4d1673 100644 --- a/cpp_src/gtests/tests/unit/clusterization_test.cc +++ b/cpp_src/gtests/tests/unit/clusterization_test.cc @@ -4,6 +4,7 @@ #include "client/raftclient.h" #include "clusterization_api.h" #include "core/cjson/jsonbuilder.h" +#include "yaml-cpp/yaml.h" TEST_F(ClusterizationApi, LeaderElections) { // Check leader election on deffirent conditions diff --git a/cpp_src/gtests/tests/unit/clusterproxy_test.cc b/cpp_src/gtests/tests/unit/clusterproxy_test.cc index 89b91c15a..adfce807b 100644 --- a/cpp_src/gtests/tests/unit/clusterproxy_test.cc +++ b/cpp_src/gtests/tests/unit/clusterproxy_test.cc @@ -3,6 +3,8 @@ #include "clusterization_proxy.h" #include "core/cjson/jsonbuilder.h" +using namespace reindexer; + static std::string itemData(int id, std::string_view valueData, std::string_view modifyValueData) { WrSerializer ser; JsonBuilder jb(ser); @@ -89,9 +91,7 @@ TEST_F(ClusterizationProxyApi, Transaction) { tx.Delete(std::move(item)); } // remove items using SQL query - Query qModify; - qModify.FromSQL("delete from " + kNsName + " where id>=10"); - tx.Modify(std::move(qModify)); + tx.Modify(Query::FromSQL("delete from " + kNsName + " where id>=10")); // commit transaction BaseApi::QueryResultsType qrTx; @@ -485,10 +485,9 @@ static void CheckInsertUpsertUpdateDelete(ClusterizationApi::Cluster& cluster, i { // update item - reindexer::Query q; - q.FromSQL("select * from " + kNsName + " where id=" + std::to_string(pk)); BaseApi::QueryResultsType qres(kResultsWithPayloadTypes | kResultsCJson | kResultsWithItemID); - Error err = cluster.GetNode(followerId)->api.reindexer->Select(q, qres); + Error err = + cluster.GetNode(followerId)->api.reindexer->Select("select * from " + kNsName + " where id=" + std::to_string(pk), qres); ASSERT_TRUE(err.ok()) << err.what(); ASSERT_EQ(qres.Count(), 1); auto itsel = qres.begin().GetItem(); @@ -504,10 +503,9 @@ static void CheckInsertUpsertUpdateDelete(ClusterizationApi::Cluster& cluster, i { // delete item - reindexer::Query q; - q.FromSQL("select * from " + kNsName + " where id=" + std::to_string(pk)); BaseApi::QueryResultsType qres(kResultsWithPayloadTypes | kResultsCJson | kResultsWithItemID); - Error err = cluster.GetNode(followerId)->api.reindexer->Select(q, qres); + Error err = + cluster.GetNode(followerId)->api.reindexer->Select("select * from " + kNsName + " where id=" + std::to_string(pk), qres); ASSERT_TRUE(err.ok()) << err.what(); ASSERT_EQ(qres.Count(), 1); auto itsel = qres.begin().GetItem(); @@ -568,10 +566,8 @@ static void CheckInsertUpsertUpdateDeleteItemQR(ClusterizationApi::Cluster& clus { // update item - reindexer::Query q; - q.FromSQL("select * from " + kNsName + " where id=" + std::to_string(pk)); BaseApi::QueryResultsType qres(kResultsWithPayloadTypes | kResultsCJson | kResultsWithItemID); - Error err = cluster.GetNode(followerId)->api.reindexer->Select(q, qres); + Error err = cluster.GetNode(followerId)->api.reindexer->Select(Query(kNsName).Where("id", CondEq, std::to_string(pk)), qres); ASSERT_TRUE(err.ok()) << err.what(); ASSERT_EQ(qres.Count(), 1); auto itsel = qres.begin().GetItem(); @@ -593,10 +589,8 @@ static void CheckInsertUpsertUpdateDeleteItemQR(ClusterizationApi::Cluster& clus { // delete item - reindexer::Query q; - q.FromSQL("select * from " + kNsName + " where id=" + std::to_string(pk)); BaseApi::QueryResultsType qres(kResultsWithPayloadTypes | kResultsCJson | kResultsWithItemID); - Error err = cluster.GetNode(followerId)->api.reindexer->Select(q, qres); + Error err = cluster.GetNode(followerId)->api.reindexer->Select(Query(kNsName).Where("id", CondEq, std::to_string(pk)), qres); ASSERT_TRUE(err.ok()) << err.what(); ASSERT_EQ(qres.Count(), 1); auto itsel = qres.begin().GetItem(); @@ -707,10 +701,8 @@ static void CheckInsertUpsertUpdateItemQRSerial(ClusterizationApi::Cluster& clus { // update item - reindexer::Query q; - q.FromSQL("select * from " + kNsName + " where id=" + std::to_string(pk)); BaseApi::QueryResultsType qres(kResultsWithPayloadTypes | kResultsCJson | kResultsWithItemID); - Error err = cluster.GetNode(followerId)->api.reindexer->Select(q, qres); + Error err = cluster.GetNode(followerId)->api.reindexer->Select(Query(kNsName).Where("id", CondEq, std::to_string(pk)), qres); ASSERT_TRUE(err.ok()) << err.what(); ASSERT_EQ(qres.Count(), 1); auto itsel = qres.begin().GetItem(); @@ -756,10 +748,8 @@ static void CheckInsertUpsertUpdateItemQRSerial(ClusterizationApi::Cluster& clus } { // delete item - reindexer::Query q; - q.FromSQL("select * from " + kNsName + " where id=" + std::to_string(pk)); BaseApi::QueryResultsType qres(kResultsWithPayloadTypes | kResultsCJson | kResultsWithItemID); - Error err = cluster.GetNode(followerId)->api.reindexer->Select(q, qres); + Error err = cluster.GetNode(followerId)->api.reindexer->Select(Query(kNsName).Where("id", CondEq, std::to_string(pk)), qres); ASSERT_TRUE(err.ok()) << err.what(); ASSERT_EQ(qres.Count(), 1); auto itsel = qres.begin().GetItem(); @@ -826,7 +816,7 @@ static void CheckSQL(ClusterizationApi::Cluster& cluster, int followerId, int le // select all (one) items from namespace { BaseApi::QueryResultsType qresSelectTmp; - err = cluster.GetNode(followerId)->api.reindexer->Select("select * from " + kNsName, qresSelectTmp); + err = cluster.GetNode(followerId)->api.reindexer->Select(Query(kNsName), qresSelectTmp); ASSERT_TRUE(err.ok()) << err.what(); ASSERT_TRUE(qresSelectTmp.Count() == 1) << "select count = " << qresSelectTmp.Count(); } @@ -843,7 +833,7 @@ static void CheckSQL(ClusterizationApi::Cluster& cluster, int followerId, int le { // check the correctness of the update BaseApi::QueryResultsType qresSelect; - err = cluster.GetNode(followerId)->api.reindexer->Select("select name from " + kNsName, qresSelect); + err = cluster.GetNode(followerId)->api.reindexer->Select(Query(kNsName), qresSelect); ASSERT_TRUE(err.ok()) << err.what(); ASSERT_TRUE(qresSelect.Count() == 1) << "select count = " << qresSelect.Count(); @@ -932,13 +922,12 @@ TEST_F(ClusterizationProxyApi, DeleteSelect) { cluster.WaitSync(kNsName); auto followerNode = cluster.GetNode(followerId); + const auto selQ = Query(kNsName).Where("id", CondLt, 5); for (int k = 0; k < 10; k++) { // delete rows { - Query qDel; - qDel.FromSQL("select * from " + kNsName + " where id<5"); BaseApi::QueryResultsType delResult; - Error err = followerNode->api.reindexer->Select(qDel, delResult); + Error err = followerNode->api.reindexer->Select(selQ, delResult); ASSERT_EQ(delResult.Count(), 5) << "incorect count for delete"; for (auto& it : delResult) { auto item = it.GetItem(); @@ -948,10 +937,8 @@ TEST_F(ClusterizationProxyApi, DeleteSelect) { } // check delete correctness { - Query qSel; - qSel.FromSQL("select * from " + kNsName + " where id<5"); BaseApi::QueryResultsType selResult; - Error err = followerNode->api.reindexer->Select(qSel, selResult); + Error err = followerNode->api.reindexer->Select(selQ, selResult); ASSERT_TRUE(selResult.Count() == 0) << "incorrect count =" << selResult.Count(); } { diff --git a/cpp_src/gtests/tests/unit/composite_indexes_api.h b/cpp_src/gtests/tests/unit/composite_indexes_api.h index a555bb162..e78f25c55 100644 --- a/cpp_src/gtests/tests/unit/composite_indexes_api.h +++ b/cpp_src/gtests/tests/unit/composite_indexes_api.h @@ -91,16 +91,24 @@ class CompositeIndexesApi : public ReindexerApi { QueryResults qr; auto err = rt.reindexer->Select(query, qr); EXPECT_TRUE(err.ok()) << err.what(); + assert(err.ok()); QueryResults qrSql; auto sqlQuery = query.GetSQL(); err = rt.reindexer->Select(query.GetSQL(), qrSql); EXPECT_TRUE(err.ok()) << err.what(); + assert(err.ok()); EXPECT_EQ(qr.Count(), qrSql.Count()) << "SQL: " << sqlQuery; for (auto it = qr.begin(), itSql = qrSql.begin(); it != qr.end() && itSql != qrSql.end(); ++it, ++itSql) { + EXPECT_TRUE(it.Status().ok()) << it.Status().what(); + assert(it.Status().ok()); reindexer::WrSerializer ser, serSql; - it.GetCJSON(ser); - itSql.GetCJSON(serSql); + err = it.GetCJSON(ser); + EXPECT_TRUE(err.ok()) << err.what(); + assert(err.ok()); + err = itSql.GetCJSON(serSql); + EXPECT_TRUE(err.ok()) << err.what(); + assert(err.ok()); EXPECT_EQ(ser.Slice(), serSql.Slice()) << "SQL: " << sqlQuery; } return qr; diff --git a/cpp_src/gtests/tests/unit/composite_indexes_test.cc b/cpp_src/gtests/tests/unit/composite_indexes_test.cc index 00e0fff51..e754bf7cf 100644 --- a/cpp_src/gtests/tests/unit/composite_indexes_test.cc +++ b/cpp_src/gtests/tests/unit/composite_indexes_test.cc @@ -50,11 +50,13 @@ TEST_F(CompositeIndexesApi, AddIndexWithExistingCompositeIndex) { static void selectAll(reindexer::Reindexer* reindexer, const std::string& ns) { QueryResults qr; Error err = reindexer->Select(Query(ns, 0, 1000, ModeAccurateTotal), qr); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); for (auto it : qr) { + ASSERT_TRUE(it.Status().ok()) << it.Status().what(); reindexer::WrSerializer wrser; - it.GetJSON(wrser, false); + err = it.GetJSON(wrser, false); + ASSERT_TRUE(err.ok()) << err.what(); } } @@ -68,7 +70,7 @@ TEST_F(CompositeIndexesApi, DropTest2) { for (int i = 0; i < 1000; ++i) { Item item = NewItem(test_ns); - EXPECT_TRUE(!!item); + EXPECT_FALSE(!item); EXPECT_TRUE(item.Status().ok()) << item.Status().what(); item["id"] = i + 1; @@ -122,7 +124,7 @@ TEST_F(CompositeIndexesApi, CompositeIndexesSelectTest) { auto qr = execAndCompareQuery( Query(default_namespace).WhereComposite(compositeIndexName.c_str(), CondEq, {{Variant(priceValue), Variant(pagesValue)}})); - EXPECT_TRUE(qr.Count() == 1); + ASSERT_EQ(qr.Count(), 1); Item pricePageRow = qr.begin().GetItem(false); Variant selectedPrice = pricePageRow[kFieldNamePrice]; @@ -133,8 +135,8 @@ TEST_F(CompositeIndexesApi, CompositeIndexesSelectTest) { Item titleNameRow = qr.begin().GetItem(false); Variant selectedTitle = titleNameRow[kFieldNameTitle]; Variant selectedName = titleNameRow[kFieldNameName]; - EXPECT_TRUE(static_cast(selectedTitle)->compare(std::string(titleValue)) == 0); - EXPECT_TRUE(static_cast(selectedName)->compare(std::string(nameValue)) == 0); + EXPECT_EQ(static_cast(selectedTitle)->compare(std::string(titleValue)), 0); + EXPECT_EQ(static_cast(selectedName)->compare(std::string(nameValue)), 0); execAndCompareQuery(Query(default_namespace).WhereComposite(compositeIndexName, CondLt, {{Variant(priceValue), Variant(pagesValue)}})); execAndCompareQuery(Query(default_namespace).WhereComposite(compositeIndexName, CondLe, {{Variant(priceValue), Variant(pagesValue)}})); diff --git a/cpp_src/gtests/tests/unit/dsl_parser_test.cc b/cpp_src/gtests/tests/unit/dsl_parser_test.cc index 0daee4f86..8ca312173 100644 --- a/cpp_src/gtests/tests/unit/dsl_parser_test.cc +++ b/cpp_src/gtests/tests/unit/dsl_parser_test.cc @@ -1,17 +1,20 @@ #include "join_selects_api.h" +static void checkQueryDslParse(const reindexer::Query& q) { + const std::string dsl = q.GetJSON(); + Query parsedQuery; + Error err = parsedQuery.FromJSON(dsl); + ASSERT_TRUE(err.ok()) << err.what() << "\nDSL:\n" << dsl; + ASSERT_EQ(q, parsedQuery) << "DSL:\n" << dsl << "\nOriginal query:\n" << q.GetSQL() << "\nParsed query:\n" << parsedQuery.GetSQL(); +} + TEST_F(JoinSelectsApi, JoinsDSLTest) { Query queryGenres(genres_namespace); Query queryAuthors(authors_namespace); Query queryBooks{Query(books_namespace, 0, 10).Where(price, CondGe, 500)}; queryBooks.OrInnerJoin(genreId_fk, genreid, CondEq, std::move(queryGenres)); queryBooks.LeftJoin(authorid_fk, authorid, CondEq, std::move(queryAuthors)); - - std::string dsl = queryBooks.GetJSON(); - Query testLoadDslQuery; - Error err = testLoadDslQuery.FromJSON(dsl); - ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(queryBooks == testLoadDslQuery); + checkQueryDslParse(queryBooks); } TEST_F(JoinSelectsApi, EqualPositionDSLTest) { @@ -22,12 +25,7 @@ TEST_F(JoinSelectsApi, EqualPositionDSLTest) { query.OpenBracket().Where("f4", CondEq, 4).Where("f5", CondLt, 10); query.AddEqualPosition({"f4", "f5"}); query.CloseBracket(); - - std::string dsl = query.GetJSON(); - Query testLoadDslQuery; - Error err = testLoadDslQuery.FromJSON(dsl); - ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(query == testLoadDslQuery); + checkQueryDslParse(query); } TEST_F(JoinSelectsApi, MergedQueriesDSLTest) { @@ -35,85 +33,43 @@ TEST_F(JoinSelectsApi, MergedQueriesDSLTest) { Query firstMergedQuery{Query(books_namespace, 10, 100).Where(pages, CondLe, 250)}; Query secondMergedQuery{Query(books_namespace, 100, 50).Where(bookid, CondGe, 100)}; - mainBooksQuery.mergeQueries_.emplace_back(Merge, std::move(firstMergedQuery)); - mainBooksQuery.mergeQueries_.emplace_back(Merge, std::move(secondMergedQuery)); - - std::string dsl = mainBooksQuery.GetJSON(); - Query testLoadDslQuery; - Error err = testLoadDslQuery.FromJSON(dsl); - ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(mainBooksQuery == testLoadDslQuery); + mainBooksQuery.Merge(std::move(firstMergedQuery)); + mainBooksQuery.Merge(std::move(secondMergedQuery)); + checkQueryDslParse(mainBooksQuery); } TEST_F(JoinSelectsApi, AggregateFunctonsDSLTest) { Query query{Query(books_namespace, 10, 100).Where(pages, CondGe, 150)}; - query.aggregations_.push_back({AggAvg, {price}}); - query.aggregations_.push_back({AggSum, {pages}}); - query.aggregations_.push_back({AggFacet, {title, pages}, {{{title, true}}}, 100, 10}); - - std::string dsl = query.GetJSON(); - Query testLoadDslQuery; - Error err = testLoadDslQuery.FromJSON(dsl); - ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(query == testLoadDslQuery); + checkQueryDslParse(query); } TEST_F(JoinSelectsApi, SelectFilterDSLTest) { - Query query{Query(books_namespace, 10, 100).Where(pages, CondGe, 150)}; - query.selectFilter_.push_back(price); - query.selectFilter_.push_back(pages); - query.selectFilter_.push_back(title); - - std::string dsl = query.GetJSON(); - Query testLoadDslQuery; - Error err = testLoadDslQuery.FromJSON(dsl); - ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(query == testLoadDslQuery); + Query query{Query(books_namespace, 10, 100).Where(pages, CondGe, 150).Select({price, pages, title})}; + checkQueryDslParse(query); } TEST_F(JoinSelectsApi, SelectFilterInJoinDSLTest) { - Query queryBooks(books_namespace, 0, 10); - queryBooks.selectFilter_.push_back(price); - queryBooks.selectFilter_.push_back(title); + Query queryBooks = Query(books_namespace, 0, 10).Select({price, title}); { - Query queryAuthors(authors_namespace); - queryAuthors.selectFilter_.push_back(authorid); - queryAuthors.selectFilter_.push_back(age); + Query queryAuthors = Query(authors_namespace).Select({authorid, age}); queryBooks.LeftJoin(authorid_fk, authorid, CondEq, std::move(queryAuthors)); } - std::string dsl = queryBooks.GetJSON(); - Query testLoadDslQuery; - Error err = testLoadDslQuery.FromJSON(dsl); - ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_EQ(queryBooks, testLoadDslQuery); + checkQueryDslParse(queryBooks); } TEST_F(JoinSelectsApi, ReqTotalDSLTest) { Query query{Query(books_namespace, 10, 100, ModeNoTotal).Where(pages, CondGe, 150)}; - - std::string dsl1 = query.GetJSON(); - Query testLoadDslQuery1; - Error err = testLoadDslQuery1.FromJSON(dsl1); - ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(query == testLoadDslQuery1); + checkQueryDslParse(query); query.CachedTotal(); - std::string dsl2 = query.GetJSON(); - Query testLoadDslQuery2; - err = testLoadDslQuery2.FromJSON(dsl2); - ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(query == testLoadDslQuery2); + checkQueryDslParse(query); query.ReqTotal(); - std::string dsl3 = query.GetJSON(); - Query testLoadDslQuery3; - err = testLoadDslQuery3.FromJSON(dsl3); - ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_EQ(query, testLoadDslQuery3); + checkQueryDslParse(query); } TEST_F(JoinSelectsApi, SelectFunctionsDSLTest) { @@ -121,22 +77,13 @@ TEST_F(JoinSelectsApi, SelectFunctionsDSLTest) { query.AddFunction("f1()"); query.AddFunction("f2()"); query.AddFunction("f3()"); - - std::string dsl = query.GetJSON(); - Query testLoadDslQuery; - Error err = testLoadDslQuery.FromJSON(dsl); - ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(query == testLoadDslQuery); + checkQueryDslParse(query); } TEST_F(JoinSelectsApi, CompositeValuesDSLTest) { std::string pagesBookidIndex = pages + std::string("+") + bookid; Query query{Query(books_namespace).WhereComposite(pagesBookidIndex.c_str(), CondGe, {{Variant(500), Variant(10)}})}; - std::string dsl = query.GetJSON(); - Query testLoadDslQuery; - Error err = testLoadDslQuery.FromJSON(dsl); - ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(query == testLoadDslQuery); + checkQueryDslParse(query); } TEST_F(JoinSelectsApi, GeneralDSLTest) { @@ -146,19 +93,12 @@ TEST_F(JoinSelectsApi, GeneralDSLTest) { Query innerJoinQuery = queryBooks.InnerJoin(authorid_fk, authorid, CondEq, std::move(queryAuthors)); Query testDslQuery = innerJoinQuery.OrInnerJoin(genreId_fk, genreid, CondEq, std::move(queryGenres)); - testDslQuery.mergeQueries_.emplace_back(Merge, std::move(queryBooks)); - testDslQuery.mergeQueries_.emplace_back(Merge, std::move(innerJoinQuery)); - testDslQuery.selectFilter_.push_back(genreid); - testDslQuery.selectFilter_.push_back(bookid); - testDslQuery.selectFilter_.push_back(authorid_fk); + testDslQuery.Merge(std::move(queryBooks)); + testDslQuery.Merge(std::move(innerJoinQuery)); + testDslQuery.Select({genreid, bookid, authorid_fk}); testDslQuery.AddFunction("f1()"); testDslQuery.AddFunction("f2()"); - testDslQuery.aggregations_.push_back({AggDistinct, {bookid}}); - Query testLoadDslQuery; - const std::string dsl1 = testDslQuery.GetJSON(); - Error err = testLoadDslQuery.FromJSON(dsl1); - EXPECT_TRUE(err.ok()) << err.what(); - EXPECT_TRUE(testDslQuery == testLoadDslQuery); + checkQueryDslParse(testDslQuery); } diff --git a/cpp_src/gtests/tests/unit/equalposition_tests.cc b/cpp_src/gtests/tests/unit/equalposition_tests.cc index a9b9f359a..0a2138349 100644 --- a/cpp_src/gtests/tests/unit/equalposition_tests.cc +++ b/cpp_src/gtests/tests/unit/equalposition_tests.cc @@ -248,15 +248,13 @@ TEST_F(EqualPositionApi, EmptyCompOpErr) { } { QueryResults qr; - Query q; - q.FromSQL("SELECT * FROM ns2 WHERE a1=10 AND a2=20 equal_position(a1, a2)"); + Query q = Query::FromSQL("SELECT * FROM ns2 WHERE a1=10 AND a2=20 equal_position(a1, a2)"); err = rt.reindexer->Select(q, qr); EXPECT_TRUE(err.ok()) << err.what(); } { QueryResults qr; - Query q; - q.FromSQL("SELECT * FROM ns2 WHERE a1 IS NULL AND a2=20 equal_position(a1, a2)"); + Query q = Query::FromSQL("SELECT * FROM ns2 WHERE a1 IS NULL AND a2=20 equal_position(a1, a2)"); err = rt.reindexer->Select(q, qr); EXPECT_TRUE(err.what() == "Condition IN(with empty parameter list), IS NULL, IS EMPTY not allowed for equal position!") << err.what(); @@ -264,8 +262,7 @@ TEST_F(EqualPositionApi, EmptyCompOpErr) { } { QueryResults qr; - Query q; - q.FromSQL("SELECT * FROM ns2 WHERE a1 =10 AND a2 IS EMPTY equal_position(a1, a2)"); + Query q = Query::FromSQL("SELECT * FROM ns2 WHERE a1 =10 AND a2 IS EMPTY equal_position(a1, a2)"); err = rt.reindexer->Select(q, qr); EXPECT_TRUE(err.what() == "Condition IN(with empty parameter list), IS NULL, IS EMPTY not allowed for equal position!") << err.what(); @@ -273,8 +270,7 @@ TEST_F(EqualPositionApi, EmptyCompOpErr) { } { QueryResults qr; - Query q; - q.FromSQL("SELECT * FROM ns2 WHERE a1 IN () AND a2 IS EMPTY equal_position(a1, a2)"); + Query q = Query::FromSQL("SELECT * FROM ns2 WHERE a1 IN () AND a2 IS EMPTY equal_position(a1, a2)"); err = rt.reindexer->Select(q, qr); EXPECT_TRUE(err.what() == "Condition IN(with empty parameter list), IS NULL, IS EMPTY not allowed for equal position!") << err.what(); @@ -302,8 +298,7 @@ TEST_F(EqualPositionApi, SamePositionFromSql) { QueryResults qr; // SQL query contains equal_position() for field 'a1' twice const std::string_view sql = "select * from test_namespace where a1 > 0 and a1 < 10 equal_position(a1, a1)"; - Query q; - q.FromSQL(sql); + Query q = Query::FromSQL(sql); // Make sure processing this query leads to error const Error err = rt.reindexer->Select(q, qr); EXPECT_FALSE(err.ok()); diff --git a/cpp_src/gtests/tests/unit/ft/ft_generic.cc b/cpp_src/gtests/tests/unit/ft/ft_generic.cc index 2a16f7304..393d01895 100644 --- a/cpp_src/gtests/tests/unit/ft/ft_generic.cc +++ b/cpp_src/gtests/tests/unit/ft/ft_generic.cc @@ -113,6 +113,54 @@ TEST_P(FTGenericApi, CompositeSelectWithFields) { } } +TEST_P(FTGenericApi, MergeWithSameNSAndSelectFunctions) { + Init(GetDefaultConfig()); + AddInBothFields("An entity is something|"sv, "| that in exists entity as itself"sv); + AddInBothFields("In law, a legal entity is|"sv, "|an entity that is capable of something bearing legal rights"sv); + AddInBothFields("In politics, entity is used as|"sv, "| term for entity territorial divisions of some countries"sv); + + for (const auto& query : CreateAllPermutatedQueries("", {"*entity", "somethin*"}, "")) { + for (const auto& field : {std::string("ft1"), std::string("ft2")}) { + auto dsl = std::string("@").append(field).append(" ").append(query); + auto qr{reindexer::Query("nm1").Where("ft3", CondEq, dsl)}; + reindexer::QueryResults res; + auto mqr{reindexer::Query("nm1").Where("ft3", CondEq, std::move(dsl))}; + mqr.AddFunction(field + " = snippet(,\"\",3,2,,d)"); + + qr.Merge(std::move(mqr)); + qr.AddFunction(field + " = highlight(,)"); + auto err = rt.reindexer->Select(qr, res); + EXPECT_TRUE(err.ok()) << err.what(); + + std::unordered_set data{"An entity is something|"sv, + "An entity is something|d"sv, + "| that in exists entity as itself"sv, + "In law, a legal entity is|"sv, + "|an entity that is capable of something bearing legal rights"sv, + "an entity tdof something bd"sv, + "al entity id"sv, + "In politics, entity is used as|"sv, + "| term for entity territorial divisions of some countries"sv, + "ts entity ad"sv, + "s, entity id"sv, + "or entity td"sv}; + + rt.PrintQueryResults("nm1", res); + for (auto it : res) { + auto ritem(it.GetItem(false)); + for (auto idx = 1; idx < ritem.NumFields(); idx++) { + auto curField = ritem[idx].Name(); + if (curField != field) continue; + auto it = data.find(ritem[curField].As()); + ASSERT_TRUE(it != data.end()); + data.erase(it); + } + } + EXPECT_TRUE(data.empty()); + } + } +} + TEST_P(FTGenericApi, SelectWithPlus) { Init(GetDefaultConfig()); @@ -534,7 +582,8 @@ TEST_P(FTGenericApi, DeleteTest) { // Delete(data[1].first); // Delete(data[1].first); - Delete(data.find("In law, a legal entity is an entity that is capable of bearing legal rights")->second); + const auto err = Delete(data.find("In law, a legal entity is an entity that is capable of bearing legal rights")->second); + ASSERT_TRUE(err.ok()) << err.what(); res = SimpleSelect("entity"); // for (auto it : res) { @@ -572,7 +621,8 @@ TEST_P(FTGenericApi, RebuildAfterDeletion) { auto res = selectF("entity"); ASSERT_EQ(res.Count(), 3); - Delete(data.find("In law, a legal entity is an entity that is capable of bearing legal rights")->second); + err = Delete(data.find("In law, a legal entity is an entity that is capable of bearing legal rights")->second); + ASSERT_TRUE(err.ok()) << err.what(); res = selectF("entity"); ASSERT_EQ(res.Count(), 2); } diff --git a/cpp_src/gtests/tests/unit/ft/ft_stress.cc b/cpp_src/gtests/tests/unit/ft/ft_stress.cc index f25e5429c..e0a0d35e7 100644 --- a/cpp_src/gtests/tests/unit/ft/ft_stress.cc +++ b/cpp_src/gtests/tests/unit/ft/ft_stress.cc @@ -10,7 +10,9 @@ class FTStressApi : public FTApi { }; TEST_P(FTStressApi, BasicStress) { - Init(GetDefaultConfig()); + const std::string kStorage = reindexer::fs::JoinPath(reindexer::fs::GetTempDir(), "reindex_FTApi/BasicStress"); + reindexer::fs::RmDirAll(kStorage); + Init(GetDefaultConfig(), NS1, kStorage); std::vector data; std::vector phrase; @@ -29,7 +31,8 @@ TEST_P(FTStressApi, BasicStress) { std::thread statsThread([&] { while (!terminate) { reindexer::QueryResults qr; - rt.reindexer->Select(reindexer::Query("#memstats"), qr); + const auto err = rt.reindexer->Select(reindexer::Query("#memstats"), qr); + ASSERT_TRUE(err.ok()) << err.what(); std::this_thread::sleep_for(std::chrono::milliseconds(10)); } }); @@ -89,7 +92,8 @@ TEST_P(FTStressApi, ConcurrencyCheck) { lck.unlock(); while (!terminate) { reindexer::QueryResults qr; - rt.reindexer->Select(reindexer::Query("#memstats"), qr); + const auto err = rt.reindexer->Select(reindexer::Query("#memstats"), qr); + ASSERT_TRUE(err.ok()) << err.what(); } }); } else { diff --git a/cpp_src/gtests/tests/unit/join_test.cc b/cpp_src/gtests/tests/unit/join_test.cc index 5ea3f5c86..d4cebae18 100644 --- a/cpp_src/gtests/tests/unit/join_test.cc +++ b/cpp_src/gtests/tests/unit/join_test.cc @@ -4,6 +4,7 @@ #include #include "core/itemimpl.h" #include "core/nsselecter/joinedselector.h" +#include "core/type_consts_helpers.h" #include "join_on_conditions_api.h" #include "join_selects_api.h" #include "test_helpers.h" @@ -33,8 +34,8 @@ TEST_F(JoinSelectsApi, JoinsAsWhereConditionsTest) { QueryWatcher watcher{queryBooks}; reindexer::QueryResults qr; Error err = rt.reindexer->Select(queryBooks, qr); - EXPECT_TRUE(err.ok()) << err.what(); - EXPECT_TRUE(qr.Count() <= 50); + ASSERT_TRUE(err.ok()) << err.what(); + EXPECT_LE(qr.Count(), 50); CheckJoinsInComplexWhereCondition(qr); } @@ -47,7 +48,7 @@ TEST_F(JoinSelectsApi, JoinsLockWithCache_364) { for (int i = 0; i < 10; ++i) { reindexer::QueryResults qr; Error err = rt.reindexer->Select(queryBooks, qr); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); } } @@ -62,13 +63,12 @@ TEST_F(JoinSelectsApi, JoinsAsWhereConditionsTest2) { "OR INNER JOIN (SELECT *FROM authors_namespace WHERE authorid >= 300 AND authorid <= 400) ON authors_namespace.authorid = " "books_namespace.authorid_fk LIMIT 50"; - Query query; - query.FromSQL(sql); + Query query = Query::FromSQL(sql); QueryWatcher watcher{query}; reindexer::QueryResults qr; Error err = rt.reindexer->Select(query, qr); - EXPECT_TRUE(err.ok()) << err.what(); - EXPECT_TRUE(qr.Count() <= 50); + ASSERT_TRUE(err.ok()) << err.what(); + EXPECT_LE(qr.Count(), 50); CheckJoinsInComplexWhereCondition(qr); } @@ -81,24 +81,23 @@ TEST_F(JoinSelectsApi, SqlParsingTest) { "(authorid >= 10 AND authorid <= 20) limit 100) on " "authors_namespace.authorid = books_namespace.authorid_fk) or pages == 3 limit 20"; - Query srcQuery; - srcQuery.FromSQL(sql); + Query srcQuery = Query::FromSQL(sql); QueryWatcher watcher{srcQuery}; reindexer::WrSerializer wrser; srcQuery.GetSQL(wrser); - Query dstQuery; - dstQuery.FromSQL(wrser.Slice()); + Query dstQuery = Query::FromSQL(wrser.Slice()); - ASSERT_TRUE(srcQuery == dstQuery); + ASSERT_EQ(srcQuery, dstQuery); wrser.Reset(); srcQuery.Serialize(wrser); - Query deserializedQuery; reindexer::Serializer ser(wrser.Buf(), wrser.Len()); - deserializedQuery.Deserialize(ser); - ASSERT_TRUE(srcQuery == deserializedQuery); + Query deserializedQuery = Query::Deserialize(ser); + ASSERT_EQ(srcQuery, deserializedQuery) << "Original query:\n" + << srcQuery.GetSQL() << "\nDeserialized query:\n" + << deserializedQuery.GetSQL(); } TEST_F(JoinSelectsApi, InnerJoinTest) { @@ -109,14 +108,14 @@ TEST_F(JoinSelectsApi, InnerJoinTest) { reindexer::QueryResults joinQueryRes; Error err = rt.reindexer->Select(joinQuery, joinQueryRes); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); err = VerifyResJSON(joinQueryRes); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); reindexer::QueryResults pureSelectRes; err = rt.reindexer->Select(queryBooks, pureSelectRes); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); QueryResultRows joinSelectRows; QueryResultRows pureSelectRows; @@ -129,7 +128,7 @@ TEST_F(JoinSelectsApi, InnerJoinTest) { reindexer::QueryResults authorsSelectRes; Query authorsQuery{Query(authors_namespace).Where(authorid, CondEq, authorIdKeyRef)}; err = rt.reindexer->Select(authorsQuery, authorsSelectRes); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); if (err.ok()) { int bookId = booksItem[bookid].Get(); @@ -152,7 +151,7 @@ TEST_F(JoinSelectsApi, LeftJoinTest) { Query booksQuery{Query(books_namespace).Where(price, CondGe, 500)}; reindexer::QueryResults booksQueryRes; Error err = rt.reindexer->Select(booksQuery, booksQueryRes); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); QueryResultRows pureSelectRows; if (err.ok()) { @@ -169,10 +168,10 @@ TEST_F(JoinSelectsApi, LeftJoinTest) { QueryWatcher watcher{joinQuery}; reindexer::QueryResults joinQueryRes; err = rt.reindexer->Select(joinQuery, joinQueryRes); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); err = VerifyResJSON(joinQueryRes); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); if (err.ok()) { std::unordered_set presentedAuthorIds; @@ -188,7 +187,7 @@ TEST_F(JoinSelectsApi, LeftJoinTest) { for (auto joinedFieldIt = itemIt.begin(); joinedFieldIt != itemIt.end(); ++joinedFieldIt) { reindexer::ItemImpl item2(joinedFieldIt.GetItem(0, joinQueryRes.GetPayloadType(1), joinQueryRes.GetTagsMatcher(1))); Variant authorIdKeyRef2 = item2.GetField(joinQueryRes.GetPayloadType(1).FieldByName(authorid_fk)); - EXPECT_TRUE(authorIdKeyRef1 == authorIdKeyRef2); + EXPECT_EQ(authorIdKeyRef1, authorIdKeyRef2); } presentedAuthorIds.insert(static_cast(authorIdKeyRef1)); @@ -208,15 +207,15 @@ TEST_F(JoinSelectsApi, LeftJoinTest) { int authorId = static_cast(authorIdKeyRef1); auto itAutorid(presentedAuthorIds.find(authorId)); - EXPECT_TRUE(itAutorid != presentedAuthorIds.end()); + EXPECT_NE(itAutorid, presentedAuthorIds.end()); auto itRowidIndex(rowidsIndexes.find(rowid)); - EXPECT_TRUE(itRowidIndex != rowidsIndexes.end()); + EXPECT_NE(itRowidIndex, rowidsIndexes.end()); if (itRowidIndex != rowidsIndexes.end()) { Item item2((joinQueryRes.begin() + rowid).GetItem(false)); Variant authorIdKeyRef2 = item2[authorid]; - EXPECT_TRUE(authorIdKeyRef1 == authorIdKeyRef2); + EXPECT_EQ(authorIdKeyRef1, authorIdKeyRef2); } } } @@ -236,10 +235,10 @@ TEST_F(JoinSelectsApi, OrInnerJoinTest) { reindexer::QueryResults queryRes; Error err = rt.reindexer->Select(orInnerJoinQuery, queryRes); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); err = VerifyResJSON(queryRes); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); if (err.ok()) { for (auto rowIt : queryRes) { @@ -251,7 +250,7 @@ TEST_F(JoinSelectsApi, OrInnerJoinTest) { for (int i = 0; i < authorIdIt.ItemsCount(); ++i) { reindexer::ItemImpl authorsItem(authorIdIt.GetItem(i, queryRes.GetPayloadType(1), queryRes.GetTagsMatcher(1))); Variant authorIdKeyRef2 = authorsItem.GetField(queryRes.GetPayloadType(1).FieldByName(authorid)); - EXPECT_TRUE(authorIdKeyRef1 == authorIdKeyRef2); + EXPECT_EQ(authorIdKeyRef1, authorIdKeyRef2); } reindexer::joins::JoinedFieldIterator genreIdIt = itemIt.at(genresNsJoinIndex); @@ -259,7 +258,7 @@ TEST_F(JoinSelectsApi, OrInnerJoinTest) { for (int i = 0; i < genreIdIt.ItemsCount(); ++i) { reindexer::ItemImpl genresItem = genreIdIt.GetItem(i, queryRes.GetPayloadType(2), queryRes.GetTagsMatcher(2)); Variant genresIdKeyRef2 = genresItem.GetField(queryRes.GetPayloadType(2).FieldByName(genreid)); - EXPECT_TRUE(genresIdKeyRef1 == genresIdKeyRef2); + EXPECT_EQ(genresIdKeyRef1, genresIdKeyRef2); } } } @@ -292,7 +291,7 @@ TEST_F(JoinSelectsApi, JoinTestSorting) { for (auto rowIt : joinQueryRes) { Item item = rowIt.GetItem(false); if (!prevField.Type().Is()) { - ASSERT_TRUE(prevField.Compare(item[age]) <= 0); + ASSERT_LE(prevField.Compare(item[age]), 0); } Variant key = item[authorid]; @@ -304,14 +303,14 @@ TEST_F(JoinSelectsApi, JoinTestSorting) { for (int i = 0; i < joinedFieldIt.ItemsCount(); ++i) { reindexer::ItemImpl joinItem(joinedFieldIt.GetItem(i, joinQueryRes.GetPayloadType(1), joinQueryRes.GetTagsMatcher(1))); Variant fkey = joinItem.GetField(joinQueryRes.GetPayloadType(1).FieldByName(authorid_fk)); - ASSERT_TRUE(key.Compare(fkey) == 0) << key.As() << " " << fkey.As(); + ASSERT_EQ(key.Compare(fkey), 0) << key.As() << " " << fkey.As(); Variant recentJoinedValue = joinItem.GetField(joinQueryRes.GetPayloadType(1).FieldByName(price)); - ASSERT_TRUE(recentJoinedValue.As() >= 200); + ASSERT_GE(recentJoinedValue.As(), 200); if (!prevJoinedValue.Type().Is()) { - ASSERT_TRUE(prevJoinedValue.Compare(recentJoinedValue) >= 0); + ASSERT_GE(prevJoinedValue.Compare(recentJoinedValue), 0); } Variant pagesValue = joinItem.GetField(joinQueryRes.GetPayloadType(1).FieldByName(pages)); - ASSERT_TRUE(pagesValue.As() >= 100); + ASSERT_GE(pagesValue.As(), 100); prevJoinedValue = recentJoinedValue; } prevField = item[age]; @@ -350,7 +349,7 @@ TEST_F(JoinSelectsApi, TestSortingByJoinedNs) { const Variant recentValue = joinItem.GetField(joinQueryRes2.GetPayloadType(1).FieldByName(age)); if (!prevValue.Type().Is()) { reindexer::WrSerializer ser; - ASSERT_TRUE(prevValue.Compare(recentValue) <= 0) << (prevValue.Dump(ser), ser << ' ', recentValue.Dump(ser), ser.Slice()); + ASSERT_LE(prevValue.Compare(recentValue), 0) << (prevValue.Dump(ser), ser << ' ', recentValue.Dump(ser), ser.Slice()); } prevValue = recentValue; } @@ -365,12 +364,12 @@ TEST_F(JoinSelectsApi, JoinTestSelectNonIndexedField) { qr); ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qr.Count() == 1) << err.what(); + ASSERT_EQ(qr.Count(), 1); Item theOnlyItem = qr.begin().GetItem(false); VariantArray krefs = theOnlyItem[title]; - ASSERT_TRUE(krefs.size() == 1); - ASSERT_TRUE(krefs[0].As() == "Crime and Punishment"); + ASSERT_EQ(krefs.size(), 1); + ASSERT_EQ(krefs[0].As(), "Crime and Punishment"); } TEST_F(JoinSelectsApi, JoinByNonIndexedField) { @@ -400,7 +399,7 @@ TEST_F(JoinSelectsApi, JoinByNonIndexedField) { qr); ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qr.Count() == 1) << err.what(); + ASSERT_EQ(qr.Count(), 1); // And backwards even! reindexer::QueryResults qr2; @@ -411,7 +410,7 @@ TEST_F(JoinSelectsApi, JoinByNonIndexedField) { qr2); ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qr2.Count() == 1) << err.what(); + ASSERT_EQ(qr2.Count(), 1); } TEST_F(JoinSelectsApi, JoinsEasyStressTest) { @@ -426,15 +425,15 @@ TEST_F(JoinSelectsApi, JoinsEasyStressTest) { for (size_t i = 0; i < 10; ++i) { reindexer::QueryResults queryRes; Error err = rt.reindexer->Select(orInnerJoinQuery, queryRes); - EXPECT_TRUE(err.ok()) << err.what(); - EXPECT_TRUE(queryRes.Count() > 0); + ASSERT_TRUE(err.ok()) << err.what(); + EXPECT_GT(queryRes.Count(), 0); } }; auto removeTh = [this]() { QueryResults qres; Error err = rt.reindexer->Delete(Query(books_namespace, 0, 10).Where(price, CondGe, 5000), qres); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); }; int32_t since = 0, count = 1000; @@ -493,54 +492,55 @@ TEST_F(JoinSelectsApi, JoinPreResultStoreValuesOptimizationStressTest) { QueryResults qres; while (!start) std::this_thread::sleep_for(std::chrono::milliseconds(1)); Error err = rt.reindexer->Select(q, qres); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); }); } start = true; for (auto& th : threads) th.join(); } -static bool checkForAllowedJsonTags(const std::vector& tags, gason::JsonValue jsonValue) { +static void checkForAllowedJsonTags(const std::vector& tags, gason::JsonValue jsonValue) { size_t count = 0; for (const auto& elem : jsonValue) { - if (std::find(tags.begin(), tags.end(), std::string_view(elem.key)) == tags.end()) { - return false; - } + ASSERT_NE(std::find(tags.begin(), tags.end(), std::string_view(elem.key)), tags.end()); ++count; } - return (count == tags.size()); + ASSERT_EQ(count, tags.size()); } TEST_F(JoinSelectsApi, JoinWithSelectFilter) { - Query queryAuthors(authors_namespace); - queryAuthors.selectFilter_.emplace_back(name); - queryAuthors.selectFilter_.emplace_back(age); + Query queryAuthors = Query(authors_namespace).Select({name, age}); - Query queryBooks{Query(books_namespace).Where(pages, CondGe, 100).InnerJoin(authorid_fk, authorid, CondEq, std::move(queryAuthors))}; - queryBooks.selectFilter_.emplace_back(title); - queryBooks.selectFilter_.emplace_back(price); + Query queryBooks{Query(books_namespace) + .Where(pages, CondGe, 100) + .InnerJoin(authorid_fk, authorid, CondEq, std::move(queryAuthors)) + .Select({title, price})}; QueryResults qr; Error err = rt.reindexer->Select(queryBooks, qr); ASSERT_TRUE(err.ok()) << err.what(); for (auto it : qr) { + ASSERT_TRUE(it.Status().ok()) << it.Status().what(); reindexer::WrSerializer wrser; - it.GetJSON(wrser, false); + err = it.GetJSON(wrser, false); + ASSERT_TRUE(err.ok()) << err.what(); reindexer::joins::ItemIterator joinIt = it.GetJoined(); gason::JsonParser jsonParser; gason::JsonNode root = jsonParser.Parse(reindexer::giftStr(wrser.Slice())); - EXPECT_TRUE(checkForAllowedJsonTags({title, price, "joined_authors_namespace"}, root.value)); + checkForAllowedJsonTags({title, price, "joined_authors_namespace"}, root.value); for (auto fieldIt = joinIt.begin(); fieldIt != joinIt.end(); ++fieldIt) { LocalQueryResults jqr = fieldIt.ToQueryResults(); - jqr.addNSContext(qr.GetPayloadType(1), qr.GetTagsMatcher(1), qr.GetFieldsFilter(1), qr.GetSchema(1)); + jqr.addNSContext(qr, 1, reindexer::lsn_t()); for (auto jit : jqr) { + ASSERT_TRUE(jit.Status().ok()) << jit.Status().what(); wrser.Reset(); - jit.GetJSON(wrser, false); + err = jit.GetJSON(wrser, false); + ASSERT_TRUE(err.ok()) << err.what(); root = jsonParser.Parse(reindexer::giftStr(wrser.Slice())); - EXPECT_TRUE(checkForAllowedJsonTags({name, age}, root.value)); + checkForAllowedJsonTags({name, age}, root.value); } } } @@ -558,7 +558,7 @@ TEST_F(JoinSelectsApi, TestMergeWithJoins) { // Build the 2nd query (with join) with 'authors_namespace' as the main NS. Query queryAuthors = Query(authors_namespace); queryAuthors.LeftJoin(locationid_fk, locationid, CondEq, Query(location_namespace)); - queryBooks.mergeQueries_.emplace_back(Merge, std::move(queryAuthors)); + queryBooks.Merge(std::move(queryAuthors)); // Execute it QueryResults qr; @@ -573,26 +573,26 @@ TEST_F(JoinSelectsApi, TestMergeWithJoins) { for (auto it : qr) { Item item = it.GetItem(false); auto joined = it.GetJoined(); - ASSERT_TRUE(joined.getJoinedFieldsCount() == 1); + ASSERT_EQ(joined.getJoinedFieldsCount(), 1); bool booksItem = (rowId <= 10000); LocalQueryResults jqr = joined.begin().ToQueryResults(); int joinedNs = booksItem ? 2 : 3; - jqr.addNSContext(qr.GetPayloadType(joinedNs), qr.GetTagsMatcher(joinedNs), qr.GetFieldsFilter(joinedNs), qr.GetSchema(joinedNs)); + jqr.addNSContext(qr, joinedNs, reindexer::lsn_t()); if (booksItem) { Variant fkValue = item[authorid_fk]; for (auto jit : jqr) { Item jItem = jit.GetItem(false); Variant value = jItem[authorid]; - ASSERT_TRUE(value == fkValue); + ASSERT_EQ(value, fkValue); } } else { Variant fkValue = item[locationid_fk]; for (auto jit : jqr) { Item jItem = jit.GetItem(false); Variant value = jItem[locationid]; - ASSERT_TRUE(value == fkValue); + ASSERT_EQ(value, fkValue); } } @@ -634,30 +634,28 @@ TEST_F(JoinSelectsApi, TestNestedMergesInMergesError) { TEST_F(JoinOnConditionsApi, TestGeneralConditions) { const std::string sqlTemplate = R"(select * from books_namespace inner join books_namespace on (books_namespace.authorid_fk = books_namespace.authorid_fk and books_namespace.pages %s books_namespace.pages);)"; - std::vector conditionsSet = {CondLt, CondLe, CondGt, CondGe, CondEq}; - for (size_t i = 0; i < conditionsSet.size(); ++i) { - CondType condition = conditionsSet[i]; - Query queryBooks; - queryBooks.FromSQL(GetSql(sqlTemplate, condition)); + for (CondType condition : {CondLt, CondLe, CondGt, CondGe, CondEq}) { + Query queryBooks = Query::FromSQL(GetSql(sqlTemplate, condition)); QueryResults qr; Error err = rt.reindexer->Select(queryBooks, qr); ASSERT_TRUE(err.ok()) << err.what(); for (auto it : qr) { - auto item = it.GetItem(); + const auto item = it.GetItem(); ASSERT_TRUE(item.Status().ok()) << item.Status().what(); - auto joined = it.GetJoined(); - ASSERT_TRUE(joined.getJoinedFieldsCount() == 1); + const Variant authorid1 = item[authorid_fk]; + const Variant pages1 = item[pages]; + const auto joined = it.GetJoined(); + ASSERT_EQ(joined.getJoinedFieldsCount(), 1); LocalQueryResults jqr = joined.begin().ToQueryResults(); - jqr.addNSContext(qr.GetPayloadType(0), qr.GetTagsMatcher(0), qr.GetFieldsFilter(0), qr.GetSchema(0)); + jqr.addNSContext(qr, 0, reindexer::lsn_t()); for (auto jit : jqr) { auto joinedItem = jit.GetItem(); ASSERT_TRUE(joinedItem.Status().ok()) << joinedItem.Status().what(); - Variant authorid1 = item[authorid_fk]; Variant authorid2 = joinedItem[authorid_fk]; - ASSERT_TRUE(authorid1 == authorid2); - Variant pages1 = item[pages]; + ASSERT_EQ(authorid1, authorid2); Variant pages2 = joinedItem[pages]; - ASSERT_TRUE(CompareVariants(pages1, pages2, condition)); + ASSERT_TRUE(CompareVariants(pages1, pages2, condition)) + << pages1.As() << ' ' << reindexer::CondTypeToStr(condition) << ' ' << pages2.As(); } } } @@ -674,14 +672,12 @@ TEST_F(JoinOnConditionsApi, TestComparisonConditions) { for (size_t i = 0; i < sqlTemplates.size(); ++i) { const auto& sqlTemplate = sqlTemplates[i]; for (const auto& condition : conditions) { - Query query1; - query1.FromSQL(GetSql(sqlTemplate.first, condition.first)); + Query query1 = Query::FromSQL(GetSql(sqlTemplate.first, condition.first)); QueryResults qr1; Error err = rt.reindexer->Select(query1, qr1); ASSERT_TRUE(err.ok()) << err.what(); - Query query2; - query2.FromSQL(GetSql(sqlTemplate.second, condition.second)); + Query query2 = Query::FromSQL(GetSql(sqlTemplate.second, condition.second)); QueryResults qr2; err = rt.reindexer->Select(query2, qr2); ASSERT_TRUE(err.ok()) << err.what(); @@ -691,16 +687,16 @@ TEST_F(JoinOnConditionsApi, TestComparisonConditions) { auto item1 = it1.GetItem(); ASSERT_TRUE(item1.Status().ok()) << item1.Status().what(); auto joined1 = it1.GetJoined(); - ASSERT_TRUE(joined1.getJoinedFieldsCount() == 1); + ASSERT_EQ(joined1.getJoinedFieldsCount(), 1); LocalQueryResults jqr1 = joined1.begin().ToQueryResults(); - jqr1.addNSContext(qr1.GetPayloadType(1), qr1.GetTagsMatcher(1), qr1.GetFieldsFilter(1), qr1.GetSchema(0)); + jqr1.addNSContext(qr1, 1, reindexer::lsn_t()); auto item2 = it2.GetItem(); ASSERT_TRUE(item2.Status().ok()) << item2.Status().what(); auto joined2 = it2.GetJoined(); - ASSERT_TRUE(joined2.getJoinedFieldsCount() == 1); + ASSERT_EQ(joined2.getJoinedFieldsCount(), 1); LocalQueryResults jqr2 = joined2.begin().ToQueryResults(); - jqr2.addNSContext(qr2.GetPayloadType(1), qr2.GetTagsMatcher(1), qr2.GetFieldsFilter(1), qr2.GetSchema(0)); + jqr2.addNSContext(qr2, 1, reindexer::lsn_t()); ASSERT_EQ(jqr1.Count(), jqr2.Count()); @@ -748,8 +744,10 @@ TEST_F(JoinOnConditionsApi, TestLeftJoinOnCondSet) { ASSERT_EQ(qr.Count(), results.size()); int k = 0; for (auto it = qr.begin(); it != qr.end(); ++it, ++k) { + ASSERT_TRUE(it.Status().ok()) << it.Status().what(); reindexer::WrSerializer ser; - it.GetJSON(ser, false); + err = it.GetJSON(ser, false); + ASSERT_TRUE(err.ok()) << err.what(); ASSERT_EQ(ser.c_str(), results[k]); } }; @@ -764,8 +762,7 @@ TEST_F(JoinOnConditionsApi, TestLeftJoinOnCondSet) { } auto sqlTestCase = [execQuery](const std::string& s) { - Query q; - q.FromSQL(s); + Query q = Query::FromSQL(s); execQuery(q); }; @@ -782,13 +779,12 @@ TEST_F(JoinOnConditionsApi, TestInvalidConditions) { R"(select * from books_namespace inner join authors_namespace on (books_namespace.authorid_fk = books_namespace.authorid_fk and books_namespace.pages in(1, 50, 100, 500, 1000, 1500));)", }; for (const std::string& sql : sqls) { - Query queryBooks; - EXPECT_THROW(queryBooks.FromSQL(sql), Error); + EXPECT_THROW((void)Query::FromSQL(sql), Error); } QueryResults qr; Error err = rt.reindexer->Select(Query(books_namespace).InnerJoin(authorid_fk, authorid, CondAllSet, Query(authors_namespace)), qr); - EXPECT_TRUE(!err.ok()); + EXPECT_FALSE(err.ok()); qr.Clear(); err = rt.reindexer->Select(Query(books_namespace).InnerJoin(authorid_fk, authorid, CondLike, Query(authors_namespace)), qr); - EXPECT_TRUE(!err.ok()); + EXPECT_FALSE(err.ok()); } diff --git a/cpp_src/gtests/tests/unit/namespace_test.cc b/cpp_src/gtests/tests/unit/namespace_test.cc index d73d2eee9..82c5eb29f 100644 --- a/cpp_src/gtests/tests/unit/namespace_test.cc +++ b/cpp_src/gtests/tests/unit/namespace_test.cc @@ -6,13 +6,11 @@ #include "core/cjson/msgpackbuilder.h" #include "core/cjson/msgpackdecoder.h" #include "core/defnsconfigs.h" -#include "core/itemimpl.h" #include "estl/span.h" #include "ns_api.h" #include "tools/jsontools.h" #include "tools/serializer.h" #include "vendor/gason/gason.h" -#include "vendor/msgpack/msgpack.h" using QueryResults = ReindexerApi::QueryResults; using Item = ReindexerApi::Item; @@ -133,9 +131,9 @@ TEST_F(NsApi, UpsertWithPrecepts) { { // Set precepts - std::vector precepts = {updatedTimeSecFieldName + "=NOW()", updatedTimeMSecFieldName + "=NOW(msec)", + std::vector precepts = {updatedTimeSecFieldName + "=NOW()", updatedTimeMSecFieldName + "=NOW(msec)", updatedTimeUSecFieldName + "=NOW(usec)", updatedTimeNSecFieldName + "=NOW(nsec)", - serialFieldName + "=SERIAL()", stringField + "=SERIAL()"}; + serialFieldName + "=SERIAL()", stringField + "=SERIAL()"}; item.SetPrecepts(std::move(precepts)); } @@ -348,12 +346,12 @@ TEST_F(NsApi, QueryperfstatsNsDummyTest) { QueryResults qr; err = rt.reindexer->Select(Query("#queriesperfstats"), qr); ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qr.Count() > 0) << "#queriesperfstats table is empty!"; + ASSERT_GT(qr.Count(), 0) << "#queriesperfstats table is empty!"; for (auto &it : qr) { std::cout << it.GetItem(false).GetJSON() << std::endl; } } - ASSERT_TRUE(qres.Count() == 1) << "Expected 1 row for this query, got " << qres.Count(); + ASSERT_EQ(qres.Count(), 1) << "Expected 1 row for this query, got " << qres.Count(); Item item = qres.begin().GetItem(false); Variant val; val = item["latency_stddev"]; @@ -424,12 +422,12 @@ TEST_F(NsApi, TestUpdateNonindexedField) { Query updateQuery{Query(default_namespace).Where("id", CondGe, Variant("1500")).Set("nested.bonus", static_cast(100500))}; Error err = rt.reindexer->Update(updateQuery, qrUpdate); ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qrUpdate.Count() == 500) << qrUpdate.Count(); + ASSERT_EQ(qrUpdate.Count(), 500); QueryResults qrAll; err = rt.reindexer->Select(Query(default_namespace).Where("id", CondGe, Variant("1500")), qrAll); ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qrAll.Count() == 500) << qrAll.Count(); + ASSERT_EQ(qrAll.Count(), 500); for (auto it : qrAll) { Item item = it.GetItem(false); @@ -448,12 +446,12 @@ TEST_F(NsApi, TestUpdateSparseField) { Query updateQuery{Query(default_namespace).Where("id", CondGe, Variant("1500")).Set("sparse_field", static_cast(100500))}; Error err = rt.reindexer->Update(updateQuery, qrUpdate); ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qrUpdate.Count() == 500) << qrUpdate.Count(); + ASSERT_EQ(qrUpdate.Count(), 500); QueryResults qrAll; err = rt.reindexer->Select(Query(default_namespace).Where("id", CondGe, Variant("1500")), qrAll); ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qrAll.Count() == 500) << qrAll.Count(); + ASSERT_EQ(qrAll.Count(), 500); for (auto it : qrAll) { Item item = it.GetItem(false); @@ -482,7 +480,7 @@ TEST_F(NsApi, TestUpdateTwoFields) { // Make sure query worked well ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qrUpdate.Count() == 1) << qrUpdate.Count(); + ASSERT_EQ(qrUpdate.Count(), 1); // Make sure: // 1. JSON of the item is correct @@ -503,18 +501,56 @@ TEST_F(NsApi, TestUpdateTwoFields) { } } +TEST_F(NsApi, TestUpdateNewFieldCheckTmVersion) { + DefineDefaultNamespace(); + FillDefaultNamespace(); + + auto check = [this](const Query &query, int tmVersion) { + QueryResults qrUpdate; + auto err = rt.reindexer->Update(query, qrUpdate); + ASSERT_TRUE(err.ok()) << err.what(); + ASSERT_EQ(qrUpdate.Count(), 1); + ASSERT_EQ(qrUpdate.GetTagsMatcher(0).version(), tmVersion); + }; + + QueryResults qr; + Error err = rt.reindexer->Select(Query(default_namespace).Where(idIdxName, CondEq, 1), qr); + ASSERT_TRUE(err.ok()) << err.what(); + ASSERT_EQ(qr.Count(), 1); + auto tmVersion = qr.GetTagsMatcher(0).version(); + Query updateQuery = Query(default_namespace).Where(idIdxName, CondEq, 1).Set("some_new_field", "some_value"); + + // Make sure the version increases by 1 when one new tag with non-object content is added + check(updateQuery, ++tmVersion); + + // Make sure the version not change when the same update query applied + check(updateQuery, tmVersion); + + updateQuery.SetObject("new_obj_field", R"({"id":111, "name":"successfully updated!"})"); + + // Make sure that tm version updates correctly when new tags are added: + // +1 by tag very_nested, + // +1 by all new tags processed in ItemModifier::modifyCJSON for SetObject-method + // +1 by merge of two corresponded tagsmatchers + check(updateQuery, tmVersion += 3); + + // Make sure that if no new tags were added to the tagsmatcher during the update, + // then the version of the tagsmatcher will not change + check(updateQuery, tmVersion); +} + static void updateArrayField(const std::shared_ptr &reindexer, const std::string &ns, const std::string &updateFieldPath, const VariantArray &values) { QueryResults qrUpdate; Query updateQuery{Query(ns).Where("id", CondGe, Variant("500")).Set(updateFieldPath, values)}; Error err = reindexer->Update(updateQuery, qrUpdate); ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qrUpdate.Count() > 0) << qrUpdate.Count(); + ASSERT_GT(qrUpdate.Count(), 0); QueryResults qrAll; err = reindexer->Select(Query(ns).Where("id", CondGe, Variant("500")), qrAll); ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qrAll.Count() == qrUpdate.Count()) << qrAll.Count(); + ASSERT_EQ(qrAll.Count(), qrUpdate.Count()); for (auto it : qrAll) { Item item = it.GetItem(false); @@ -546,7 +582,7 @@ TEST_F(NsApi, TestUpdateNonindexedArrayField2) { QueryResults qr; Error err = rt.reindexer->Select(R"(update test_namespace set nested.bonus=[{"first":1,"second":2,"third":3}] where id = 1000;)", qr); ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qr.Count() == 1) << qr.Count(); + ASSERT_EQ(qr.Count(), 1); Item item = qr.begin().GetItem(false); std::string_view json = item.GetJSON(); @@ -562,7 +598,7 @@ TEST_F(NsApi, TestUpdateNonindexedArrayField3) { Error err = rt.reindexer->Select(R"(update test_namespace set nested.bonus=[{"id":1},{"id":2},{"id":3},{"id":4}] where id = 1000;)", qr); ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qr.Count() == 1) << qr.Count(); + ASSERT_EQ(qr.Count(), 1); Item item = qr.begin().GetItem(false); VariantArray val = item["nested.bonus"]; @@ -585,12 +621,12 @@ TEST_F(NsApi, TestUpdateNonindexedArrayField4) { QueryResults qr; Error err = rt.reindexer->Select(R"(update test_namespace set nested.bonus=[0] where id = 1000;)", qr); ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qr.Count() == 1) << qr.Count(); + ASSERT_EQ(qr.Count(), 1); Item item = qr.begin().GetItem(false); std::string_view json = item.GetJSON(); size_t pos = json.find(R"("nested":{"bonus":[0])"); - ASSERT_TRUE(pos != std::string::npos) << "'nested.bonus' was not updated properly" << json; + ASSERT_NE(pos, std::string::npos) << "'nested.bonus' was not updated properly" << json; } TEST_F(NsApi, TestUpdateNonindexedArrayField5) { @@ -620,12 +656,12 @@ TEST_F(NsApi, TestUpdateIndexedArrayField2) { Query q{Query(default_namespace).Where(idIdxName, CondEq, static_cast(1000)).Set(indexedArrayField, std::move(value.MarkArray()))}; Error err = rt.reindexer->Update(q, qr); ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qr.Count() == 1) << qr.Count(); + ASSERT_EQ(qr.Count(), 1); Item item = qr.begin().GetItem(false); std::string_view json = item.GetJSON(); size_t pos = json.find(R"("indexed_array_field":[77])"); - ASSERT_TRUE(pos != std::string::npos) << "'indexed_array_field' was not updated properly" << json; + ASSERT_NE(pos, std::string::npos) << "'indexed_array_field' was not updated properly" << json; } static void addAndSetNonindexedField(const std::shared_ptr &reindexer, const std::string &ns, @@ -853,6 +889,7 @@ TEST_F(NsApi, DropArrayField3) { DropArrayItem(rt.reindexer, default_namespace, "nested.nested_array[*].prices[*]", "nested.nested_array.prices"); } +#if (0) // #1500 TEST_F(NsApi, DropArrayField4) { // 1. Define NS // 2. Fill NS @@ -862,6 +899,7 @@ TEST_F(NsApi, DropArrayField4) { DropArrayItem(rt.reindexer, default_namespace, "nested.nested_array[0].prices[((2+4)*2)/6]", "nested.nested_array.prices", 0, ((2 + 4) * 2) / 6); } +#endif TEST_F(NsApi, SetArrayFieldWithSql) { // 1. Define NS @@ -870,8 +908,7 @@ TEST_F(NsApi, SetArrayFieldWithSql) { AddUnindexedData(); // 3. Set all items of array to 777 - Query updateQuery; - updateQuery.FromSQL("update test_namespace set nested.nested_array[1].prices[*] = 777"); + Query updateQuery = Query::FromSQL("update test_namespace set nested.nested_array[1].prices[*] = 777"); QueryResults qrUpdate; Error err = rt.reindexer->Update(updateQuery, qrUpdate); ASSERT_TRUE(err.ok()) << err.what(); @@ -896,8 +933,7 @@ TEST_F(NsApi, DropArrayFieldWithSql) { AddUnindexedData(); // 3. Drop all items of array nested.nested_array[1].prices - Query updateQuery; - updateQuery.FromSQL("update test_namespace drop nested.nested_array[1].prices[*]"); + Query updateQuery = Query::FromSQL("update test_namespace drop nested.nested_array[1].prices[*]"); QueryResults qrUpdate; Error err = rt.reindexer->Update(updateQuery, qrUpdate); ASSERT_TRUE(err.ok()) << err.what(); @@ -920,8 +956,7 @@ TEST_F(NsApi, ExtendArrayFromTopWithSql) { AddUnindexedData(); // Append the following items: [88, 88, 88] to the top of the array array_field - Query updateQuery; - updateQuery.FromSQL("update test_namespace set array_field = [88,88,88] || array_field"); + Query updateQuery = Query::FromSQL("update test_namespace set array_field = [88,88,88] || array_field"); QueryResults qrUpdate; Error err = rt.reindexer->Update(updateQuery, qrUpdate); ASSERT_TRUE(err.ok()) << err.what(); @@ -947,8 +982,8 @@ TEST_F(NsApi, AppendToArrayWithSql) { AddUnindexedData(); // 3. Extend array_field with expression substantially - Query updateQuery; - updateQuery.FromSQL("update test_namespace set array_field = array_field || objects.more[1].array[4] || [22,22,22] || [11]"); + Query updateQuery = + Query::FromSQL("update test_namespace set array_field = array_field || objects.more[1].array[4] || [22,22,22] || [11]"); QueryResults qrUpdate; Error err = rt.reindexer->Update(updateQuery, qrUpdate); ASSERT_TRUE(err.ok()) << err.what(); @@ -1129,8 +1164,8 @@ TEST_F(NsApi, UpdateObjectsArray) { AddUnindexedData(); // 3. Update object array and change one of it's items - Query updateQuery; - updateQuery.FromSQL(R"(update test_namespace set nested.nested_array[1] = {"id":1,"name":"modified", "prices":[4,5,6]})"); + Query updateQuery = + Query::FromSQL(R"(update test_namespace set nested.nested_array[1] = {"id":1,"name":"modified", "prices":[4,5,6]})"); QueryResults qrUpdate; Error err = rt.reindexer->Update(updateQuery, qrUpdate); ASSERT_TRUE(err.ok()) << err.what(); @@ -1150,8 +1185,7 @@ TEST_F(NsApi, UpdateObjectsArray2) { AddUnindexedData(); // 3. Set all items of the object array to a new value - Query updateQuery; - updateQuery.FromSQL(R"(update test_namespace set nested.nested_array[*] = {"ein":1,"zwei":2, "drei":3})"); + Query updateQuery = Query::FromSQL(R"(update test_namespace set nested.nested_array[*] = {"ein":1,"zwei":2, "drei":3})"); QueryResults qrUpdate; Error err = rt.reindexer->Update(updateQuery, qrUpdate); ASSERT_TRUE(err.ok()) << err.what(); @@ -1448,6 +1482,112 @@ TEST_F(NsApi, UpdateObjectsArray4) { } } +TEST_F(NsApi, UpdateArrayIndexFieldWithSeveralJsonPaths) { + struct Values { + std::vector valsList, newValsList; + }; + const int fieldsCnt = 5; + const int valsPerFieldCnt = 4; + std::vector fieldsValues(fieldsCnt); + for (int i = 0; i < fieldsCnt; ++i) { + for (int j = 0; j < valsPerFieldCnt; ++j) { + fieldsValues[i].valsList.emplace_back(fmt::sprintf("data%d%d", i, j)); + fieldsValues[i].newValsList.emplace_back(fmt::sprintf("data%d%d", i, j + i)); + } + } + + enum class OpT { Insert, Update }; + + auto makeFieldsList = [&fieldsValues](const reindexer::fast_hash_set &indexes, OpT type) { + auto quote = type == OpT::Insert ? '"' : '\''; + std::vector Values::*list = type == OpT::Insert ? &Values::valsList : &Values::newValsList; + const auto fieldsListTmplt = type == OpT::Insert ? R"("%sfield%d": [%s])" : R"(%sfield%d = [%s])"; + std::string fieldsList; + for (int idx : indexes) { + std::string fieldList; + for (const auto &data : fieldsValues[idx].*list) { + fieldList += std::string(fieldList.empty() ? "" : ", ") + quote + data + quote; + } + fieldsList += fmt::sprintf(fieldsListTmplt, fieldsList.empty() ? "" : ", ", idx, fieldList); + } + return fieldsList; + }; + + auto makeItem = [&makeFieldsList](int id, const reindexer::fast_hash_set &indexes) { + auto list = makeFieldsList(indexes, OpT::Insert); + return fmt::sprintf(R"({"id": %d%s})", id, (list.empty() ? "" : ", ") + list); + }; + + auto makeUpdate = [this, &makeFieldsList](int id, const reindexer::fast_hash_set &indexes) { + return fmt::sprintf("UPDATE %s SET %s WHERE id = %d", default_namespace, makeFieldsList(indexes, OpT::Update), id); + }; + + struct TestCase { + reindexer::fast_hash_set insertIdxs, updateIdxs; + auto expected() const { + auto res = insertIdxs; + res.insert(updateIdxs.begin(), updateIdxs.end()); + return res; + } + }; + + std::vector testCases{ + {{}, {0}}, + {{}, {2}}, + {{}, {0, 1, 2}}, + {{2, 3, 4}, {0}}, + {{3}, {0, 2, 4}}, + {{0, 3, 4}, {2, 1}}, + {{0, 1, 2, 3}, {4}}, + {{0, 1, 2}, {3, 4}}, + {{0, 2, 3}, {1, 4}}, + {{4}, {0, 1, 2, 3}}, + {{3, 4}, {0, 2, 1}}, + {{}, {0, 1, 2, 3, 4}}, + {{0, 1, 2, 3, 4}, {0}}, + {{0, 3, 4}, {0, 3, 4}}, + {{0, 1, 2}, {2, 3, 1}}, + {{0, 3, 4}, {2, 3, 4}}, + {{0, 1, 3}, {0, 1, 2, 3, 4}}, + {{0, 1, 2, 3, 4}, {0, 1, 2, 3, 4}}, + }; + + Error err = rt.reindexer->OpenNamespace(default_namespace); + ASSERT_TRUE(err.ok()) << err.what(); + + err = rt.reindexer->AddIndex(default_namespace, {"id", "hash", "int", IndexOpts().PK()}); + ASSERT_TRUE(err.ok()) << err.what(); + err = rt.reindexer->AddIndex(default_namespace, {"array_index", reindexer::JsonPaths{"field0", "field1", "field2", "field3", "field4"}, + "hash", "string", IndexOpts().Array()}); + ASSERT_TRUE(err.ok()) << err.what(); + + for (size_t i = 0; i < testCases.size(); ++i) { + AddItemFromJSON(default_namespace, makeItem(i, testCases[i].insertIdxs)); + { + QueryResults qr; + err = rt.reindexer->Select(makeUpdate(i, testCases[i].updateIdxs), qr); + ASSERT_TRUE(err.ok()) << err.what(); + ASSERT_EQ(qr.Count(), 1); + + auto item = qr.begin().GetItem(false); + for (auto idx : testCases[i].expected()) { + int varArrCnt = 0; + for (auto &&var : VariantArray(item[fmt::sprintf("field%d", idx)])) { + const auto &data = testCases[i].updateIdxs.count(idx) ? fieldsValues[idx].newValsList : fieldsValues[idx].valsList; + ASSERT_EQ(var.As(), data[varArrCnt++]); + } + } + } + } + + // Check that prohibited updating an index array field with several json paths by index name + QueryResults qr; + err = rt.reindexer->Select(fmt::sprintf(R"(UPDATE %s SET array_index = ['data0', 'data1', 'data2'] WHERE id = 0)", default_namespace), + qr); + ASSERT_FALSE(err.ok()); + ASSERT_EQ(err.what(), "Ambiguity when updating field with several json paths by index name: 'array_index'"); +} + TEST_F(NsApi, UpdateWithObjectAndFieldsDuplication) { Error err = rt.reindexer->OpenNamespace(default_namespace); ASSERT_TRUE(err.ok()) << err.what(); @@ -1701,12 +1841,12 @@ static void checkFieldConversion(const std::shared_ptr &re ASSERT_TRUE(!err.ok()); } else { ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qrUpdate.Count() > 0) << qrUpdate.Count(); + ASSERT_GT(qrUpdate.Count(), 0); QueryResults qrAll; err = reindexer->Select(selectQuery, qrAll); ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qrAll.Count() == qrUpdate.Count()) << qrAll.Count(); + ASSERT_EQ(qrAll.Count(), qrUpdate.Count()); for (auto it : qrAll) { Item item = it.GetItem(false); @@ -1851,16 +1991,21 @@ TEST_F(NsApi, TestUpdatePkFieldNoConditions) { DefineDefaultNamespace(); FillDefaultNamespace(); + QueryResults qrCount; + Error err = rt.reindexer->Select("select count(*) from test_namespace", qrCount); + ASSERT_TRUE(err.ok()) << err.what(); + QueryResults qr; - Error err = rt.reindexer->Select("update test_namespace set id = id + 1;", qr); + err = rt.reindexer->Select("update test_namespace set id = id + " + std::to_string(qrCount.TotalCount() + 100), qr); ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qr.Count() > 0); + ASSERT_GT(qr.Count(), 0); - int i = 1; + int i = 0; for (auto &it : qr) { Item item = it.GetItem(false); Variant intFieldVal = item[idIdxName]; - ASSERT_TRUE(static_cast(intFieldVal) == i++); + ASSERT_EQ(static_cast(intFieldVal), i + qrCount.TotalCount() + 100); + i++; } } @@ -1871,7 +2016,7 @@ TEST_F(NsApi, TestUpdateIndexArrayWithNull) { QueryResults qr; Error err = rt.reindexer->Select("update test_namespace set indexed_array_field = null where id = 1;", qr); ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qr.Count() == 1); + ASSERT_EQ(qr.Count(), 1); for (auto &it : qr) { Item item = it.GetItem(false); @@ -1992,7 +2137,7 @@ TEST_F(NsApi, TestUpdateNonIndexFieldWithNull) { QueryResults qr; Error err = rt.reindexer->Select("update test_namespace set extra = null where id = 1001;", qr); ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qr.Count() == 1); + ASSERT_EQ(qr.Count(), 1); for (auto &it : qr) { Item item = it.GetItem(false); @@ -2017,7 +2162,7 @@ TEST_F(NsApi, TestUpdateEmptyArrayField) { QueryResults qr; Error err = rt.reindexer->Select("update test_namespace set indexed_array_field = [] where id = 1;", qr); ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qr.Count() == 1); + ASSERT_EQ(qr.Count(), 1); Item item = qr.begin().GetItem(false); Variant idFieldVal = item[idIdxName]; @@ -2079,12 +2224,12 @@ TEST_F(NsApi, TestUpdateEmptyIndexedField) { .Set(indexedArrayField, {Variant(static_cast(4)), Variant(static_cast(5)), Variant(static_cast(6))}); Error err = rt.reindexer->Update(q, qr); ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qr.Count() == 1); + ASSERT_EQ(qr.Count(), 1); QueryResults qr2; err = rt.reindexer->Select("select * from test_namespace where id = 1001;", qr2); ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qr2.Count() == 1); + ASSERT_EQ(qr2.Count(), 1); for (auto it : qr2) { Item item = it.GetItem(false); @@ -2109,7 +2254,7 @@ TEST_F(NsApi, TestDropField) { QueryResults qr; Error err = rt.reindexer->Select("update test_namespace drop extra where id >= 1000 and id < 1010;", qr); ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qr.Count() == 10) << qr.Count(); + ASSERT_EQ(qr.Count(), 10); for (auto it : qr) { Item item = it.GetItem(false); @@ -2121,7 +2266,7 @@ TEST_F(NsApi, TestDropField) { QueryResults qr2; err = rt.reindexer->Select("update test_namespace drop nested.bonus where id >= 1005 and id < 1010;", qr2); ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qr2.Count() == 5); + ASSERT_EQ(qr2.Count(), 5); for (auto it : qr2) { Item item = it.GetItem(false); @@ -2153,7 +2298,7 @@ TEST_F(NsApi, TestUpdateFieldWithFunction) { Error err = rt.reindexer->Select( "update test_namespace set int_field = SERIAL(), extra = SERIAL(), nested.timeField = NOW(msec) where id >= 0;", qr); ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qr.Count() > 0); + ASSERT_GT(qr.Count(), 0); int i = 1; for (auto &it : qr) { @@ -2177,7 +2322,7 @@ TEST_F(NsApi, TestUpdateFieldWithExpressions) { "0;", qr); ASSERT_TRUE(err.ok()) << err.what(); - ASSERT_TRUE(qr.Count() > 0) << qr.Count(); + ASSERT_GT(qr.Count(), 0); int i = 1; for (auto &it : qr) { @@ -2216,22 +2361,22 @@ static void checkQueryDsl(const Query &src) { } } if (objectValues) { - EXPECT_TRUE(src.entries == dst.entries); - EXPECT_TRUE(src.aggregations_ == dst.aggregations_); - EXPECT_TRUE(src._namespace == dst._namespace); - EXPECT_TRUE(src.sortingEntries_ == dst.sortingEntries_); - EXPECT_TRUE(src.calcTotal == dst.calcTotal); - EXPECT_TRUE(src.start == dst.start); - EXPECT_TRUE(src.count == dst.count); - EXPECT_TRUE(src.debugLevel == dst.debugLevel); - EXPECT_TRUE(src.strictMode == dst.strictMode); - EXPECT_TRUE(src.forcedSortOrder_ == dst.forcedSortOrder_); - EXPECT_TRUE(src.selectFilter_ == dst.selectFilter_); - EXPECT_TRUE(src.selectFunctions_ == dst.selectFunctions_); - EXPECT_TRUE(src.joinQueries_ == dst.joinQueries_); - EXPECT_TRUE(src.mergeQueries_ == dst.mergeQueries_); + EXPECT_EQ(src.Entries(), dst.Entries()); + EXPECT_EQ(src.aggregations_, dst.aggregations_); + EXPECT_EQ(src.NsName(), dst.NsName()); + EXPECT_EQ(src.sortingEntries_, dst.sortingEntries_); + EXPECT_EQ(src.CalcTotal(), dst.CalcTotal()); + EXPECT_EQ(src.Offset(), dst.Offset()); + EXPECT_EQ(src.Limit(), dst.Limit()); + EXPECT_EQ(src.GetDebugLevel(), dst.GetDebugLevel()); + EXPECT_EQ(src.GetStrictMode(), dst.GetStrictMode()); + EXPECT_EQ(src.forcedSortOrder_, dst.forcedSortOrder_); + EXPECT_EQ(src.SelectFilters(), dst.SelectFilters()); + EXPECT_EQ(src.selectFunctions_, dst.selectFunctions_); + EXPECT_EQ(src.GetJoinQueries(), dst.GetJoinQueries()); + EXPECT_EQ(src.GetMergeQueries(), dst.GetMergeQueries()); } else { - EXPECT_TRUE(dst == src); + EXPECT_EQ(dst, src); } } @@ -2239,45 +2384,38 @@ TEST_F(NsApi, TestModifyQueriesSqlEncoder) { const std::string sqlUpdate = "UPDATE ns SET field1 = 'mrf',field2 = field2+1,field3 = ['one','two','three','four','five'] WHERE a = true AND location = " "'msk'"; - Query q1; - q1.FromSQL(sqlUpdate); + Query q1 = Query::FromSQL(sqlUpdate); EXPECT_EQ(q1.GetSQL(), sqlUpdate); checkQueryDsl(q1); const std::string sqlDrop = "UPDATE ns DROP field1,field2 WHERE a = true AND location = 'msk'"; - Query q2; - q2.FromSQL(sqlDrop); + Query q2 = Query::FromSQL(sqlDrop); EXPECT_EQ(q2.GetSQL(), sqlDrop); checkQueryDsl(q2); const std::string sqlUpdateWithObject = R"(UPDATE ns SET field = {"id":0,"name":"apple","price":1000,"nested":{"n_id":1,"desription":"good","array":[{"id":1,"description":"first"},{"id":2,"description":"second"},{"id":3,"description":"third"}]},"bonus":7} WHERE a = true AND location = 'msk')"; - Query q3; - q3.FromSQL(sqlUpdateWithObject); + Query q3 = Query::FromSQL(sqlUpdateWithObject); EXPECT_EQ(q3.GetSQL(), sqlUpdateWithObject); checkQueryDsl(q3); const std::string sqlTruncate = R"(TRUNCATE ns)"; - Query q4; - q4.FromSQL(sqlTruncate); + Query q4 = Query::FromSQL(sqlTruncate); EXPECT_EQ(q4.GetSQL(), sqlTruncate); checkQueryDsl(q4); const std::string sqlArrayAppend = R"(UPDATE ns SET array = array||[1,2,3]||array2||objects[0].nested.prices[0])"; - Query q5; - q5.FromSQL(sqlArrayAppend); + Query q5 = Query::FromSQL(sqlArrayAppend); EXPECT_EQ(q5.GetSQL(), sqlArrayAppend); checkQueryDsl(q5); const std::string sqlIndexUpdate = R"(UPDATE ns SET objects[0].nested.prices[*] = 'NE DOROGO!')"; - Query q6; - q6.FromSQL(sqlIndexUpdate); + Query q6 = Query::FromSQL(sqlIndexUpdate); EXPECT_EQ(q6.GetSQL(), sqlIndexUpdate); checkQueryDsl(q6); const std::string sqlSpeccharsUpdate = R"(UPDATE ns SET f1 = 'HELLO\n\r\b\f',f2 = '\t',f3 = '\"')"; - Query q7; - q7.FromSQL(sqlSpeccharsUpdate); + Query q7 = Query::FromSQL(sqlSpeccharsUpdate); EXPECT_EQ(q7.GetSQL(), sqlSpeccharsUpdate); checkQueryDsl(q7); } @@ -2388,12 +2526,13 @@ TEST_F(NsApi, MsgPackEncodingTest) { ASSERT_TRUE(err.ok()) << err.what(); std::string json(item.GetJSON()); - ASSERT_TRUE(json == items[i++]); + ASSERT_EQ(json, items[i++]); } reindexer::WrSerializer wrSer3; for (auto &it : qr) { - it.GetMsgPack(wrSer3, false); + const auto err = it.GetMsgPack(wrSer3, false); + ASSERT_TRUE(err.ok()) << err.what(); } i = 0; @@ -2406,7 +2545,7 @@ TEST_F(NsApi, MsgPackEncodingTest) { ASSERT_TRUE(err.ok()) << err.what(); std::string json(item.GetJSON()); - ASSERT_TRUE(json == items[i++]); + ASSERT_EQ(json, items[i++]); } } @@ -2471,7 +2610,6 @@ TEST_F(NsApi, DeleteLastItems) { ASSERT_EQ(qr.Count(), 2); } - TEST_F(NsApi, IncorrectNsName) { auto check = [&](const std::vector &names, auto func) { for (const auto &v : names) { @@ -2504,14 +2642,13 @@ TEST_F(NsApi, IncorrectNsName) { ASSERT_TRUE(err.ok()) << err.what(); err = rt.reindexer->RenameNamespace(kNsName, name); ASSERT_FALSE(err.ok()); - ASSERT_EQ(err.what(), "Namespace name contains invalid character. Only alphas, digits,'_','-', are allowed ("+name+")"); + ASSERT_EQ(err.what(), "Namespace name contains invalid character. Only alphas, digits,'_','-', are allowed (" + name + ")"); err = rt.reindexer->DropNamespace(kNsName); ASSERT_TRUE(err.ok()) << err.what(); }; check(variants, rename); } - TEST_F(NsApi, TagsmatchersMerge) { using reindexer::TagsMatcher; using reindexer::PayloadType; diff --git a/cpp_src/gtests/tests/unit/queries_test.cc b/cpp_src/gtests/tests/unit/queries_test.cc index db33fa5bc..ca05b4d10 100644 --- a/cpp_src/gtests/tests/unit/queries_test.cc +++ b/cpp_src/gtests/tests/unit/queries_test.cc @@ -1,5 +1,7 @@ #include + #include + #include "core/cjson/csvbuilder.h" #include "core/schema.h" #include "csv2jsonconverter.h" @@ -32,7 +34,7 @@ TEST_F(QueriesApi, QueriesStandardTestSet) { auto& items = insertedItems_[default_namespace]; for (auto it = items.begin(); it != items.end();) { Error err = rt.reindexer->Delete(default_namespace, it->second); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); it = items.erase(it); if (++itemsCount == 4000) break; } @@ -43,7 +45,7 @@ TEST_F(QueriesApi, QueriesStandardTestSet) { itemsCount = 0; for (auto it = items.begin(); it != items.end();) { Error err = rt.reindexer->Delete(default_namespace, it->second); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); it = items.erase(it); if (++itemsCount == 5000) break; } @@ -52,7 +54,7 @@ TEST_F(QueriesApi, QueriesStandardTestSet) { auto itToRemove = items.begin(); if (itToRemove != items.end()) { Error err = rt.reindexer->Delete(default_namespace, itToRemove->second); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); items.erase(itToRemove); } FillDefaultNamespace(rand() % 100, 1, 0); @@ -62,7 +64,7 @@ TEST_F(QueriesApi, QueriesStandardTestSet) { std::advance(itToRemove, rand() % std::min(100, int(items.size()))); if (itToRemove != items.end()) { Error err = rt.reindexer->Delete(default_namespace, itToRemove->second); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); items.erase(itToRemove); } } @@ -70,7 +72,7 @@ TEST_F(QueriesApi, QueriesStandardTestSet) { for (auto it = items.begin(); it != items.end();) { Error err = rt.reindexer->Delete(default_namespace, it->second); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); it = items.erase(it); } @@ -105,6 +107,18 @@ TEST_F(QueriesApi, QueriesConditions) { CheckConditions(); } +#if !defined(REINDEX_WITH_TSAN) +TEST_F(QueriesApi, UuidQueries) { + FillUUIDNs(); + // hack to obtain not index not string uuid fields + /*auto err = rt.reindexer->DropIndex(uuidNs, {kFieldNameUuidNotIndex2}); // TODO uncomment this #1470 + ASSERT_TRUE(err.ok()) << err.what(); + err = rt.reindexer->DropIndex(uuidNs, {kFieldNameUuidNotIndex3}); + ASSERT_TRUE(err.ok()) << err.what();*/ + CheckUUIDQueries(); +} +#endif + TEST_F(QueriesApi, IndexCacheInvalidationTest) { std::vector> data{{0, 10}, {1, 9}, {2, 8}, {3, 7}, {4, 6}, {5, 5}, {6, 4}, {7, 3}, {8, 2}, {9, 1}, {10, 0}, {11, -1}}; @@ -218,6 +232,16 @@ TEST_F(QueriesApi, SqlParseGenerate) { Query{"test_namespace"}.Sort("index", true, {"str1", "str2", "str3"})}, {"SELECT * FROM test_namespace ORDER BY FIELD(index, {10, 'str1'}, {20, 'str2'}, {30, 'str3'})", Query{"test_namespace"}.Sort("index", false, std::vector>{{10, "str1"}, {20, "str2"}, {30, "str3"}})}, + {"SELECT * FROM main_ns WHERE (SELECT * FROM second_ns WHERE id < 10 LIMIT 0) IS NOT NULL", + Query{"main_ns"}.Where(Query{"second_ns"}.Where("id", CondLt, 10), CondAny, VariantArray{})}, + {"SELECT * FROM main_ns WHERE id = (SELECT id FROM second_ns WHERE id < 10)", + Query{"main_ns"}.Where("id", CondEq, Query{"second_ns"}.Select({"id"}).Where("id", CondLt, 10))}, + {"SELECT * FROM main_ns WHERE (SELECT max(id) FROM second_ns WHERE id < 10) > 18", + Query{"main_ns"}.Where(Query{"second_ns"}.Aggregate(AggMax, {"id"}).Where("id", CondLt, 10), CondGt, {18})}, + {"SELECT * FROM main_ns WHERE id > (SELECT avg(id) FROM second_ns WHERE id < 10)", + Query{"main_ns"}.Where("id", CondGt, Query{"second_ns"}.Aggregate(AggAvg, {"id"}).Where("id", CondLt, 10))}, + {"SELECT * FROM main_ns WHERE id > (SELECT COUNT(*) FROM second_ns WHERE id < 10 LIMIT 0)", + Query{"main_ns"}.Where("id", CondGt, Query{"second_ns"}.Where("id", CondLt, 10).ReqTotal())}, }; for (const auto& [sql, expected, direction] : cases) { @@ -227,20 +251,18 @@ TEST_F(QueriesApi, SqlParseGenerate) { EXPECT_EQ(q.GetSQL(), sql); } if (direction & PARSE) { - Query parsed; try { - parsed.FromSQL(sql); + Query parsed = Query::FromSQL(sql); + EXPECT_EQ(parsed, q) << sql; } catch (const Error& err) { ADD_FAILURE() << "Unexpected error: " << err.what() << "\nSQL: " << sql; continue; } - EXPECT_EQ(parsed, q) << sql; } } else { const Error& expectedErr = std::get(expected); - Query parsed; try { - parsed.FromSQL(sql); + Query parsed = Query::FromSQL(sql); ADD_FAILURE() << "Expected error: " << expectedErr.what() << "\nSQL: " << sql; } catch (const Error& err) { EXPECT_EQ(err.what(), expectedErr.what()) << "\nSQL: " << sql; @@ -256,25 +278,302 @@ TEST_F(QueriesApi, DslGenerateParse) { std::string dsl; std::variant expected; Direction direction = BOTH; - } cases[]{ - {R"({"namespace":")"s + geomNs + - R"(","limit":-1,"offset":0,"req_total":"disabled","explain":false,"type":"select","select_with_rank":false,"select_filter":[],"select_functions":[],"sort":[],"filters":[{"op":"and","cond":"dwithin","field":")" + - kFieldNamePointLinearRTree + R"(","value":[[-9.2,-0.145],0.581]}],"merge_queries":[],"aggregations":[]})", - Query{geomNs}.DWithin(kFieldNamePointLinearRTree, reindexer::Point{-9.2, -0.145}, 0.581)}, - {R"({"namespace":")"s + default_namespace + - R"(","limit":-1,"offset":0,"req_total":"disabled","explain":false,"type":"select","select_with_rank":false,"select_filter":[],"select_functions":[],"sort":[],"filters":[{"op":"and","cond":"gt","first_field":")" + - kFieldNameStartTime + R"(","second_field":")" + kFieldNamePackages + R"("}],"merge_queries":[],"aggregations":[]})", - Query{Query(default_namespace).WhereBetweenFields(kFieldNameStartTime, CondGt, kFieldNamePackages)}}}; + } cases[]{{fmt::sprintf( + R"({ + "namespace": "%s", + "limit": -1, + "offset": 0, + "req_total": "disabled", + "explain": false, + "type": "select", + "select_with_rank": false, + "select_filter": [], + "select_functions": [], + "sort": [], + "filters": [ + { + "op": "and", + "always": true + } + ], + "merge_queries": [], + "aggregations": [] +})", + geomNs), + Query{geomNs}.AppendQueryEntry(OpAnd)}, + {fmt::sprintf( + R"({ + "namespace": "%s", + "limit": -1, + "offset": 0, + "req_total": "disabled", + "explain": false, + "type": "select", + "select_with_rank": false, + "select_filter": [], + "select_functions": [], + "sort": [], + "filters": [ + { + "op": "and", + "cond": "dwithin", + "field": "%s", + "value": [ + [ + -9.2, + -0.145 + ], + 0.581 + ] + } + ], + "merge_queries": [], + "aggregations": [] +})", + geomNs, kFieldNamePointLinearRTree), + Query{geomNs}.DWithin(kFieldNamePointLinearRTree, reindexer::Point{-9.2, -0.145}, 0.581)}, + {fmt::sprintf( + R"({ + "namespace": "%s", + "limit": -1, + "offset": 0, + "req_total": "disabled", + "explain": false, + "type": "select", + "select_with_rank": false, + "select_filter": [], + "select_functions": [], + "sort": [], + "filters": [ + { + "op": "and", + "cond": "gt", + "first_field": "%s", + "second_field": "%s" + } + ], + "merge_queries": [], + "aggregations": [] +})", + default_namespace, kFieldNameStartTime, kFieldNamePackages), + Query(default_namespace).WhereBetweenFields(kFieldNameStartTime, CondGt, kFieldNamePackages)}, + {fmt::sprintf( + R"({ + "namespace": "%s", + "limit": -1, + "offset": 0, + "req_total": "disabled", + "explain": false, + "type": "select", + "select_with_rank": false, + "select_filter": [], + "select_functions": [], + "sort": [], + "filters": [ + { + "op": "and", + "cond": "gt", + "subquery": { + "namespace": "%s", + "limit": 10, + "offset": 10, + "req_total": "disabled", + "select_filter": [], + "sort": [], + "filters": [], + "aggregations": [ + { + "type": "max", + "fields": [ + "%s" + ] + } + ] + }, + "value": 18 + } + ], + "merge_queries": [], + "aggregations": [] +})", + default_namespace, joinNs, kFieldNameAge), + Query(default_namespace).Where(Query(joinNs).Aggregate(AggMax, {kFieldNameAge}).Limit(10).Offset(10), CondGt, {18})}, + {fmt::sprintf( + R"({ + "namespace": "%s", + "limit": -1, + "offset": 0, + "req_total": "disabled", + "explain": false, + "type": "select", + "select_with_rank": false, + "select_filter": [], + "select_functions": [], + "sort": [], + "filters": [ + { + "op": "and", + "cond": "any", + "subquery": { + "namespace": "%s", + "limit": 0, + "offset": 0, + "req_total": "disabled", + "select_filter": [], + "sort": [], + "filters": [ + { + "op": "and", + "cond": "eq", + "field": "%s", + "value": 1 + } + ], + "aggregations": [] + } + } + ], + "merge_queries": [], + "aggregations": [] +})", + default_namespace, joinNs, kFieldNameId), + Query(default_namespace).Where(Query(joinNs).Where(kFieldNameId, CondEq, 1), CondAny, {})}, + {fmt::sprintf( + R"({ + "namespace": "%s", + "limit": -1, + "offset": 0, + "req_total": "disabled", + "explain": false, + "type": "select", + "select_with_rank": false, + "select_filter": [], + "select_functions": [], + "sort": [], + "filters": [ + { + "op": "and", + "cond": "eq", + "field": "%s", + "subquery": { + "namespace": "%s", + "limit": -1, + "offset": 0, + "req_total": "disabled", + "select_filter": [ + "%s" + ], + "sort": [], + "filters": [ + { + "op": "and", + "cond": "set", + "field": "%s", + "value": [ + 1, + 10, + 100 + ] + } + ], + "aggregations": [] + } + } + ], + "merge_queries": [], + "aggregations": [] +})", + default_namespace, kFieldNameName, joinNs, kFieldNameName, kFieldNameId), + Query(default_namespace) + .Where(kFieldNameName, CondEq, Query(joinNs).Select({kFieldNameName}).Where(kFieldNameId, CondSet, {1, 10, 100}))}, + {fmt::sprintf( + R"({ + "namespace": "%s", + "limit": -1, + "offset": 0, + "req_total": "disabled", + "explain": false, + "type": "select", + "select_with_rank": false, + "select_filter": [], + "select_functions": [], + "sort": [], + "filters": [ + { + "op": "and", + "cond": "gt", + "field": "%s", + "subquery": { + "namespace": "%s", + "limit": -1, + "offset": 0, + "req_total": "disabled", + "select_filter": [], + "sort": [], + "filters": [], + "aggregations": [ + { + "type": "avg", + "fields": [ + "%s" + ] + } + ] + } + } + ], + "merge_queries": [], + "aggregations": [] +})", + default_namespace, kFieldNameId, joinNs, kFieldNameId), + Query(default_namespace).Where(kFieldNameId, CondGt, Query(joinNs).Aggregate(AggAvg, {kFieldNameId}))}, + {fmt::sprintf( + R"({ + "namespace": "%s", + "limit": -1, + "offset": 0, + "req_total": "disabled", + "explain": false, + "type": "select", + "select_with_rank": false, + "select_filter": [], + "select_functions": [], + "sort": [], + "filters": [ + { + "op": "and", + "cond": "gt", + "field": "%s", + "subquery": { + "namespace": "%s", + "limit": 0, + "offset": 0, + "req_total": "enabled", + "select_filter": [], + "sort": [], + "filters": [], + "aggregations": [] + } + } + ], + "merge_queries": [], + "aggregations": [] +})", + default_namespace, kFieldNameId, joinNs, kFieldNameId), + Query(default_namespace).Where(kFieldNameId, CondGt, Query(joinNs).ReqTotal())}}; for (const auto& [dsl, expected, direction] : cases) { if (std::holds_alternative(expected)) { const Query& q = std::get(expected); if (direction & GEN) { - EXPECT_EQ(q.GetJSON(), dsl); + reindexer::WrSerializer ser; + reindexer::prettyPrintJSON(q.GetJSON(), ser, 3); + EXPECT_EQ(ser.Slice(), dsl); } if (direction & PARSE) { Query parsed; try { - parsed.FromJSON(dsl); + const auto err = parsed.FromJSON(dsl); + ASSERT_TRUE(err.ok()) << err.what() << "\nDSL: " << dsl; } catch (const Error& err) { ADD_FAILURE() << "Unexpected error: " << err.what() << "\nDSL: " << dsl; continue; @@ -285,7 +584,8 @@ TEST_F(QueriesApi, DslGenerateParse) { const Error& expectedErr = std::get(expected); Query parsed; try { - parsed.FromJSON(dsl); + const auto err = parsed.FromJSON(dsl); + ASSERT_TRUE(err.ok()) << err.what(); ADD_FAILURE() << "Expected error: " << expectedErr.what() << "\nDSL: " << dsl; } catch (const Error& err) { EXPECT_EQ(err.what(), expectedErr.what()) << "\nDSL: " << dsl; @@ -294,7 +594,7 @@ TEST_F(QueriesApi, DslGenerateParse) { } } -std::vector generateForcedSortOrder(int maxValue, size_t size) { +static std::vector generateForcedSortOrder(int maxValue, size_t size) { std::set res; while (res.size() < size) res.insert(rand() % maxValue); return {res.cbegin(), res.cend()}; @@ -340,7 +640,7 @@ TEST_F(QueriesApi, StrictModeTest) { const std::string kNotExistingField = "some_random_name123"; QueryResults qr; { - Query query = Query(testSimpleNs).Where(kNotExistingField, CondEmpty, 0); + Query query = Query(testSimpleNs).Where(kNotExistingField, CondEmpty, VariantArray{}); Error err = rt.reindexer->Select(query.Strict(StrictModeNames), qr); EXPECT_EQ(err.code(), errStrictMode); qr.Clear(); @@ -348,7 +648,7 @@ TEST_F(QueriesApi, StrictModeTest) { EXPECT_EQ(err.code(), errStrictMode); qr.Clear(); err = rt.reindexer->Select(query.Strict(StrictModeNone), qr); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); Verify(qr, Query(testSimpleNs), *rt.reindexer); qr.Clear(); } @@ -362,7 +662,7 @@ TEST_F(QueriesApi, StrictModeTest) { EXPECT_EQ(err.code(), errStrictMode); qr.Clear(); err = rt.reindexer->Select(query.Strict(StrictModeNone), qr); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); EXPECT_EQ(qr.Count(), 0); } } @@ -397,18 +697,16 @@ TEST_F(QueriesApi, SQLLeftJoinSerialize) { } { - Query qSql; std::string sqlQ = createQuery(tLeft, tRight, iLeft, iRight, c.first); - qSql.FromSQL(sqlQ); + Query qSql = Query::FromSQL(sqlQ); reindexer::WrSerializer wrSer; qSql.GetSQL(wrSer); ASSERT_EQ(sqlQ, std::string(wrSer.c_str())); } { - Query qSql; std::string sqlQ = createQuery(tRight, tLeft, iRight, iLeft, c.second); - qSql.FromSQL(sqlQ); + Query qSql = Query::FromSQL(sqlQ); ASSERT_EQ(q.GetJSON(), qSql.GetJSON()); reindexer::WrSerializer wrSer; qSql.GetSQL(wrSer); @@ -454,6 +752,7 @@ TEST_F(QueriesApi, JoinByNotIndexField) { unsigned i = 0; for (auto& it : qr) { Item item = it.GetItem(false); + ASSERT_TRUE(item.Status().ok()) << item.Status().what(); VariantArray values = item["id"]; ASSERT_EQ(values.size(), 1); EXPECT_EQ(values[0].As(), expectedIds[i++]); @@ -816,14 +1115,18 @@ TEST_F(QueriesApi, ConvertationStringToDoubleDuringSorting) { std::string print(const reindexer::Query& q, reindexer::QueryResults::Iterator& currIt, reindexer::QueryResults::Iterator& prevIt, const reindexer::QueryResults& qr) { + assertrx(currIt.Status().ok()); std::string res = '\n' + q.GetSQL() + "\ncurr: "; reindexer::WrSerializer ser; - currIt.GetJSON(ser, false); + const auto err = currIt.GetJSON(ser, false); + assertrx(err.ok()); res += ser.Slice(); if (prevIt != qr.end()) { + assertrx(prevIt.Status().ok()); res += "\nprev: "; ser.Reset(); - prevIt.GetJSON(ser, false); + const auto err = prevIt.GetJSON(ser, false); + assertrx(err.ok()); res += ser.Slice(); } return res; @@ -965,3 +1268,75 @@ TEST_F(QueriesApi, SortByFieldWithDifferentTypes) { sortByNsDifferentTypesImpl(nsName, Query{nsName}, ""); } + +TEST_F(QueriesApi, SerializeDeserialize) { + Query q = Query(default_namespace).Where(Query(default_namespace), CondAny, {}); + (void)q; + + Query queries[]{ + Query(default_namespace).Where(Query(default_namespace), CondAny, {}), + Query(default_namespace).Where(kFieldNameUuidArr, CondRange, randHeterogeneousUuidArray(2, 2)), + Query(default_namespace) + .WhereComposite(kCompositeFieldUuidName, CondRange, + {VariantArray::Create(nilUuid(), RandString()), VariantArray::Create(randUuid(), RandString())}), + Query(default_namespace).Where(Query(default_namespace).Where(kFieldNameId, CondEq, 10), CondAny, {}), + Query(default_namespace).Not().Where(Query(default_namespace), CondEmpty, {}), + Query(default_namespace).Where(kFieldNameId, CondLt, Query(default_namespace).Aggregate(AggAvg, {kFieldNameId})), + Query(default_namespace) + .Where(kFieldNameGenre, CondSet, Query(joinNs).Select({kFieldNameGenre}).Where(kFieldNameId, CondSet, {10, 20, 30, 40})), + + Query(default_namespace).Where(Query(joinNs).Select({kFieldNameGenre}).Where(kFieldNameId, CondGt, 10), CondSet, {10, 20, 30, 40}), + Query(default_namespace) + .Where(Query(joinNs).Select({kFieldNameGenre}).Where(kFieldNameId, CondGt, 10).Offset(1), CondSet, {10, 20, 30, 40}), + Query(default_namespace) + .Where(Query(joinNs).Where(kFieldNameId, CondGt, 10).Aggregate(AggMax, {kFieldNameGenre}), CondRange, {48, 50}), + Query(default_namespace).Where(Query(joinNs).Where(kFieldNameId, CondGt, 10).ReqTotal(), CondGt, {50}), + Query(default_namespace) + .Debug(LogTrace) + .Where(kFieldNameGenre, CondEq, 5) + .Not() + .Where(Query(default_namespace).Where(kFieldNameGenre, CondEq, 5), CondAny, {}) + .Or() + .Where(kFieldNameGenre, CondSet, Query(joinNs).Select({kFieldNameGenre}).Where(kFieldNameId, CondSet, {10, 20, 30, 40})) + .Not() + .OpenBracket() + .Where(kFieldNameYear, CondRange, {2001, 2020}) + .Or() + .Where(kFieldNameName, CondLike, RandLikePattern()) + .Or() + .Where(Query(joinNs).Where(kFieldNameYear, CondEq, 2000 + rand() % 210), CondEmpty, {}) + .CloseBracket() + .Or() + .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) + .OpenBracket() + .Where(kFieldNameNumeric, CondLt, std::to_string(600)) + .Not() + .OpenBracket() + .Where(kFieldNamePackages, CondSet, RandIntVector(5, 10000, 50)) + .Where(kFieldNameGenre, CondLt, 6) + .Or() + .Where(kFieldNameId, CondLt, Query(default_namespace).Aggregate(AggAvg, {kFieldNameId})) + .CloseBracket() + .Not() + .Where(Query(joinNs).Where(kFieldNameId, CondGt, 10).Aggregate(AggMax, {kFieldNameGenre}), CondRange, {48, 50}) + .Or() + .Where(kFieldNameYear, CondEq, 10) + .CloseBracket(), + + Query(default_namespace) + .Where(kCompositeFieldIdTemp, CondEq, Query(default_namespace).Select({kCompositeFieldIdTemp}).Where(kFieldNameId, CondGt, 10)), + Query(default_namespace) + .Where(Query(default_namespace).Select({kCompositeFieldUuidName}).Where(kFieldNameId, CondGt, 10), CondRange, + {VariantArray::Create(nilUuid(), RandString()), VariantArray::Create(randUuid(), RandString())}), + Query(default_namespace) + .Where(Query(default_namespace).Select({kCompositeFieldAgeGenre}).Where(kFieldNameId, CondGt, 10).Limit(10), CondLe, + {Variant(VariantArray::Create(rand() % 50, rand() % 50))}), + }; + for (Query& q : queries) { + reindexer::WrSerializer wser; + q.Serialize(wser); + reindexer::Serializer rser(wser.Slice()); + const auto deserializedQuery = Query::Deserialize(rser); + EXPECT_EQ(q, deserializedQuery) << "Origin query:\n" << q.GetSQL() << "\nDeserialized query:\n" << deserializedQuery.GetSQL(); + } +} diff --git a/cpp_src/gtests/tests/unit/replication_config_tests.cc b/cpp_src/gtests/tests/unit/replication_config_tests.cc index cdd348409..acd8704b1 100644 --- a/cpp_src/gtests/tests/unit/replication_config_tests.cc +++ b/cpp_src/gtests/tests/unit/replication_config_tests.cc @@ -35,7 +35,7 @@ class ReplicationConfigTests : public ::testing::Test { enum class ConfigType { File, Namespace }; const std::chrono::milliseconds kReplicationConfLoadDelay = std::chrono::milliseconds(1200); - const std::string kSimpleReplConfigStoragePath = "/tmp/reindex/simple_replicationConf_tests/"; + const std::string kSimpleReplConfigStoragePath = fs::JoinPath(fs::GetTempDir(), "reindex/simple_replicationConf_tests/"); const std::string kStoragePath = kSimpleReplConfigStoragePath; const std::string kBuiltin = "builtin://" + kStoragePath; const std::string kConfigNs = "#config"; diff --git a/cpp_src/gtests/tests/unit/replication_test.cc b/cpp_src/gtests/tests/unit/replication_test.cc index b7121528d..63eb540b5 100644 --- a/cpp_src/gtests/tests/unit/replication_test.cc +++ b/cpp_src/gtests/tests/unit/replication_test.cc @@ -348,6 +348,88 @@ TEST_F(ReplicationLoadApi, ConfigReadingOnStartup) { CheckReplicationConfigNamespace(kTestServerID, config); } +TEST_F(ReplicationLoadApi, DuplicatePKFollowerTest) { + InitNs(); + const unsigned int kItemCount = 5; + auto srv = GetSrv(masterId_); + auto& api = srv->api; + + std::string changedIds; + const unsigned int kChangedCount = 2; + std::unordered_set ids; + for (unsigned i = 0; i < kChangedCount; ++i) { + ids.insert(std::rand() % kItemCount); + } + + bool isFirst = true; + for (const auto id : ids) { + if (!isFirst) changedIds += ", "; + changedIds += std::to_string(id); + isFirst = false; + } + + std::unordered_map> items; + Error err; + for (size_t i = 0; i < kItemCount; ++i) { + std::string jsonChange; + BaseApi::ItemType item = api.NewItem("some"); + auto json = fmt::sprintf(R"json({"id":%d,"int":%d,"string":"%s","uuid":"%s"})json", i, i + 100, std::to_string(1 + 1000), nilUUID); + err = item.FromJSON(json); + api.Upsert("some", item); + jsonChange = json; + int idNew = i; + if (ids.find(i) != ids.end()) { + jsonChange = fmt::sprintf(R"json({"id":%d,"int":%d,"string":"%s","uuid":"%s"})json", kItemCount * 2 + i, i + 100, + std::to_string(1 + 1000), nilUUID); + idNew = kItemCount * 2 + i; + } + items.emplace(idNew, std::make_pair(json, jsonChange)); + } + + WaitSync("some"); + { + BaseApi::QueryResultsType qr; + err = api.reindexer->Select("Update some set id=id+" + std::to_string(kItemCount * 2) + " where id in(" + changedIds + ")", qr); + ASSERT_TRUE(err.ok()) << err.what(); + WaitSync("some"); + } + + for (size_t k = 0; k < GetServersCount(); k++) { + auto server = GetSrv(k); + { + BaseApi::QueryResultsType qr; + err = server->api.reindexer->Select("select * from some order by id", qr); + ASSERT_TRUE(err.ok()) << err.what(); + ASSERT_EQ(qr.Count(), items.size()); + for (auto i : qr) { + WrSerializer ser; + err = i.GetJSON(ser, false); + gason::JsonParser parser; + auto root = parser.Parse(ser.Slice()); + int id = root["id"].As(); + ASSERT_TRUE(err.ok()) << err.what(); + ASSERT_EQ(ser.Slice(), items[id].second); + } + } + { + for (auto id : ids) { + BaseApi::QueryResultsType qr; + err = server->api.reindexer->Select("select * from some where id=" + std::to_string(id), qr); + ASSERT_TRUE(err.ok()) << err.what(); + ASSERT_EQ(qr.Count(), 0); + } + } + { + for (auto id : ids) { + BaseApi::QueryResultsType qr; + err = server->api.reindexer->Select("select * from some where id=" + std::to_string(id + kItemCount * 2), qr); + ASSERT_TRUE(err.ok()) << err.what(); + ASSERT_EQ(qr.Count(), 1); + } + } + } +} + TEST_F(ReplicationLoadApi, ConfigSync) { // Check automatic replication config file and #config namespace sync using ReplNode = AsyncReplicationConfigTest::Node; diff --git a/cpp_src/gtests/tests/unit/rpcclient_test.cc b/cpp_src/gtests/tests/unit/rpcclient_test.cc index 12d807d21..6918b524e 100644 --- a/cpp_src/gtests/tests/unit/rpcclient_test.cc +++ b/cpp_src/gtests/tests/unit/rpcclient_test.cc @@ -29,17 +29,17 @@ TEST_F(RPCClientTestApi, CoroRequestTimeout) { config.NetTimeout = seconds(1); reindexer::client::CoroReindexer rx(config); auto err = rx.Connect(std::string("cproto://") + kDefaultRPCServerAddr + "/test_db", loop); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); const std::string kNamespaceName = "MyNamespace"; err = rx.AddNamespace(reindexer::NamespaceDef(kNamespaceName)); EXPECT_EQ(err.code(), errTimeout); loop.sleep(std::chrono::seconds(4)); err = rx.DropNamespace(kNamespaceName); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); }); loop.run(); Error err = StopServer(); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); } static std::chrono::seconds GetMaxTimeForCoroSelectTimeout(unsigned requests, std::chrono::seconds delay) { @@ -112,16 +112,16 @@ TEST_F(RPCClientTestApi, CoroSelectTimeout) { [&] { return server.CloseQRRequestsCount() >= kCorCount * kQueriesCount; }); EXPECT_EQ(server.CloseQRRequestsCount(), kCorCount * kQueriesCount); err = rx.AddNamespace(reindexer::NamespaceDef(kNamespaceName + std::to_string(index))); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); finished[index] = true; }); } loop.run(); for (size_t i = 0; i < kCorCount; ++i) { - EXPECT_TRUE(finished[i]); + ASSERT_TRUE(finished[i]); } Error const err = StopServer(); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); } TEST_F(RPCClientTestApi, CoroRequestCancels) { @@ -132,7 +132,7 @@ TEST_F(RPCClientTestApi, CoroRequestCancels) { loop.spawn([&loop]() noexcept { reindexer::client::CoroReindexer rx; auto err = rx.Connect(std::string("cproto://") + kDefaultRPCServerAddr + "/test_db", loop); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); { CancelRdxContext ctx; @@ -156,7 +156,7 @@ TEST_F(RPCClientTestApi, CoroRequestCancels) { }); loop.run(); Error err = StopServer(); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); } TEST_F(RPCClientTestApi, CoroSuccessfullRequestWithTimeout) { @@ -169,13 +169,13 @@ TEST_F(RPCClientTestApi, CoroSuccessfullRequestWithTimeout) { config.NetTimeout = seconds(6); reindexer::client::CoroReindexer rx(config); auto err = rx.Connect(std::string("cproto://") + kDefaultRPCServerAddr + "/test_db", loop); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); err = rx.AddNamespace(reindexer::NamespaceDef("MyNamespace")); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); }); loop.run(); Error err = StopServer(); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); } TEST_F(RPCClientTestApi, CoroErrorLoginResponse) { @@ -192,7 +192,7 @@ TEST_F(RPCClientTestApi, CoroErrorLoginResponse) { }); loop.run(); Error err = StopServer(); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); } TEST_F(RPCClientTestApi, CoroStatus) { @@ -212,7 +212,7 @@ TEST_F(RPCClientTestApi, CoroStatus) { err = rx.Status(); ASSERT_TRUE(err.ok()) << err.what(); err = StopServer(); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); loop.sleep(std::chrono::milliseconds(20)); // Allow reading coroutine to handle disconnect err = rx.Status(); ASSERT_EQ(err.code(), errNetwork) << err.what(); @@ -322,8 +322,7 @@ TEST_F(RPCClientTestApi, CoroUpserts) { for (auto& it : qr) { ASSERT_TRUE(it.Status().ok()) << it.Status().what(); } - err = rx.Stop(); - ASSERT_TRUE(err.ok()) << err.what(); + rx.Stop(); }); loop.run(); @@ -352,8 +351,7 @@ void ReconnectTest(RxT& rx, RPCClientTestApi& api, size_t dataCount, const std:: ASSERT_TRUE(err.ok()) << err.what(); ASSERT_EQ(qr.Count(), dataCount); - err = rx.Stop(); - ASSERT_TRUE(err.ok()) << err.what(); + rx.Stop(); } TEST_F(RPCClientTestApi, Reconnect) { @@ -496,8 +494,7 @@ TEST_F(RPCClientTestApi, ServerRestart) { ready = true; wg.wait(); - err = rx.Stop(); - ASSERT_TRUE(err.ok()) << err.what(); + rx.Stop(); }); loop.run(); @@ -509,7 +506,7 @@ TEST_F(RPCClientTestApi, ServerRestart) { // Shutdown server step = Step::ShutdownInProgress; Error err = StopServer(); - EXPECT_TRUE(err.ok()) << err.what(); + ASSERT_TRUE(err.ok()) << err.what(); step = Step::ShutdownDone; std::this_thread::sleep_for(std::chrono::milliseconds(300)); @@ -553,8 +550,7 @@ TEST_F(RPCClientTestApi, TemporaryNamespaceAutoremove) { ASSERT_EQ(nsList[0].name, tmpNsName); // Reconnect - err = rx.Stop(); - ASSERT_TRUE(err.ok()) << err.what(); + rx.Stop(); err = rx.Connect(dsn, loop, opts); ASSERT_TRUE(err.ok()) << err.what(); @@ -570,8 +566,7 @@ TEST_F(RPCClientTestApi, TemporaryNamespaceAutoremove) { ASSERT_TRUE(false); } - err = rx.Stop(); - ASSERT_TRUE(err.ok()) << err.what(); + rx.Stop(); }); loop.run(); @@ -776,8 +771,7 @@ TEST_F(RPCClientTestApi, FetchingWithJoin) { EXPECT_EQ(ser.Slice(), expected); i++; } - err = rx.Stop(); - ASSERT_TRUE(err.ok()) << err.what(); + rx.Stop(); }); loop.run(); @@ -843,8 +837,7 @@ TEST_F(RPCClientTestApi, QRWithMultipleIterationLoops) { } ++id; } - err = rx.Stop(); - ASSERT_TRUE(err.ok()) << err.what(); + rx.Stop(); }); loop.run(); @@ -904,8 +897,7 @@ TEST_F(RPCClientTestApi, AggregationsFetching) { } } - err = rx.Stop(); - ASSERT_TRUE(err.ok()) << err.what(); + rx.Stop(); }); loop.run(); @@ -985,8 +977,7 @@ TEST_F(RPCClientTestApi, AggregationsFetchingWithLazyMode) { EXPECT_EQ(qr.TotalCount(), kItemsCount); // Total count is still available } - err = rx.Stop(); - ASSERT_TRUE(err.ok()) << err.what(); + rx.Stop(); }); loop.run(); @@ -1012,3 +1003,72 @@ TEST_F(RPCClientTestApi, AggregationsWithStrictModeTest) { loop.run(); } + +TEST_F(RPCClientTestApi, SubQuery) { + using namespace reindexer::client; + using namespace reindexer::net::ev; + using reindexer::coroutine::wait_group; + using reindexer::coroutine::wait_group_guard; + + StartDefaultRealServer(); + dynamic_loop loop; + + loop.spawn([&loop, this]() noexcept { + const std::string kLeftNsName = "left_ns"; + const std::string kRightNsName = "right_ns"; + const std::string dsn = "cproto://" + kDefaultRPCServerAddr + "/db1"; + reindexer::client::ConnectOpts opts; + opts.CreateDBIfMissing(); + reindexer::client::ReindexerConfig cfg; + constexpr auto kFetchCount = 50; + constexpr auto kNsSize = kFetchCount * 3; + cfg.FetchAmount = kFetchCount; + CoroReindexer rx(cfg); + auto err = rx.Connect(dsn, loop, opts); + ASSERT_TRUE(err.ok()) << err.what(); + + CreateNamespace(rx, kLeftNsName); + CreateNamespace(rx, kRightNsName); + + auto upsertFn = [&rx](const std::string& nsName) { + for (size_t i = 0; i < kNsSize; ++i) { + auto item = rx.NewItem(nsName); + ASSERT_TRUE(item.Status().ok()) << nsName << " " << item.Status().what(); + + WrSerializer wrser; + JsonBuilder jsonBuilder(wrser, ObjType::TypeObject); + jsonBuilder.Put("id", i); + jsonBuilder.Put("value", "value_" + std::to_string(i)); + jsonBuilder.End(); + char* endp = nullptr; + auto err = item.Unsafe().FromJSON(wrser.Slice(), &endp); + ASSERT_TRUE(err.ok()) << nsName << " " << err.what(); + err = rx.Upsert(nsName, item); + ASSERT_TRUE(err.ok()) << nsName << " " << err.what(); + } + }; + + upsertFn(kLeftNsName); + upsertFn(kRightNsName); + + const auto kHalfSize = kNsSize / 2; + { + client::CoroQueryResults qr; + err = rx.Select(Query(kLeftNsName).Where("id", CondSet, Query(kRightNsName).Select({"id"}).Where("id", CondLt, kHalfSize)), qr); + ASSERT_TRUE(err.ok()) << err.what(); + ASSERT_EQ(qr.Count(), kHalfSize); + } + { + const int limit = 10; + client::CoroQueryResults qr; + err = rx.Select( + Query(kLeftNsName).Where(Query(kRightNsName).Where("id", CondLt, kHalfSize).ReqTotal(), CondEq, {kHalfSize}).Limit(limit), + qr); + ASSERT_TRUE(err.ok()) << err.what(); + ASSERT_EQ(qr.Count(), limit); + } + rx.Stop(); + }); + + loop.run(); +} diff --git a/cpp_src/gtests/tests/unit/rtree_test.cc b/cpp_src/gtests/tests/unit/rtree_test.cc index c66caebc5..a6c0f59c4 100644 --- a/cpp_src/gtests/tests/unit/rtree_test.cc +++ b/cpp_src/gtests/tests/unit/rtree_test.cc @@ -7,7 +7,7 @@ #include "core/index/rtree/rstarsplitter.h" #include "gtest/gtest.h" #include "reindexer_api.h" -#include "tools/random.h" +#include "tools/randompoint.h" namespace { @@ -72,7 +72,7 @@ static void TestInsert() { size_t insertedCount = 0; for (size_t i = 0; i < 10000; ++i) { - const auto p = randPoint(kRange); + const auto p = reindexer::randPoint(kRange); const auto insertRes{tree.insert(reindexer::Point{p})}; if (insertRes.second) { ++insertedCount; @@ -100,7 +100,7 @@ static void TestIterators() { size_t dublicates = 0; for (size_t i = 0; i < 10000 + dublicates; ++i) { - const auto res = tree.insert(randPoint(kRange)); + const auto res = tree.insert(reindexer::randPoint(kRange)); if (!res.second) ++dublicates; ASSERT_TRUE(tree.Check()); auto it = tree.begin(), end = tree.end(); @@ -129,6 +129,8 @@ TEST(RTree, RStarIterators) { TestIterators(); } template