From 48ff5e1a4ae125ce922a6496a779196a959ceeb1 Mon Sep 17 00:00:00 2001 From: Georgii Novoselov <22564079+farost@users.noreply.github.com> Date: Wed, 23 Oct 2024 17:52:22 +0100 Subject: [PATCH] Introduce 3.0 Python driver docs and tests. Implement new fetch BDD steps in 3.0 drivers (#700) ## Usage and product changes We introduce updated documentation, usage examples, and automated tests for the Java driver, to cover all the existing driver's APIs by sustainable validations. Additionally, we implement additional BDD steps to check concept documents in BDDs for other 3.0 drivers: Rust and Python. As a small bonus, the protocol has been updated, and `Ok` `QueryAnswer`s receive correct `QueryType`s from the server instead of the client-side hardcode usage. ## Implementation For behaviour tests, the already existing architecture is preserved, and only the steps are rewritten to match the new definitions. For integration tests, the new approach of the Python driver is chosen, and all the excessive tests are removed. Now, we only write `example` tests and tests to cover language-specific values (especially our implementations and their interfaces). For docs, the old instruments are used without changes. For `README`, the `example` integration test parsing instrument is used as in Rust and Python. --- .factory/automation.yml | 58 +- dependencies/vaticle/artifacts.bzl | 2 +- dependencies/vaticle/repositories.bzl | 4 +- .../java/answer/ConceptDocumentIterator.adoc | 75 + .../ROOT/partials/java/answer/ConceptRow.adoc | 14 +- .../java/answer/ConceptRowIterator.adoc | 28 +- .../partials/java/answer/ConceptTree.adoc | 7 - .../java/answer/ConceptTreeIterator.adoc | 59 - .../partials/java/answer/OkQueryAnswer.adoc | 83 +- .../partials/java/answer/QueryAnswer.adoc | 69 +- .../ROOT/partials/java/concept/Concept.adoc | 144 +- .../ROOT/partials/java/connection/Driver.adoc | 4 +- .../ROOT/partials/java/data/Attribute.adoc | 468 ++++++- .../ROOT/partials/java/data/Entity.adoc | 72 +- .../java/data/{Thing.adoc => Instance.adoc} | 186 ++- .../ROOT/partials/java/data/Relation.adoc | 72 +- .../ROOT/partials/java/data/Value.adoc | 164 +-- .../partials/java/schema/AttributeType.adoc | 94 +- .../ROOT/partials/java/schema/EntityType.adoc | 88 +- .../ROOT/partials/java/schema/Label.adoc | 195 --- .../partials/java/schema/RelationType.adoc | 88 +- .../ROOT/partials/java/schema/RoleType.adoc | 132 +- .../ROOT/partials/java/schema/ThingType.adoc | 464 ------- .../ROOT/partials/java/schema/Type.adoc | 137 +- .../java/transaction/Transaction.adoc | 2 +- .../ROOT/partials/java/value/Duration.adoc | 225 +++ .../partials/rust/answer/QueryAnswer.adoc | 2 +- java/BUILD | 27 + java/README.md | 192 +++ java/api/answer/JSON.java | 12 +- java/common/Duration.java | 71 + java/common/exception/ErrorMessage.java | 10 +- java/concept/value/ValueImpl.java | 22 +- java/connection/DatabaseManagerImpl.java | 4 - java/core_example | 183 +++ java/docs_structure.bzl | 16 +- java/test/behaviour/config/Parameters.java | 270 +++- java/test/behaviour/connection/BUILD | 2 + .../connection/ConnectionStepsBase.java | 49 +- .../connection/ConnectionStepsCloud.java | 10 +- .../connection/ConnectionStepsCore.java | 46 +- java/test/behaviour/connection/database/BUILD | 1 + .../connection/database/DatabaseSteps.java | 18 +- .../transaction/TransactionSteps.java | 138 +- java/test/behaviour/connection/user/BUILD | 36 +- .../behaviour/driver/{query => driver}/BUILD | 4 +- .../QueryTest.java => driver/DriverTest.java} | 4 +- java/test/behaviour/query/QuerySteps.java | 1220 +++++++++++++---- java/test/behaviour/util/Util.java | 13 +- java/test/behaviour/util/UtilSteps.java | 2 +- .../integration/AddressTranslationTest.java | 48 - java/test/integration/BUILD | 46 - java/test/integration/cloud/BUILD | 24 + java/test/integration/concept/.gitkeep | 1 - java/test/integration/core/BUILD | 76 + java/test/integration/core/ExampleTest.java | 278 ++++ .../ValueTest.java} | 305 ++--- python/README.md | 2 +- python/core_example | 2 +- python/tests/behaviour/config/parameters.py | 13 + python/tests/behaviour/query/query_steps.py | 64 +- python/tests/behaviour/util/util.py | 7 + python/tests/integration/core/test_example.py | 3 +- rust/README.md | 2 +- rust/core_example | 2 +- rust/src/answer/mod.rs | 8 +- rust/src/connection/message.rs | 3 +- rust/src/connection/network/proto/message.rs | 4 +- rust/src/connection/transaction_stream.rs | 2 +- rust/tests/behaviour/steps/lib.rs | 73 +- rust/tests/behaviour/steps/params.rs | 37 +- rust/tests/behaviour/steps/query.rs | 53 +- rust/tests/behaviour/steps/util.rs | 152 +- rust/tests/integration/core/BUILD | 5 + rust/tests/integration/core/example.rs | 2 +- tool/docs/update.sh | 2 +- 76 files changed, 4056 insertions(+), 2444 deletions(-) create mode 100644 docs/modules/ROOT/partials/java/answer/ConceptDocumentIterator.adoc delete mode 100644 docs/modules/ROOT/partials/java/answer/ConceptTree.adoc delete mode 100644 docs/modules/ROOT/partials/java/answer/ConceptTreeIterator.adoc rename docs/modules/ROOT/partials/java/data/{Thing.adoc => Instance.adoc} (78%) delete mode 100644 docs/modules/ROOT/partials/java/schema/Label.adoc delete mode 100644 docs/modules/ROOT/partials/java/schema/ThingType.adoc create mode 100644 docs/modules/ROOT/partials/java/value/Duration.adoc create mode 100644 java/core_example rename java/test/behaviour/driver/{query => driver}/BUILD (97%) rename java/test/behaviour/driver/{query/QueryTest.java => driver/DriverTest.java} (95%) delete mode 100644 java/test/integration/AddressTranslationTest.java create mode 100644 java/test/integration/cloud/BUILD delete mode 100644 java/test/integration/concept/.gitkeep create mode 100644 java/test/integration/core/BUILD create mode 100644 java/test/integration/core/ExampleTest.java rename java/test/integration/{DriverQueryTest.java => core/ValueTest.java} (56%) diff --git a/.factory/automation.yml b/.factory/automation.yml index 8e56483a52..7491959b22 100644 --- a/.factory/automation.yml +++ b/.factory/automation.yml @@ -74,8 +74,7 @@ build: sudo mv /var/tmp/doxygen /usr/local/bin/ && sudo chmod +x /usr/local/bin/doxygen bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh # TODO: Temporarily update only 3.0 drivers - # DOCS_DIRS="docs/modules/ROOT/partials/rust docs/modules/ROOT/partials/java docs/modules/ROOT/partials/python docs/modules/ROOT/partials/nodejs docs/modules/ROOT/partials/cpp docs/modules/ROOT/partials/c docs/modules/ROOT/partials/csharp" - DOCS_DIRS=("docs/modules/ROOT/partials/rust" "docs/modules/ROOT/partials/python") + DOCS_DIRS=("docs/modules/ROOT/partials/rust" "docs/modules/ROOT/partials/java" "docs/modules/ROOT/partials/python") find "${DOCS_DIRS[@]}" -type f ! -name 'api-reference.adoc' -exec rm -f {} \; tool/docs/update.sh git add "${DOCS_DIRS[@]}" @@ -169,21 +168,27 @@ build: tool/test/stop-core-server.sh exit $TEST_SUCCESS -# test-java-behaviour-core: -# image: vaticle-ubuntu-22.04 -# dependencies: -# - build -# command: | -# export ARTIFACT_USERNAME=$REPO_TYPEDB_USERNAME -# export ARTIFACT_PASSWORD=$REPO_TYPEDB_PASSWORD -# bazel run @vaticle_dependencies//distribution/artifact:create-netrc -# bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh -# .factory/test-core.sh //java/test/behaviour/connection/... --test_output=errors --jobs=1 -# # TODO: delete --jobs=1 if we fix the issue with excess memory usage -# .factory/test-core.sh //java/test/behaviour/concept/... --test_output=errors -# .factory/test-core.sh //java/test/behaviour/driver/query/... --test_output=errors -# .factory/test-core.sh //java/test/behaviour/query/language/... --test_output=errors --jobs=1 -# +# source tool/test/start-cloud-servers.sh 3 && # use source to receive export vars +# .factory/test-cloud.sh //java/test/integration/... --test_env=ROOT_CA=$ROOT_CA --test_output=errors --jobs=1 && +# export TEST_SUCCESS=0 || export TEST_SUCCESS=1 +# tool/test/stop-cloud-servers.sh +# exit $TEST_SUCCESS + + test-java-behaviour-core: + image: vaticle-ubuntu-22.04 + dependencies: + - build + command: | + export ARTIFACT_USERNAME=$REPO_TYPEDB_USERNAME + export ARTIFACT_PASSWORD=$REPO_TYPEDB_PASSWORD + bazel run @vaticle_dependencies//distribution/artifact:create-netrc + bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh + + tool/test/start-core-server.sh && + .factory/test-core.sh //java/test/behaviour/... --test_output=errors --jobs=1 && + export TEST_SUCCESS=0 || export TEST_SUCCESS=1 + tool/test/stop-core-server.sh + # test-java-behaviour-cloud: # image: vaticle-ubuntu-22.04 # dependencies: @@ -193,11 +198,12 @@ build: # export ARTIFACT_PASSWORD=$REPO_TYPEDB_PASSWORD # bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh # bazel run @vaticle_dependencies//distribution/artifact:create-netrc -# .factory/test-cloud.sh //java/test/behaviour/connection/... --test_output=errors --jobs=1 -# # TODO: delete --jobs=1 if we fix the issue with excess memory usage -# .factory/test-cloud.sh //java/test/behaviour/concept/... --test_output=errors -# .factory/test-cloud.sh //java/test/behaviour/driver/query/... --test_output=errors -# .factory/test-cloud.sh //java/test/behaviour/query/language/... --test_output=errors --jobs=1 +# +# source tool/test/start-cloud-servers.sh 3 && # use source to receive export vars +# .factory/test-cloud.sh //java/test/behaviour/... --test_env=ROOT_CA=$ROOT_CA --test_output=errors --jobs=1 && +# export TEST_SUCCESS=0 || export TEST_SUCCESS=1 +# tool/test/stop-cloud-servers.sh +# exit $TEST_SUCCESS test-python-integration: image: vaticle-ubuntu-22.04 @@ -253,7 +259,7 @@ build: # bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh # bazel run @vaticle_dependencies//distribution/artifact:create-netrc # -# source tool/test/start-cloud-servers.sh && # use source to receive export vars +# source tool/test/start-cloud-servers.sh 3 && # use source to receive export vars # .factory/test-cloud.sh //python/tests/behaviour/... --test_env=ROOT_CA=$ROOT_CA --test_output=streamed --jobs=1 && # export TEST_SUCCESS=0 || export TEST_SUCCESS=1 # tool/test/stop-cloud-servers.sh @@ -312,7 +318,7 @@ build: # export ARTIFACT_USERNAME=$REPO_TYPEDB_USERNAME # export ARTIFACT_PASSWORD=$REPO_TYPEDB_PASSWORD # bazel run @vaticle_dependencies//distribution/artifact:create-netrc -# source tool/test/start-cloud-servers.sh && # use source to receive export vars +# source tool/test/start-cloud-servers.sh 3 && # use source to receive export vars # .factory/test-cloud.sh //nodejs/test/behaviour/connection/database/... --test_env=ROOT_CA=$ROOT_CA --test_output=errors --jobs=1 && # .factory/test-cloud.sh //nodejs/test/behaviour/connection/session/... --test_env=ROOT_CA=$ROOT_CA --test_output=errors --jobs=1 && # .factory/test-cloud.sh //nodejs/test/behaviour/connection/transaction/... --test_env=ROOT_CA=$ROOT_CA --test_output=errors --jobs=1 && @@ -381,7 +387,7 @@ build: # export ARTIFACT_PASSWORD=$REPO_TYPEDB_PASSWORD # bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh # bazel run @vaticle_dependencies//distribution/artifact:create-netrc -# source tool/test/start-cloud-servers.sh && +# source tool/test/start-cloud-servers.sh 3 && # .factory/test-cloud.sh //cpp/test/behaviour/connection/database/... --test_env=ROOT_CA=$ROOT_CA --test_output=streamed --jobs=1 && # .factory/test-cloud.sh //cpp/test/behaviour/connection/session/... --test_env=ROOT_CA=$ROOT_CA --test_output=streamed --jobs=1 && # .factory/test-cloud.sh //cpp/test/behaviour/connection/transaction/... --test_env=ROOT_CA=$ROOT_CA --test_output=streamed --jobs=1 && @@ -452,7 +458,7 @@ build: # export ARTIFACT_PASSWORD=$REPO_TYPEDB_PASSWORD # bazel run @vaticle_dependencies//tool/bazelinstall:remote_cache_setup.sh # bazel run @vaticle_dependencies//distribution/artifact:create-netrc -# source tool/test/start-cloud-servers.sh && +# source tool/test/start-cloud-servers.sh 3 && # .factory/test-cloud.sh //csharp/Test/Behaviour/Connection/Database/... --test_env=ROOT_CA=$ROOT_CA --test_output=errors --jobs=1 && # .factory/test-cloud.sh //csharp/Test/Behaviour/Connection/Session/... --test_env=ROOT_CA=$ROOT_CA --test_output=errors --jobs=1 && # .factory/test-cloud.sh //csharp/Test/Behaviour/Connection/Transaction/... --test_env=ROOT_CA=$ROOT_CA --test_output=errors --jobs=1 && diff --git a/dependencies/vaticle/artifacts.bzl b/dependencies/vaticle/artifacts.bzl index da9721f77f..3608c7f03a 100644 --- a/dependencies/vaticle/artifacts.bzl +++ b/dependencies/vaticle/artifacts.bzl @@ -25,7 +25,7 @@ def vaticle_typedb_artifact(): artifact_name = "typedb-all-{platform}-{version}.{ext}", tag_source = deployment["artifact"]["release"]["download"], commit_source = deployment["artifact"]["snapshot"]["download"], - commit = "b52a7e6da7e59fd32aea1c56d583ecc73ae62412" + commit = "2c110dc1dce03270842b3a92f859f0b4a7a18cb4" ) #def vaticle_typedb_cloud_artifact(): diff --git a/dependencies/vaticle/repositories.bzl b/dependencies/vaticle/repositories.bzl index 68ccee9505..9786c59681 100644 --- a/dependencies/vaticle/repositories.bzl +++ b/dependencies/vaticle/repositories.bzl @@ -28,12 +28,12 @@ def vaticle_typedb_protocol(): git_repository( name = "vaticle_typedb_protocol", remote = "https://github.com/typedb/typedb-protocol", - tag = "3.0.0-alpha-6", # sync-marker: do not remove this comment, this is used for sync-dependencies by @vaticle_typedb_protocol + commit = "56eb6ef6db0d0721800f46512e2ee5e02aad6adb", # sync-marker: do not remove this comment, this is used for sync-dependencies by @vaticle_typedb_protocol ) def vaticle_typedb_behaviour(): git_repository( name = "vaticle_typedb_behaviour", remote = "https://github.com/typedb/typedb-behaviour", - commit = "52d3aca4b833865861550262edd760a9ca592be8", # sync-marker: do not remove this comment, this is used for sync-dependencies by @vaticle_typedb_behaviour + commit = "4ec10ad5a5f9a7fd19b327240a33bcb77e502f57", # sync-marker: do not remove this comment, this is used for sync-dependencies by @vaticle_typedb_behaviour ) diff --git a/docs/modules/ROOT/partials/java/answer/ConceptDocumentIterator.adoc b/docs/modules/ROOT/partials/java/answer/ConceptDocumentIterator.adoc new file mode 100644 index 0000000000..014a39bb09 --- /dev/null +++ b/docs/modules/ROOT/partials/java/answer/ConceptDocumentIterator.adoc @@ -0,0 +1,75 @@ +[#_ConceptDocumentIterator] +=== ConceptDocumentIterator + +*Package*: `com.typedb.driver.api.answer` + +*Superinterfaces:* + +* `java.util.Iterator` +* `QueryAnswer` + +Represents an iterator over concept documents (represented as ``JSON``s) returned as a server answer. + +// tag::methods[] +[#_ConceptDocumentIterator_asConceptDocuments_] +==== asConceptDocuments + +[source,java] +---- +@CheckReturnValue +default ConceptDocumentIterator asConceptDocuments() +---- + +Casts the query answer to ``ConceptDocumentIterator``. + + +[caption=""] +.Returns +`ConceptDocumentIterator` + +[caption=""] +.Code examples +[source,java] +---- +concept.asConceptDocuments(); +---- + +[#_ConceptDocumentIterator_isConceptDocuments_] +==== isConceptDocuments + +[source,java] +---- +default boolean isConceptDocuments() +---- + +Checks if the query answer is a ``ConceptDocumentIterator``. + + +[caption=""] +.Returns +`boolean` + +[caption=""] +.Code examples +[source,java] +---- +concept.isConceptDocuments(); +---- + +[#_ConceptDocumentIterator_stream_] +==== stream + +[source,java] +---- +@CheckReturnValue +java.util.stream.Stream stream() +---- + + + +[caption=""] +.Returns +`java.util.stream.Stream` + +// end::methods[] + diff --git a/docs/modules/ROOT/partials/java/answer/ConceptRow.adoc b/docs/modules/ROOT/partials/java/answer/ConceptRow.adoc index f3a2f928ab..ce97856435 100644 --- a/docs/modules/ROOT/partials/java/answer/ConceptRow.adoc +++ b/docs/modules/ROOT/partials/java/answer/ConceptRow.adoc @@ -15,7 +15,7 @@ Contains a row of concepts with a header. java.util.stream.Stream columnNames() ---- -Produces a stream over all column names (header) in this ``ConceptRow``. Shared between all the rows in a QueryAnswer. +Produces a stream over all column names (variables) in the header of this ``ConceptRow``. Shared between all the rows in a QueryAnswer. [caption=""] @@ -26,7 +26,7 @@ Produces a stream over all column names (header) in this ``ConceptRow``. Shared .Code examples [source,java] ---- -conceptRow.header(); +conceptRow.columnNames(); ---- [#_ConceptRow_concepts_] @@ -38,7 +38,7 @@ conceptRow.header(); java.util.stream.Stream concepts() ---- -Produces an iterator over all concepts in this `ConceptRow`, skipping empty results. +Produces a stream over all concepts in this `ConceptRow`, skipping empty results. [caption=""] @@ -61,7 +61,7 @@ conceptRow.concepts(); Concept get​(java.lang.String columnName) ---- -Retrieves a concept for a given variable name. +Retrieves a concept for a given column name (variable). [caption=""] @@ -70,7 +70,7 @@ Retrieves a concept for a given variable name. [options="header"] |=== |Name |Description |Type -a| `columnName` a| the column name (header) a| `java.lang.String` +a| `columnName` a| the variable (column name from ``column_names``) a| `java.lang.String` |=== [caption=""] @@ -93,7 +93,7 @@ conceptRow.get(columnName); Concept getIndex​(long columnIndex) ---- -Retrieves a concept for a given variable name. +Retrieves a concept for a given index of the header (``columnNames``). [caption=""] @@ -136,7 +136,7 @@ Retrieves the executed query's type of this ``ConceptRow``. Shared between all t .Code examples [source,java] ---- -conceptRow.queryType(); +conceptRow.getQueryType(); ---- // end::methods[] diff --git a/docs/modules/ROOT/partials/java/answer/ConceptRowIterator.adoc b/docs/modules/ROOT/partials/java/answer/ConceptRowIterator.adoc index a104358e9d..5448958643 100644 --- a/docs/modules/ROOT/partials/java/answer/ConceptRowIterator.adoc +++ b/docs/modules/ROOT/partials/java/answer/ConceptRowIterator.adoc @@ -8,6 +8,8 @@ * `java.util.Iterator` * `QueryAnswer` +Represents an iterator over ``ConceptRow``s returned as a server answer. + // tag::methods[] [#_ConceptRowIterator_asConceptRows_] ==== asConceptRows @@ -18,13 +20,20 @@ default ConceptRowIterator asConceptRows() ---- -Casts the query answer to ``ConceptRowStreamQueryAnswer``. +Casts the query answer to ``ConceptRowIterator``. [caption=""] .Returns `ConceptRowIterator` +[caption=""] +.Code examples +[source,java] +---- +concept.asConceptRows(); +---- + [#_ConceptRowIterator_isConceptRows_] ==== isConceptRows @@ -33,13 +42,20 @@ Casts the query answer to ``ConceptRowStreamQueryAnswer``. default boolean isConceptRows() ---- -Checks if the query answer is a ``ConceptRowStream``. +Checks if the query answer is a ``ConceptRowIterator``. [caption=""] .Returns `boolean` +[caption=""] +.Code examples +[source,java] +---- +concept.isConceptRows(); +---- + [#_ConceptRowIterator_stream_] ==== stream @@ -49,11 +65,19 @@ Checks if the query answer is a ``ConceptRowStream``. java.util.stream.Stream stream() ---- +Creates a stream over ``ConceptRow``s based on this iterator. [caption=""] .Returns `java.util.stream.Stream` +[caption=""] +.Code examples +[source,java] +---- +answer.asConceptRows().stream(); +---- + // end::methods[] diff --git a/docs/modules/ROOT/partials/java/answer/ConceptTree.adoc b/docs/modules/ROOT/partials/java/answer/ConceptTree.adoc deleted file mode 100644 index 5d8598896c..0000000000 --- a/docs/modules/ROOT/partials/java/answer/ConceptTree.adoc +++ /dev/null @@ -1,7 +0,0 @@ -[#_ConceptTree] -=== ConceptTree - -*Package*: `com.typedb.driver.api.answer` - -Contains a tree of concepts. - diff --git a/docs/modules/ROOT/partials/java/answer/ConceptTreeIterator.adoc b/docs/modules/ROOT/partials/java/answer/ConceptTreeIterator.adoc deleted file mode 100644 index b51335e59b..0000000000 --- a/docs/modules/ROOT/partials/java/answer/ConceptTreeIterator.adoc +++ /dev/null @@ -1,59 +0,0 @@ -[#_ConceptTreeIterator] -=== ConceptTreeIterator - -*Package*: `com.typedb.driver.api.answer` - -*Superinterfaces:* - -* `java.util.Iterator` -* `QueryAnswer` - -// tag::methods[] -[#_ConceptTreeIterator_asConceptTreesStream_] -==== asConceptTreesStream - -[source,java] ----- -@CheckReturnValue -default ConceptTreeIterator asConceptTreesStream() ----- - -Casts the query answer to ``ConceptTreesStreamQueryAnswer``. - - -[caption=""] -.Returns -`ConceptTreeIterator` - -[#_ConceptTreeIterator_isConceptTreesStream_] -==== isConceptTreesStream - -[source,java] ----- -default boolean isConceptTreesStream() ----- - -Checks if the query answer is a ``ConceptTreesStream``. - - -[caption=""] -.Returns -`boolean` - -[#_ConceptTreeIterator_stream_] -==== stream - -[source,java] ----- -@CheckReturnValue -java.util.stream.Stream stream() ----- - - - -[caption=""] -.Returns -`java.util.stream.Stream` - -// end::methods[] - diff --git a/docs/modules/ROOT/partials/java/answer/OkQueryAnswer.adoc b/docs/modules/ROOT/partials/java/answer/OkQueryAnswer.adoc index f110bcd653..714b0714ef 100644 --- a/docs/modules/ROOT/partials/java/answer/OkQueryAnswer.adoc +++ b/docs/modules/ROOT/partials/java/answer/OkQueryAnswer.adoc @@ -7,49 +7,51 @@ * `QueryAnswer` +Represents a simple Ok message as a server answer. Doesn't contain concepts. + // tag::methods[] -[#_OkQueryAnswer_asConceptRows_] -==== asConceptRows +[#_OkQueryAnswer_asConceptDocuments_] +==== asConceptDocuments [source,java] ---- -default ConceptRowIterator asConceptRows() +default ConceptDocumentIterator asConceptDocuments() ---- -Casts the query answer to ``ConceptRowStreamQueryAnswer``. +Casts the query answer to ``ConceptDocumentIterator``. [caption=""] .Returns -`ConceptRowIterator` +`ConceptDocumentIterator` [caption=""] .Code examples [source,java] ---- -concept.asConceptRowStream(); +concept.asConceptDocuments(); ---- -[#_OkQueryAnswer_asConceptTreesStream_] -==== asConceptTreesStream +[#_OkQueryAnswer_asConceptRows_] +==== asConceptRows [source,java] ---- -default ConceptTreeIterator asConceptTreesStream() +default ConceptRowIterator asConceptRows() ---- -Casts the query answer to ``ConceptTreesStreamQueryAnswer``. +Casts the query answer to ``ConceptRowIterator``. [caption=""] .Returns -`ConceptTreeIterator` +`ConceptRowIterator` [caption=""] .Code examples [source,java] ---- -concept.asConceptTreesStream(); +concept.asConceptRows(); ---- [#_OkQueryAnswer_asOk_] @@ -68,16 +70,46 @@ Casts the query answer to ``OkQueryAnswer``. .Returns `OkQueryAnswer` -[#_OkQueryAnswer_isConceptRows_] -==== isConceptRows +[caption=""] +.Code examples +[source,java] +---- +concept.asOk(); +---- + +[#_OkQueryAnswer_getQueryType_] +==== getQueryType [source,java] ---- @CheckReturnValue -default boolean isConceptRows() +QueryType getQueryType() +---- + +Retrieves the executed query's type of this ``QueryAnswer``. + + +[caption=""] +.Returns +`QueryType` + +[caption=""] +.Code examples +[source,java] +---- +queryAnswer.getQueryType(); +---- + +[#_OkQueryAnswer_isConceptDocuments_] +==== isConceptDocuments + +[source,java] +---- +@CheckReturnValue +default boolean isConceptDocuments() ---- -Checks if the query answer is a ``ConceptRowStream``. +Checks if the query answer is a ``ConceptDocumentIterator``. [caption=""] @@ -88,19 +120,19 @@ Checks if the query answer is a ``ConceptRowStream``. .Code examples [source,java] ---- -concept.isConceptRowStream(); +concept.isConceptDocuments(); ---- -[#_OkQueryAnswer_isConceptTreesStream_] -==== isConceptTreesStream +[#_OkQueryAnswer_isConceptRows_] +==== isConceptRows [source,java] ---- @CheckReturnValue -default boolean isConceptTreesStream() +default boolean isConceptRows() ---- -Checks if the query answer is a ``ConceptTreesStream``. +Checks if the query answer is a ``ConceptRowIterator``. [caption=""] @@ -111,7 +143,7 @@ Checks if the query answer is a ``ConceptTreesStream``. .Code examples [source,java] ---- -concept.isConceptTreesStream(); +concept.isConceptRows(); ---- [#_OkQueryAnswer_isOk_] @@ -129,5 +161,12 @@ Checks if the query answer is an ``Ok``. .Returns `boolean` +[caption=""] +.Code examples +[source,java] +---- +concept.isOk(); +---- + // end::methods[] diff --git a/docs/modules/ROOT/partials/java/answer/QueryAnswer.adoc b/docs/modules/ROOT/partials/java/answer/QueryAnswer.adoc index 2c3697443d..352ca55fed 100644 --- a/docs/modules/ROOT/partials/java/answer/QueryAnswer.adoc +++ b/docs/modules/ROOT/partials/java/answer/QueryAnswer.adoc @@ -3,51 +3,51 @@ *Package*: `com.typedb.driver.api.answer` -TODO: docs +General answer on a query returned by a server. Can be a simple Ok response or a collection of concepts. // tag::methods[] -[#_QueryAnswer_asConceptRows_] -==== asConceptRows +[#_QueryAnswer_asConceptDocuments_] +==== asConceptDocuments [source,java] ---- -default ConceptRowIterator asConceptRows() +default ConceptDocumentIterator asConceptDocuments() ---- -Casts the query answer to ``ConceptRowStreamQueryAnswer``. +Casts the query answer to ``ConceptDocumentIterator``. [caption=""] .Returns -`ConceptRowIterator` +`ConceptDocumentIterator` [caption=""] .Code examples [source,java] ---- -concept.asConceptRowStream(); +concept.asConceptDocuments(); ---- -[#_QueryAnswer_asConceptTreesStream_] -==== asConceptTreesStream +[#_QueryAnswer_asConceptRows_] +==== asConceptRows [source,java] ---- -default ConceptTreeIterator asConceptTreesStream() +default ConceptRowIterator asConceptRows() ---- -Casts the query answer to ``ConceptTreesStreamQueryAnswer``. +Casts the query answer to ``ConceptRowIterator``. [caption=""] .Returns -`ConceptTreeIterator` +`ConceptRowIterator` [caption=""] .Code examples [source,java] ---- -concept.asConceptTreesStream(); +concept.asConceptRows(); ---- [#_QueryAnswer_asOk_] @@ -72,16 +72,39 @@ Casts the query answer to ``OkQueryAnswer``. concept.asOk(); ---- -[#_QueryAnswer_isConceptRows_] -==== isConceptRows +[#_QueryAnswer_getQueryType_] +==== getQueryType [source,java] ---- @CheckReturnValue -default boolean isConceptRows() +QueryType getQueryType() ---- -Checks if the query answer is a ``ConceptRowStream``. +Retrieves the executed query's type of this ``QueryAnswer``. + + +[caption=""] +.Returns +`QueryType` + +[caption=""] +.Code examples +[source,java] +---- +queryAnswer.getQueryType(); +---- + +[#_QueryAnswer_isConceptDocuments_] +==== isConceptDocuments + +[source,java] +---- +@CheckReturnValue +default boolean isConceptDocuments() +---- + +Checks if the query answer is a ``ConceptDocumentIterator``. [caption=""] @@ -92,19 +115,19 @@ Checks if the query answer is a ``ConceptRowStream``. .Code examples [source,java] ---- -concept.isConceptRowStream(); +concept.isConceptDocuments(); ---- -[#_QueryAnswer_isConceptTreesStream_] -==== isConceptTreesStream +[#_QueryAnswer_isConceptRows_] +==== isConceptRows [source,java] ---- @CheckReturnValue -default boolean isConceptTreesStream() +default boolean isConceptRows() ---- -Checks if the query answer is a ``ConceptTreesStream``. +Checks if the query answer is a ``ConceptRowIterator``. [caption=""] @@ -115,7 +138,7 @@ Checks if the query answer is a ``ConceptTreesStream``. .Code examples [source,java] ---- -concept.isConceptTreesStream(); +concept.isConceptRows(); ---- [#_QueryAnswer_isOk_] diff --git a/docs/modules/ROOT/partials/java/concept/Concept.adoc b/docs/modules/ROOT/partials/java/concept/Concept.adoc index b78e6cd0cb..7c98001783 100644 --- a/docs/modules/ROOT/partials/java/concept/Concept.adoc +++ b/docs/modules/ROOT/partials/java/concept/Concept.adoc @@ -92,6 +92,28 @@ Casts the concept to ``EntityType``. concept.asEntityType(); ---- +[#_Concept_asInstance_] +==== asInstance + +[source,java] +---- +default Instance asInstance() +---- + +Casts the concept to ``Instance``. + + +[caption=""] +.Returns +`Instance` + +[caption=""] +.Code examples +[source,java] +---- +concept.asInstance(); +---- + [#_Concept_asRelation_] ==== asRelation @@ -158,92 +180,71 @@ Casts the concept to ``RoleType``. concept.asRoleType(); ---- -[#_Concept_asThing_] -==== asThing - -[source,java] ----- -default Thing asThing() ----- - -Casts the concept to ``Thing``. - - -[caption=""] -.Returns -`Thing` - -[caption=""] -.Code examples -[source,java] ----- -concept.asThing(); ----- - -[#_Concept_asThingType_] -==== asThingType +[#_Concept_asType_] +==== asType [source,java] ---- -default ThingType asThingType() +default Type asType() ---- -Casts the concept to ``ThingType``. +Casts the concept to ``Type``. [caption=""] .Returns -`ThingType` +`Type` [caption=""] .Code examples [source,java] ---- -concept.asThingType(); +concept.asType(); ---- -[#_Concept_asType_] -==== asType +[#_Concept_asValue_] +==== asValue [source,java] ---- -default Type asType() +default Value asValue() ---- -Casts the concept to ``Type``. +Casts the concept to ``Value``. [caption=""] .Returns -`Type` +`Value` [caption=""] .Code examples [source,java] ---- -concept.asType(); +concept.asValue(); ---- -[#_Concept_asValue_] -==== asValue +[#_Concept_getLabel_] +==== getLabel [source,java] ---- -default Value asValue() +@CheckReturnValue +java.lang.String getLabel() ---- -Casts the concept to ``Value``. +Retrieves the unique label of the concept. If this is an ``Instance``, return the label of the type of this instance. If this is a ``Value``, return the label of the value type of the value. If this is a ``Type``, return the label of the type. [caption=""] .Returns -`Value` +`java.lang.String` [caption=""] .Code examples [source,java] ---- -concept.asValue(); +concept.getLabel(); ---- [#_Concept_isAttribute_] @@ -338,39 +339,16 @@ Checks if the concept is an ``EntityType``. concept.isEntityType(); ---- -[#_Concept_isRelation_] -==== isRelation - -[source,java] ----- -@CheckReturnValue -default boolean isRelation() ----- - -Checks if the concept is a ``Relation``. - - -[caption=""] -.Returns -`boolean` - -[caption=""] -.Code examples -[source,java] ----- -concept.isRelation(); ----- - -[#_Concept_isRelationType_] -==== isRelationType +[#_Concept_isInstance_] +==== isInstance [source,java] ---- @CheckReturnValue -default boolean isRelationType() +default boolean isInstance() ---- -Checks if the concept is a ``RelationType``. +Checks if the concept is an ``Instance``. [caption=""] @@ -381,19 +359,19 @@ Checks if the concept is a ``RelationType``. .Code examples [source,java] ---- -concept.isRelationType(); +concept.isInstance(); ---- -[#_Concept_isRoleType_] -==== isRoleType +[#_Concept_isRelation_] +==== isRelation [source,java] ---- @CheckReturnValue -default boolean isRoleType() +default boolean isRelation() ---- -Checks if the concept is a ``RoleType``. +Checks if the concept is a ``Relation``. [caption=""] @@ -404,19 +382,19 @@ Checks if the concept is a ``RoleType``. .Code examples [source,java] ---- -concept.isRoleType(); +concept.isRelation(); ---- -[#_Concept_isThing_] -==== isThing +[#_Concept_isRelationType_] +==== isRelationType [source,java] ---- @CheckReturnValue -default boolean isThing() +default boolean isRelationType() ---- -Checks if the concept is a ``Thing``. +Checks if the concept is a ``RelationType``. [caption=""] @@ -427,19 +405,19 @@ Checks if the concept is a ``Thing``. .Code examples [source,java] ---- -concept.isThing(); +concept.isRelationType(); ---- -[#_Concept_isThingType_] -==== isThingType +[#_Concept_isRoleType_] +==== isRoleType [source,java] ---- @CheckReturnValue -default boolean isThingType() +default boolean isRoleType() ---- -Checks if the concept is a ``ThingType``. +Checks if the concept is a ``RoleType``. [caption=""] @@ -450,7 +428,7 @@ Checks if the concept is a ``ThingType``. .Code examples [source,java] ---- -concept.isThingType(); +concept.isRoleType(); ---- [#_Concept_isType_] diff --git a/docs/modules/ROOT/partials/java/connection/Driver.adoc b/docs/modules/ROOT/partials/java/connection/Driver.adoc index 69d4cb4fe4..e5b2b97dd4 100644 --- a/docs/modules/ROOT/partials/java/connection/Driver.adoc +++ b/docs/modules/ROOT/partials/java/connection/Driver.adoc @@ -98,8 +98,8 @@ Opens a communication tunnel (transaction) to the given database on the running [options="header"] |=== |Name |Description |Type -a| `database` a| The name of the database with which the session connects a| `java.lang.String` -a| `type` a| The type of session to be created (DATA or SCHEMA) a| `Transaction.Type` +a| `database` a| The name of the database with which the transaction connects a| `java.lang.String` +a| `type` a| The type of transaction to be created (READ, WRITE, or SCHEMA) a| `Transaction.Type` |=== [caption=""] diff --git a/docs/modules/ROOT/partials/java/data/Attribute.adoc b/docs/modules/ROOT/partials/java/data/Attribute.adoc index 6962ac5d44..953f2a452c 100644 --- a/docs/modules/ROOT/partials/java/data/Attribute.adoc +++ b/docs/modules/ROOT/partials/java/data/Attribute.adoc @@ -1,12 +1,12 @@ [#_Attribute] === Attribute -*Package*: `com.typedb.driver.api.concept.thing` +*Package*: `com.typedb.driver.api.concept.instance` *Superinterfaces:* * `Concept` -* `Thing` +* `Instance` @@ -60,6 +60,160 @@ Casts the concept to ``AttributeType``. concept.asAttributeType(); ---- +[#_Attribute_asBoolean_] +==== asBoolean + +[source,java] +---- +boolean asBoolean() +---- + +Returns a ``boolean`` value of the value concept that this attribute holds. If the value has another type, raises an exception. + + +[caption=""] +.Returns +`boolean` + +[caption=""] +.Code examples +[source,java] +---- +attribute.asBoolean(); +---- + +[#_Attribute_asDate_] +==== asDate + +[source,java] +---- +java.time.LocalDate asDate() +---- + +Returns a ``date`` value of the value concept that this attribute holds. If the value has another type, raises an exception. + + +[caption=""] +.Returns +`java.time.LocalDate` + +[caption=""] +.Code examples +[source,java] +---- +attribute.asDate(); +---- + +[#_Attribute_asDatetime_] +==== asDatetime + +[source,java] +---- +java.time.LocalDateTime asDatetime() +---- + +Returns a ``datetime`` value of the value concept that this attribute holds. If the value has another type, raises an exception. + + +[caption=""] +.Returns +`java.time.LocalDateTime` + +[caption=""] +.Code examples +[source,java] +---- +attribute.asDatetime(); +---- + +[#_Attribute_asDatetimeTZ_] +==== asDatetimeTZ + +[source,java] +---- +java.time.ZonedDateTime asDatetimeTZ() +---- + +Returns a ``datetime-tz`` value of the value concept that this attribute holds. If the value has another type, raises an exception. + + +[caption=""] +.Returns +`java.time.ZonedDateTime` + +[caption=""] +.Code examples +[source,java] +---- +attribute.asDatetimeTZ(); +---- + +[#_Attribute_asDecimal_] +==== asDecimal + +[source,java] +---- +java.math.BigDecimal asDecimal() +---- + +Returns a ``decimal`` value of the value concept that this attribute holds. If the value has another type, raises an exception. + + +[caption=""] +.Returns +`java.math.BigDecimal` + +[caption=""] +.Code examples +[source,java] +---- +attribute.asDecimal(); +---- + +[#_Attribute_asDouble_] +==== asDouble + +[source,java] +---- +double asDouble() +---- + +Returns a ``double`` value of the value concept that this attribute holds. If the value has another type, raises an exception. + + +[caption=""] +.Returns +`double` + +[caption=""] +.Code examples +[source,java] +---- +attribute.asDouble(); +---- + +[#_Attribute_asDuration_] +==== asDuration + +[source,java] +---- +Duration asDuration() +---- + +Returns a ``duration`` value of the value concept that this attribute holds. If the value has another type, raises an exception. + + +[caption=""] +.Returns +`Duration` + +[caption=""] +.Code examples +[source,java] +---- +attribute.asDuration(); +---- + [#_Attribute_asEntity_] ==== asEntity @@ -104,6 +258,28 @@ Casts the concept to ``EntityType``. concept.asEntityType(); ---- +[#_Attribute_asLong_] +==== asLong + +[source,java] +---- +long asLong() +---- + +Returns a ``long`` value of the value concept that this attribute holds. If the value has another type, raises an exception. + + +[caption=""] +.Returns +`long` + +[caption=""] +.Code examples +[source,java] +---- +attribute.asLong(); +---- + [#_Attribute_asRelation_] ==== asRelation @@ -170,26 +346,48 @@ Casts the concept to ``RoleType``. concept.asRoleType(); ---- -[#_Attribute_asThingType_] -==== asThingType +[#_Attribute_asString_] +==== asString [source,java] ---- -default ThingType asThingType() +java.lang.String asString() ---- -Casts the concept to ``ThingType``. +Returns a ``string`` value of the value concept that this attribute holds. If the value has another type, raises an exception. [caption=""] .Returns -`ThingType` +`java.lang.String` [caption=""] .Code examples [source,java] ---- -concept.asThingType(); +attribute.asString(); +---- + +[#_Attribute_asStruct_] +==== asStruct + +[source,java] +---- +java.util.Map> asStruct() +---- + +Returns a ``struct`` value of the value concept that this attribute holds represented as a map from field names to values. If the value has another type, raises an exception. + + +[caption=""] +.Returns +`java.util.Map>` + +[caption=""] +.Code examples +[source,java] +---- +attribute.asStruct(); ---- [#_Attribute_asType_] @@ -214,6 +412,28 @@ Casts the concept to ``Type``. concept.asType(); ---- +[#_Attribute_asUntyped_] +==== asUntyped + +[source,java] +---- +java.lang.Object asUntyped() +---- + +Returns an untyped ``Object`` value of the value concept that this attribute holds. This is useful for value equality or printing without having to switch on the actual contained value. + + +[caption=""] +.Returns +`java.lang.Object` + +[caption=""] +.Code examples +[source,java] +---- +attribute.asUntyped(); +---- + [#_Attribute_asValue_] ==== asValue @@ -236,6 +456,29 @@ Casts the concept to ``Value``. concept.asValue(); ---- +[#_Attribute_getLabel_] +==== getLabel + +[source,java] +---- +@CheckReturnValue +java.lang.String getLabel() +---- + +Retrieves the unique label of the concept. If this is an ``Instance``, return the label of the type of this instance. If this is a ``Value``, return the label of the value type of the value. If this is a ``Type``, return the label of the type. + + +[caption=""] +.Returns +`java.lang.String` + +[caption=""] +.Code examples +[source,java] +---- +concept.getLabel(); +---- + [#_Attribute_getType_] ==== getType @@ -327,6 +570,160 @@ Checks if the concept is an ``AttributeType``. concept.isAttributeType(); ---- +[#_Attribute_isBoolean_] +==== isBoolean + +[source,java] +---- +boolean isBoolean() +---- + +Returns ``True`` if this attribute holds a value of type ``boolean``. Otherwise, returns ``false``. + + +[caption=""] +.Returns +`boolean` + +[caption=""] +.Code examples +[source,java] +---- +attribute.isBoolean() +---- + +[#_Attribute_isDate_] +==== isDate + +[source,java] +---- +boolean isDate() +---- + +Returns ``True`` if this attribute holds a value of type ``date``. Otherwise, returns ``false``. + + +[caption=""] +.Returns +`boolean` + +[caption=""] +.Code examples +[source,java] +---- +attribute.isDate(); +---- + +[#_Attribute_isDatetime_] +==== isDatetime + +[source,java] +---- +boolean isDatetime() +---- + +Returns ``True`` if this attribute holds a value of type ``datetime``. Otherwise, returns ``false``. + + +[caption=""] +.Returns +`boolean` + +[caption=""] +.Code examples +[source,java] +---- +attribute.isDatetime(); +---- + +[#_Attribute_isDatetimeTZ_] +==== isDatetimeTZ + +[source,java] +---- +boolean isDatetimeTZ() +---- + +Returns ``True`` if this attribute holds a value of type ``datetime-tz``. Otherwise, returns ``false``. + + +[caption=""] +.Returns +`boolean` + +[caption=""] +.Code examples +[source,java] +---- +attribute.isDatetimeTZ(); +---- + +[#_Attribute_isDecimal_] +==== isDecimal + +[source,java] +---- +boolean isDecimal() +---- + +Returns ``True`` if this attribute holds a value of type ``decimal``. Otherwise, returns ``false``. + + +[caption=""] +.Returns +`boolean` + +[caption=""] +.Code examples +[source,java] +---- +attribute.isDecimal(); +---- + +[#_Attribute_isDouble_] +==== isDouble + +[source,java] +---- +boolean isDouble() +---- + +Returns ``True`` if this attribute holds a value of type ``double``. Otherwise, returns ``false``. + + +[caption=""] +.Returns +`boolean` + +[caption=""] +.Code examples +[source,java] +---- +attribute.isDouble(); +---- + +[#_Attribute_isDuration_] +==== isDuration + +[source,java] +---- +boolean isDuration() +---- + +Returns ``True`` if this attribute holds a value of type ``duration``. Otherwise, returns ``false``. + + +[caption=""] +.Returns +`boolean` + +[caption=""] +.Code examples +[source,java] +---- +attribute.isDuration(); +---- + [#_Attribute_isEntity_] ==== isEntity @@ -373,6 +770,28 @@ Checks if the concept is an ``EntityType``. concept.isEntityType(); ---- +[#_Attribute_isLong_] +==== isLong + +[source,java] +---- +boolean isLong() +---- + +Returns ``True`` if this attribute holds a value of type ``long``. Otherwise, returns ``false``. + + +[caption=""] +.Returns +`boolean` + +[caption=""] +.Code examples +[source,java] +---- +attribute.isLong(); +---- + [#_Attribute_isRelation_] ==== isRelation @@ -442,16 +861,37 @@ Checks if the concept is a ``RoleType``. concept.isRoleType(); ---- -[#_Attribute_isThingType_] -==== isThingType +[#_Attribute_isString_] +==== isString [source,java] ---- -@CheckReturnValue -default boolean isThingType() +boolean isString() +---- + +Returns ``True`` if this attribute holds a value of type ``string``. Otherwise, returns ``false``. + + +[caption=""] +.Returns +`boolean` + +[caption=""] +.Code examples +[source,java] +---- +attribute.isString(); +---- + +[#_Attribute_isStruct_] +==== isStruct + +[source,java] +---- +boolean isStruct() ---- -Checks if the concept is a ``ThingType``. +Returns ``True`` if this attribute holds a value of type ``struct``. Otherwise, returns ``false``. [caption=""] @@ -462,7 +902,7 @@ Checks if the concept is a ``ThingType``. .Code examples [source,java] ---- -concept.isThingType(); +attribute.isStruct(); ---- [#_Attribute_isType_] diff --git a/docs/modules/ROOT/partials/java/data/Entity.adoc b/docs/modules/ROOT/partials/java/data/Entity.adoc index eafdc4972e..f40295969d 100644 --- a/docs/modules/ROOT/partials/java/data/Entity.adoc +++ b/docs/modules/ROOT/partials/java/data/Entity.adoc @@ -1,12 +1,12 @@ [#_Entity] === Entity -*Package*: `com.typedb.driver.api.concept.thing` +*Package*: `com.typedb.driver.api.concept.instance` *Superinterfaces:* * `Concept` -* `Thing` +* `Instance` Instance of data of an entity type, representing a standalone object that exists in the data model independently. Entity does not have a value. It is usually addressed by its ownership over attribute instances and/or roles played in relation instances. @@ -166,28 +166,6 @@ Casts the concept to ``RoleType``. concept.asRoleType(); ---- -[#_Entity_asThingType_] -==== asThingType - -[source,java] ----- -default ThingType asThingType() ----- - -Casts the concept to ``ThingType``. - - -[caption=""] -.Returns -`ThingType` - -[caption=""] -.Code examples -[source,java] ----- -concept.asThingType(); ----- - [#_Entity_asType_] ==== asType @@ -255,6 +233,29 @@ Retrieves the unique id of the ``Entity``. entity.getIID(); ---- +[#_Entity_getLabel_] +==== getLabel + +[source,java] +---- +@CheckReturnValue +java.lang.String getLabel() +---- + +Retrieves the unique label of the concept. If this is an ``Instance``, return the label of the type of this instance. If this is a ``Value``, return the label of the value type of the value. If this is a ``Type``, return the label of the type. + + +[caption=""] +.Returns +`java.lang.String` + +[caption=""] +.Code examples +[source,java] +---- +concept.getLabel(); +---- + [#_Entity_getType_] ==== getType @@ -439,29 +440,6 @@ Checks if the concept is a ``RoleType``. concept.isRoleType(); ---- -[#_Entity_isThingType_] -==== isThingType - -[source,java] ----- -@CheckReturnValue -default boolean isThingType() ----- - -Checks if the concept is a ``ThingType``. - - -[caption=""] -.Returns -`boolean` - -[caption=""] -.Code examples -[source,java] ----- -concept.isThingType(); ----- - [#_Entity_isType_] ==== isType diff --git a/docs/modules/ROOT/partials/java/data/Thing.adoc b/docs/modules/ROOT/partials/java/data/Instance.adoc similarity index 78% rename from docs/modules/ROOT/partials/java/data/Thing.adoc rename to docs/modules/ROOT/partials/java/data/Instance.adoc index 2b14ab3b75..d929b90a30 100644 --- a/docs/modules/ROOT/partials/java/data/Thing.adoc +++ b/docs/modules/ROOT/partials/java/data/Instance.adoc @@ -1,14 +1,14 @@ -[#_Thing] -=== Thing +[#_Instance] +=== Instance -*Package*: `com.typedb.driver.api.concept.thing` +*Package*: `com.typedb.driver.api.concept.instance` *Superinterfaces:* * `Concept` // tag::methods[] -[#_Thing_asAttribute_] +[#_Instance_asAttribute_] ==== asAttribute [source,java] @@ -30,7 +30,7 @@ Casts the concept to ``Attribute``. concept.asAttribute(); ---- -[#_Thing_asAttributeType_] +[#_Instance_asAttributeType_] ==== asAttributeType [source,java] @@ -52,7 +52,7 @@ Casts the concept to ``AttributeType``. concept.asAttributeType(); ---- -[#_Thing_asEntity_] +[#_Instance_asEntity_] ==== asEntity [source,java] @@ -74,7 +74,7 @@ Casts the concept to ``Entity``. concept.asEntity(); ---- -[#_Thing_asEntityType_] +[#_Instance_asEntityType_] ==== asEntityType [source,java] @@ -96,185 +96,186 @@ Casts the concept to ``EntityType``. concept.asEntityType(); ---- -[#_Thing_asRelation_] -==== asRelation +[#_Instance_asInstance_] +==== asInstance [source,java] ---- -default Relation asRelation() +@CheckReturnValue +default Instance asInstance() ---- -Casts the concept to ``Relation``. +Casts the concept to ``Instance``. [caption=""] .Returns -`Relation` +`Instance` [caption=""] .Code examples [source,java] ---- -concept.asRelation(); +instance.asInstance(); ---- -[#_Thing_asRelationType_] -==== asRelationType +[#_Instance_asRelation_] +==== asRelation [source,java] ---- -default RelationType asRelationType() +default Relation asRelation() ---- -Casts the concept to ``RelationType``. +Casts the concept to ``Relation``. [caption=""] .Returns -`RelationType` +`Relation` [caption=""] .Code examples [source,java] ---- -concept.asRelationType(); +concept.asRelation(); ---- -[#_Thing_asRoleType_] -==== asRoleType +[#_Instance_asRelationType_] +==== asRelationType [source,java] ---- -default RoleType asRoleType() +default RelationType asRelationType() ---- -Casts the concept to ``RoleType``. +Casts the concept to ``RelationType``. [caption=""] .Returns -`RoleType` +`RelationType` [caption=""] .Code examples [source,java] ---- -concept.asRoleType(); +concept.asRelationType(); ---- -[#_Thing_asThing_] -==== asThing +[#_Instance_asRoleType_] +==== asRoleType [source,java] ---- -@CheckReturnValue -default Thing asThing() +default RoleType asRoleType() ---- -Casts the concept to ``Thing``. +Casts the concept to ``RoleType``. [caption=""] .Returns -`Thing` +`RoleType` [caption=""] .Code examples [source,java] ---- -thing.asThing(); +concept.asRoleType(); ---- -[#_Thing_asThingType_] -==== asThingType +[#_Instance_asType_] +==== asType [source,java] ---- -default ThingType asThingType() +default Type asType() ---- -Casts the concept to ``ThingType``. +Casts the concept to ``Type``. [caption=""] .Returns -`ThingType` +`Type` [caption=""] .Code examples [source,java] ---- -concept.asThingType(); +concept.asType(); ---- -[#_Thing_asType_] -==== asType +[#_Instance_asValue_] +==== asValue [source,java] ---- -default Type asType() +default Value asValue() ---- -Casts the concept to ``Type``. +Casts the concept to ``Value``. [caption=""] .Returns -`Type` +`Value` [caption=""] .Code examples [source,java] ---- -concept.asType(); +concept.asValue(); ---- -[#_Thing_asValue_] -==== asValue +[#_Instance_getLabel_] +==== getLabel [source,java] ---- -default Value asValue() +@CheckReturnValue +java.lang.String getLabel() ---- -Casts the concept to ``Value``. +Retrieves the unique label of the concept. If this is an ``Instance``, return the label of the type of this instance. If this is a ``Value``, return the label of the value type of the value. If this is a ``Type``, return the label of the type. [caption=""] .Returns -`Value` +`java.lang.String` [caption=""] .Code examples [source,java] ---- -concept.asValue(); +concept.getLabel(); ---- -[#_Thing_getType_] +[#_Instance_getType_] ==== getType [source,java] ---- @CheckReturnValue -ThingType getType() +Type getType() ---- -Retrieves the type which this ``Thing`` belongs to. +Retrieves the type which this ``Instance`` belongs to. [caption=""] .Returns -`ThingType` +`Type` [caption=""] .Code examples [source,java] ---- -thing.getType(); +instance.getType(); ---- -[#_Thing_isAttribute_] +[#_Instance_isAttribute_] ==== isAttribute [source,java] @@ -297,7 +298,7 @@ Checks if the concept is an ``Attribute``. concept.isAttribute(); ---- -[#_Thing_isAttributeType_] +[#_Instance_isAttributeType_] ==== isAttributeType [source,java] @@ -320,7 +321,7 @@ Checks if the concept is an ``AttributeType``. concept.isAttributeType(); ---- -[#_Thing_isEntity_] +[#_Instance_isEntity_] ==== isEntity [source,java] @@ -343,7 +344,7 @@ Checks if the concept is an ``Entity``. concept.isEntity(); ---- -[#_Thing_isEntityType_] +[#_Instance_isEntityType_] ==== isEntityType [source,java] @@ -366,39 +367,16 @@ Checks if the concept is an ``EntityType``. concept.isEntityType(); ---- -[#_Thing_isRelation_] -==== isRelation - -[source,java] ----- -@CheckReturnValue -default boolean isRelation() ----- - -Checks if the concept is a ``Relation``. - - -[caption=""] -.Returns -`boolean` - -[caption=""] -.Code examples -[source,java] ----- -concept.isRelation(); ----- - -[#_Thing_isRelationType_] -==== isRelationType +[#_Instance_isInstance_] +==== isInstance [source,java] ---- @CheckReturnValue -default boolean isRelationType() +default boolean isInstance() ---- -Checks if the concept is a ``RelationType``. +Checks if the concept is a ``Instance``. [caption=""] @@ -409,19 +387,19 @@ Checks if the concept is a ``RelationType``. .Code examples [source,java] ---- -concept.isRelationType(); +instance.isInstance(); ---- -[#_Thing_isRoleType_] -==== isRoleType +[#_Instance_isRelation_] +==== isRelation [source,java] ---- @CheckReturnValue -default boolean isRoleType() +default boolean isRelation() ---- -Checks if the concept is a ``RoleType``. +Checks if the concept is a ``Relation``. [caption=""] @@ -432,19 +410,19 @@ Checks if the concept is a ``RoleType``. .Code examples [source,java] ---- -concept.isRoleType(); +concept.isRelation(); ---- -[#_Thing_isThing_] -==== isThing +[#_Instance_isRelationType_] +==== isRelationType [source,java] ---- @CheckReturnValue -default boolean isThing() +default boolean isRelationType() ---- -Checks if the concept is a ``Thing``. +Checks if the concept is a ``RelationType``. [caption=""] @@ -455,19 +433,19 @@ Checks if the concept is a ``Thing``. .Code examples [source,java] ---- -thing.isThing(); +concept.isRelationType(); ---- -[#_Thing_isThingType_] -==== isThingType +[#_Instance_isRoleType_] +==== isRoleType [source,java] ---- @CheckReturnValue -default boolean isThingType() +default boolean isRoleType() ---- -Checks if the concept is a ``ThingType``. +Checks if the concept is a ``RoleType``. [caption=""] @@ -478,10 +456,10 @@ Checks if the concept is a ``ThingType``. .Code examples [source,java] ---- -concept.isThingType(); +concept.isRoleType(); ---- -[#_Thing_isType_] +[#_Instance_isType_] ==== isType [source,java] @@ -504,7 +482,7 @@ Checks if the concept is a ``Type``. concept.isType(); ---- -[#_Thing_isValue_] +[#_Instance_isValue_] ==== isValue [source,java] diff --git a/docs/modules/ROOT/partials/java/data/Relation.adoc b/docs/modules/ROOT/partials/java/data/Relation.adoc index b83e0c91b0..4c352ee8c2 100644 --- a/docs/modules/ROOT/partials/java/data/Relation.adoc +++ b/docs/modules/ROOT/partials/java/data/Relation.adoc @@ -1,12 +1,12 @@ [#_Relation] === Relation -*Package*: `com.typedb.driver.api.concept.thing` +*Package*: `com.typedb.driver.api.concept.instance` *Superinterfaces:* * `Concept` -* `Thing` +* `Instance` Relation is an instance of a relation type and can be uniquely addressed by a combination of its type, owned attributes and role players. @@ -166,28 +166,6 @@ Casts the concept to ``RoleType``. concept.asRoleType(); ---- -[#_Relation_asThingType_] -==== asThingType - -[source,java] ----- -default ThingType asThingType() ----- - -Casts the concept to ``ThingType``. - - -[caption=""] -.Returns -`ThingType` - -[caption=""] -.Code examples -[source,java] ----- -concept.asThingType(); ----- - [#_Relation_asType_] ==== asType @@ -255,6 +233,29 @@ Retrieves the unique id of the ``Relation``. relation.getIID(); ---- +[#_Relation_getLabel_] +==== getLabel + +[source,java] +---- +@CheckReturnValue +java.lang.String getLabel() +---- + +Retrieves the unique label of the concept. If this is an ``Instance``, return the label of the type of this instance. If this is a ``Value``, return the label of the value type of the value. If this is a ``Type``, return the label of the type. + + +[caption=""] +.Returns +`java.lang.String` + +[caption=""] +.Code examples +[source,java] +---- +concept.getLabel(); +---- + [#_Relation_getType_] ==== getType @@ -439,29 +440,6 @@ Checks if the concept is a ``RoleType``. concept.isRoleType(); ---- -[#_Relation_isThingType_] -==== isThingType - -[source,java] ----- -@CheckReturnValue -default boolean isThingType() ----- - -Checks if the concept is a ``ThingType``. - - -[caption=""] -.Returns -`boolean` - -[caption=""] -.Code examples -[source,java] ----- -concept.isThingType(); ----- - [#_Relation_isType_] ==== isType diff --git a/docs/modules/ROOT/partials/java/data/Value.adoc b/docs/modules/ROOT/partials/java/data/Value.adoc index 19a8c61ee2..8f24cf1af3 100644 --- a/docs/modules/ROOT/partials/java/data/Value.adoc +++ b/docs/modules/ROOT/partials/java/data/Value.adoc @@ -201,7 +201,7 @@ value.asDouble(); [source,java] ---- -com.typedb.driver.common.Duration asDuration() +Duration asDuration() ---- Returns a ``duration`` value of this value concept. If the value has another type, raises an exception. @@ -209,7 +209,7 @@ Returns a ``duration`` value of this value concept. If the value has another typ [caption=""] .Returns -`com.typedb.driver.common.Duration` +`Duration` [caption=""] .Code examples @@ -262,6 +262,28 @@ Casts the concept to ``EntityType``. concept.asEntityType(); ---- +[#_Value_asInstance_] +==== asInstance + +[source,java] +---- +default Instance asInstance() +---- + +Casts the concept to ``Instance``. + + +[caption=""] +.Returns +`Instance` + +[caption=""] +.Code examples +[source,java] +---- +concept.asInstance(); +---- + [#_Value_asLong_] ==== asLong @@ -394,50 +416,6 @@ Returns a ``struct`` value of this value concept represented as a map from field value.asStruct(); ---- -[#_Value_asThing_] -==== asThing - -[source,java] ----- -default Thing asThing() ----- - -Casts the concept to ``Thing``. - - -[caption=""] -.Returns -`Thing` - -[caption=""] -.Code examples -[source,java] ----- -concept.asThing(); ----- - -[#_Value_asThingType_] -==== asThingType - -[source,java] ----- -default ThingType asThingType() ----- - -Casts the concept to ``ThingType``. - - -[caption=""] -.Returns -`ThingType` - -[caption=""] -.Code examples -[source,java] ----- -concept.asThingType(); ----- - [#_Value_asType_] ==== asType @@ -504,6 +482,29 @@ Casts the concept to ``Value``. concept.asValue(); ---- +[#_Value_getLabel_] +==== getLabel + +[source,java] +---- +@CheckReturnValue +java.lang.String getLabel() +---- + +Retrieves the unique label of the concept. If this is an ``Instance``, return the label of the type of this instance. If this is a ``Value``, return the label of the value type of the value. If this is a ``Type``, return the label of the type. + + +[caption=""] +.Returns +`java.lang.String` + +[caption=""] +.Code examples +[source,java] +---- +concept.getLabel(); +---- + [#_Value_getType_] ==== getType @@ -512,7 +513,7 @@ concept.asValue(); java.lang.String getType() ---- -Retrieves the string representation of the type of this value concept. +Retrieves the ``String`` describing the value type of this ``Value`` concept. [caption=""] @@ -772,6 +773,29 @@ Checks if the concept is an ``EntityType``. concept.isEntityType(); ---- +[#_Value_isInstance_] +==== isInstance + +[source,java] +---- +@CheckReturnValue +default boolean isInstance() +---- + +Checks if the concept is an ``Instance``. + + +[caption=""] +.Returns +`boolean` + +[caption=""] +.Code examples +[source,java] +---- +concept.isInstance(); +---- + [#_Value_isLong_] ==== isLong @@ -907,52 +931,6 @@ Returns ``True`` if the value which this value concept holds is of type ``struct value.isStruct(); ---- -[#_Value_isThing_] -==== isThing - -[source,java] ----- -@CheckReturnValue -default boolean isThing() ----- - -Checks if the concept is a ``Thing``. - - -[caption=""] -.Returns -`boolean` - -[caption=""] -.Code examples -[source,java] ----- -concept.isThing(); ----- - -[#_Value_isThingType_] -==== isThingType - -[source,java] ----- -@CheckReturnValue -default boolean isThingType() ----- - -Checks if the concept is a ``ThingType``. - - -[caption=""] -.Returns -`boolean` - -[caption=""] -.Code examples -[source,java] ----- -concept.isThingType(); ----- - [#_Value_isType_] ==== isType diff --git a/docs/modules/ROOT/partials/java/schema/AttributeType.adoc b/docs/modules/ROOT/partials/java/schema/AttributeType.adoc index 35121cd744..d455c3bfb9 100644 --- a/docs/modules/ROOT/partials/java/schema/AttributeType.adoc +++ b/docs/modules/ROOT/partials/java/schema/AttributeType.adoc @@ -6,7 +6,6 @@ *Superinterfaces:* * `Concept` -* `ThingType` * `Type` Attribute types represent properties that other types can own. @@ -107,6 +106,28 @@ Casts the concept to ``EntityType``. concept.asEntityType(); ---- +[#_AttributeType_asInstance_] +==== asInstance + +[source,java] +---- +default Instance asInstance() +---- + +Casts the concept to ``Instance``. + + +[caption=""] +.Returns +`Instance` + +[caption=""] +.Code examples +[source,java] +---- +concept.asInstance(); +---- + [#_AttributeType_asRelation_] ==== asRelation @@ -173,48 +194,49 @@ Casts the concept to ``RoleType``. concept.asRoleType(); ---- -[#_AttributeType_asThing_] -==== asThing +[#_AttributeType_asValue_] +==== asValue [source,java] ---- -default Thing asThing() +default Value asValue() ---- -Casts the concept to ``Thing``. +Casts the concept to ``Value``. [caption=""] .Returns -`Thing` +`Value` [caption=""] .Code examples [source,java] ---- -concept.asThing(); +concept.asValue(); ---- -[#_AttributeType_asValue_] -==== asValue +[#_AttributeType_getLabel_] +==== getLabel [source,java] ---- -default Value asValue() +@CheckReturnValue +java.lang.String getLabel() ---- -Casts the concept to ``Value``. +Retrieves the unique label of the concept. If this is an ``Instance``, return the label of the type of this instance. If this is a ``Value``, return the label of the value type of the value. If this is a ``Type``, return the label of the type. [caption=""] .Returns -`Value` +`java.lang.String` [caption=""] .Code examples [source,java] ---- -concept.asValue(); +concept.getLabel(); ---- [#_AttributeType_getValueType_] @@ -486,6 +508,29 @@ Checks if the concept is an ``EntityType``. concept.isEntityType(); ---- +[#_AttributeType_isInstance_] +==== isInstance + +[source,java] +---- +@CheckReturnValue +default boolean isInstance() +---- + +Checks if the concept is an ``Instance``. + + +[caption=""] +.Returns +`boolean` + +[caption=""] +.Code examples +[source,java] +---- +concept.isInstance(); +---- + [#_AttributeType_isLong_] ==== isLong @@ -621,29 +666,6 @@ Returns ``True`` if this attribute type is of type ``struct``. Otherwise, return attributeType.isStruct(); ---- -[#_AttributeType_isThing_] -==== isThing - -[source,java] ----- -@CheckReturnValue -default boolean isThing() ----- - -Checks if the concept is a ``Thing``. - - -[caption=""] -.Returns -`boolean` - -[caption=""] -.Code examples -[source,java] ----- -concept.isThing(); ----- - [#_AttributeType_isUntyped_] ==== isUntyped diff --git a/docs/modules/ROOT/partials/java/schema/EntityType.adoc b/docs/modules/ROOT/partials/java/schema/EntityType.adoc index d23dfc777b..bfd422f8cb 100644 --- a/docs/modules/ROOT/partials/java/schema/EntityType.adoc +++ b/docs/modules/ROOT/partials/java/schema/EntityType.adoc @@ -6,7 +6,6 @@ *Superinterfaces:* * `Concept` -* `ThingType` * `Type` Entity types represent the classification of independent objects in the data model of the business domain. @@ -101,6 +100,28 @@ Casts the concept to ``EntityType``. concept.asEntityType(); ---- +[#_EntityType_asInstance_] +==== asInstance + +[source,java] +---- +default Instance asInstance() +---- + +Casts the concept to ``Instance``. + + +[caption=""] +.Returns +`Instance` + +[caption=""] +.Code examples +[source,java] +---- +concept.asInstance(); +---- + [#_EntityType_asRelation_] ==== asRelation @@ -167,48 +188,49 @@ Casts the concept to ``RoleType``. concept.asRoleType(); ---- -[#_EntityType_asThing_] -==== asThing +[#_EntityType_asValue_] +==== asValue [source,java] ---- -default Thing asThing() +default Value asValue() ---- -Casts the concept to ``Thing``. +Casts the concept to ``Value``. [caption=""] .Returns -`Thing` +`Value` [caption=""] .Code examples [source,java] ---- -concept.asThing(); +concept.asValue(); ---- -[#_EntityType_asValue_] -==== asValue +[#_EntityType_getLabel_] +==== getLabel [source,java] ---- -default Value asValue() +@CheckReturnValue +java.lang.String getLabel() ---- -Casts the concept to ``Value``. +Retrieves the unique label of the concept. If this is an ``Instance``, return the label of the type of this instance. If this is a ``Value``, return the label of the value type of the value. If this is a ``Type``, return the label of the type. [caption=""] .Returns -`Value` +`java.lang.String` [caption=""] .Code examples [source,java] ---- -concept.asValue(); +concept.getLabel(); ---- [#_EntityType_isAttribute_] @@ -303,16 +325,16 @@ Checks if the concept is an ``EntityType``. concept.isEntityType(); ---- -[#_EntityType_isRelation_] -==== isRelation +[#_EntityType_isInstance_] +==== isInstance [source,java] ---- @CheckReturnValue -default boolean isRelation() +default boolean isInstance() ---- -Checks if the concept is a ``Relation``. +Checks if the concept is an ``Instance``. [caption=""] @@ -323,19 +345,19 @@ Checks if the concept is a ``Relation``. .Code examples [source,java] ---- -concept.isRelation(); +concept.isInstance(); ---- -[#_EntityType_isRelationType_] -==== isRelationType +[#_EntityType_isRelation_] +==== isRelation [source,java] ---- @CheckReturnValue -default boolean isRelationType() +default boolean isRelation() ---- -Checks if the concept is a ``RelationType``. +Checks if the concept is a ``Relation``. [caption=""] @@ -346,19 +368,19 @@ Checks if the concept is a ``RelationType``. .Code examples [source,java] ---- -concept.isRelationType(); +concept.isRelation(); ---- -[#_EntityType_isRoleType_] -==== isRoleType +[#_EntityType_isRelationType_] +==== isRelationType [source,java] ---- @CheckReturnValue -default boolean isRoleType() +default boolean isRelationType() ---- -Checks if the concept is a ``RoleType``. +Checks if the concept is a ``RelationType``. [caption=""] @@ -369,19 +391,19 @@ Checks if the concept is a ``RoleType``. .Code examples [source,java] ---- -concept.isRoleType(); +concept.isRelationType(); ---- -[#_EntityType_isThing_] -==== isThing +[#_EntityType_isRoleType_] +==== isRoleType [source,java] ---- @CheckReturnValue -default boolean isThing() +default boolean isRoleType() ---- -Checks if the concept is a ``Thing``. +Checks if the concept is a ``RoleType``. [caption=""] @@ -392,7 +414,7 @@ Checks if the concept is a ``Thing``. .Code examples [source,java] ---- -concept.isThing(); +concept.isRoleType(); ---- [#_EntityType_isValue_] diff --git a/docs/modules/ROOT/partials/java/schema/Label.adoc b/docs/modules/ROOT/partials/java/schema/Label.adoc deleted file mode 100644 index bf830942d9..0000000000 --- a/docs/modules/ROOT/partials/java/schema/Label.adoc +++ /dev/null @@ -1,195 +0,0 @@ -[#_Label] -=== Label - -*Package*: `com.typedb.driver.common` - -A ``Label`` holds the uniquely identifying name of a type. - -It consists of an optional ``scope``, and a ``name``, represented ``scope:name``. The scope is used only used to distinguish between role-types of the same name declared in different relation types. - -// tag::methods[] -[#_Label_equals_java_lang_Object] -==== equals - -[source,java] ----- -public boolean equals​(java.lang.Object obj) ----- - -Checks if this Label is equal to another object. - - -[caption=""] -.Input parameters -[cols=",,"] -[options="header"] -|=== -|Name |Description |Type -a| `obj` a| Object to compare with a| `java.lang.Object` -|=== - -[caption=""] -.Returns -`public boolean` - -[caption=""] -.Code examples -[source,java] ----- -label.equals(obj); ----- - -[#_Label_name_] -==== name - -[source,java] ----- -public java.lang.String name() ----- - -Returns the name of this Label. - - -[caption=""] -.Returns -`public java.lang.String` - -[caption=""] -.Code examples -[source,java] ----- -label.name(); ----- - -[#_Label_of_java_lang_String] -==== of - -[source,java] ----- -public static Label of​(java.lang.String name) ----- - -Creates a Label from a specified name. - - -[caption=""] -.Input parameters -[cols=",,"] -[options="header"] -|=== -|Name |Description |Type -a| `name` a| Label name a| `java.lang.String` -|=== - -[caption=""] -.Returns -`public static Label` - -[caption=""] -.Code examples -[source,java] ----- -Label.of("entity"); ----- - -[#_Label_of_java_lang_String_java_lang_String] -==== of - -[source,java] ----- -public static Label of​(java.lang.String scope, - java.lang.String name) ----- - -Creates a Label from a specified scope and name. - - -[caption=""] -.Input parameters -[cols=",,"] -[options="header"] -|=== -|Name |Description |Type -a| `scope` a| Label scope a| `java.lang.String` -a| `name` a| Label name a| `java.lang.String` -|=== - -[caption=""] -.Returns -`public static Label` - -[caption=""] -.Code examples -[source,java] ----- -Label.of("relation", "role"); ----- - -[#_Label_scope_] -==== scope - -[source,java] ----- -public java.util.Optional scope() ----- - -Returns the scope of this Label. - - -[caption=""] -.Returns -`public java.util.Optional` - -[caption=""] -.Code examples -[source,java] ----- -label.scope(); ----- - -[#_Label_scopedName_] -==== scopedName - -[source,java] ----- -public java.lang.String scopedName() ----- - -Returns the string representation of the scoped name. - - -[caption=""] -.Returns -`public java.lang.String` - -[caption=""] -.Code examples -[source,java] ----- -label.scopedName(); ----- - -[#_Label_toString_] -==== toString - -[source,java] ----- -public java.lang.String toString() ----- - -Returns the string representation of the scoped name. - - -[caption=""] -.Returns -`public java.lang.String` - -[caption=""] -.Code examples -[source,java] ----- -label.toString(); ----- - -// end::methods[] - diff --git a/docs/modules/ROOT/partials/java/schema/RelationType.adoc b/docs/modules/ROOT/partials/java/schema/RelationType.adoc index 3425ced572..db8f81b8c6 100644 --- a/docs/modules/ROOT/partials/java/schema/RelationType.adoc +++ b/docs/modules/ROOT/partials/java/schema/RelationType.adoc @@ -6,7 +6,6 @@ *Superinterfaces:* * `Concept` -* `ThingType` * `Type` Relation types (or subtypes of the relation root type) represent relationships between types. Relation types have roles. Other types can play roles in relations if it’s mentioned in their definition. A relation type must specify at least one role. @@ -100,6 +99,28 @@ Casts the concept to ``EntityType``. concept.asEntityType(); ---- +[#_RelationType_asInstance_] +==== asInstance + +[source,java] +---- +default Instance asInstance() +---- + +Casts the concept to ``Instance``. + + +[caption=""] +.Returns +`Instance` + +[caption=""] +.Code examples +[source,java] +---- +concept.asInstance(); +---- + [#_RelationType_asRelation_] ==== asRelation @@ -167,48 +188,49 @@ Casts the concept to ``RoleType``. concept.asRoleType(); ---- -[#_RelationType_asThing_] -==== asThing +[#_RelationType_asValue_] +==== asValue [source,java] ---- -default Thing asThing() +default Value asValue() ---- -Casts the concept to ``Thing``. +Casts the concept to ``Value``. [caption=""] .Returns -`Thing` +`Value` [caption=""] .Code examples [source,java] ---- -concept.asThing(); +concept.asValue(); ---- -[#_RelationType_asValue_] -==== asValue +[#_RelationType_getLabel_] +==== getLabel [source,java] ---- -default Value asValue() +@CheckReturnValue +java.lang.String getLabel() ---- -Casts the concept to ``Value``. +Retrieves the unique label of the concept. If this is an ``Instance``, return the label of the type of this instance. If this is a ``Value``, return the label of the value type of the value. If this is a ``Type``, return the label of the type. [caption=""] .Returns -`Value` +`java.lang.String` [caption=""] .Code examples [source,java] ---- -concept.asValue(); +concept.getLabel(); ---- [#_RelationType_isAttribute_] @@ -303,16 +325,16 @@ Checks if the concept is an ``EntityType``. concept.isEntityType(); ---- -[#_RelationType_isRelation_] -==== isRelation +[#_RelationType_isInstance_] +==== isInstance [source,java] ---- @CheckReturnValue -default boolean isRelation() +default boolean isInstance() ---- -Checks if the concept is a ``Relation``. +Checks if the concept is an ``Instance``. [caption=""] @@ -323,19 +345,19 @@ Checks if the concept is a ``Relation``. .Code examples [source,java] ---- -concept.isRelation(); +concept.isInstance(); ---- -[#_RelationType_isRelationType_] -==== isRelationType +[#_RelationType_isRelation_] +==== isRelation [source,java] ---- @CheckReturnValue -default boolean isRelationType() +default boolean isRelation() ---- -Checks if the concept is a ``RelationType``. +Checks if the concept is a ``Relation``. [caption=""] @@ -346,19 +368,19 @@ Checks if the concept is a ``RelationType``. .Code examples [source,java] ---- -concept.isRelationType(); +concept.isRelation(); ---- -[#_RelationType_isRoleType_] -==== isRoleType +[#_RelationType_isRelationType_] +==== isRelationType [source,java] ---- @CheckReturnValue -default boolean isRoleType() +default boolean isRelationType() ---- -Checks if the concept is a ``RoleType``. +Checks if the concept is a ``RelationType``. [caption=""] @@ -369,19 +391,19 @@ Checks if the concept is a ``RoleType``. .Code examples [source,java] ---- -concept.isRoleType(); +concept.isRelationType(); ---- -[#_RelationType_isThing_] -==== isThing +[#_RelationType_isRoleType_] +==== isRoleType [source,java] ---- @CheckReturnValue -default boolean isThing() +default boolean isRoleType() ---- -Checks if the concept is a ``Thing``. +Checks if the concept is a ``RoleType``. [caption=""] @@ -392,7 +414,7 @@ Checks if the concept is a ``Thing``. .Code examples [source,java] ---- -concept.isThing(); +concept.isRoleType(); ---- [#_RelationType_isValue_] diff --git a/docs/modules/ROOT/partials/java/schema/RoleType.adoc b/docs/modules/ROOT/partials/java/schema/RoleType.adoc index 3dc6e645da..941b17854a 100644 --- a/docs/modules/ROOT/partials/java/schema/RoleType.adoc +++ b/docs/modules/ROOT/partials/java/schema/RoleType.adoc @@ -99,6 +99,28 @@ Casts the concept to ``EntityType``. concept.asEntityType(); ---- +[#_RoleType_asInstance_] +==== asInstance + +[source,java] +---- +default Instance asInstance() +---- + +Casts the concept to ``Instance``. + + +[caption=""] +.Returns +`Instance` + +[caption=""] +.Code examples +[source,java] +---- +concept.asInstance(); +---- + [#_RoleType_asRelation_] ==== asRelation @@ -166,70 +188,49 @@ Casts the concept to ``RoleType``. concept.asRoleType(); ---- -[#_RoleType_asThing_] -==== asThing - -[source,java] ----- -default Thing asThing() ----- - -Casts the concept to ``Thing``. - - -[caption=""] -.Returns -`Thing` - -[caption=""] -.Code examples -[source,java] ----- -concept.asThing(); ----- - -[#_RoleType_asThingType_] -==== asThingType +[#_RoleType_asValue_] +==== asValue [source,java] ---- -default ThingType asThingType() +default Value asValue() ---- -Casts the concept to ``ThingType``. +Casts the concept to ``Value``. [caption=""] .Returns -`ThingType` +`Value` [caption=""] .Code examples [source,java] ---- -concept.asThingType(); +concept.asValue(); ---- -[#_RoleType_asValue_] -==== asValue +[#_RoleType_getLabel_] +==== getLabel [source,java] ---- -default Value asValue() +@CheckReturnValue +java.lang.String getLabel() ---- -Casts the concept to ``Value``. +Retrieves the unique label of the concept. If this is an ``Instance``, return the label of the type of this instance. If this is a ``Value``, return the label of the value type of the value. If this is a ``Type``, return the label of the type. [caption=""] .Returns -`Value` +`java.lang.String` [caption=""] .Code examples [source,java] ---- -concept.asValue(); +concept.getLabel(); ---- [#_RoleType_isAttribute_] @@ -324,39 +325,16 @@ Checks if the concept is an ``EntityType``. concept.isEntityType(); ---- -[#_RoleType_isRelation_] -==== isRelation +[#_RoleType_isInstance_] +==== isInstance [source,java] ---- @CheckReturnValue -default boolean isRelation() ----- - -Checks if the concept is a ``Relation``. - - -[caption=""] -.Returns -`boolean` - -[caption=""] -.Code examples -[source,java] ----- -concept.isRelation(); ----- - -[#_RoleType_isRelationType_] -==== isRelationType - -[source,java] ----- -@CheckReturnValue -default boolean isRelationType() +default boolean isInstance() ---- -Checks if the concept is a ``RelationType``. +Checks if the concept is an ``Instance``. [caption=""] @@ -367,19 +345,19 @@ Checks if the concept is a ``RelationType``. .Code examples [source,java] ---- -concept.isRelationType(); +concept.isInstance(); ---- -[#_RoleType_isRoleType_] -==== isRoleType +[#_RoleType_isRelation_] +==== isRelation [source,java] ---- @CheckReturnValue -default boolean isRoleType() +default boolean isRelation() ---- -Checks if the concept is a ``RoleType``. +Checks if the concept is a ``Relation``. [caption=""] @@ -390,19 +368,19 @@ Checks if the concept is a ``RoleType``. .Code examples [source,java] ---- -concept.isRoleType(); +concept.isRelation(); ---- -[#_RoleType_isThing_] -==== isThing +[#_RoleType_isRelationType_] +==== isRelationType [source,java] ---- @CheckReturnValue -default boolean isThing() +default boolean isRelationType() ---- -Checks if the concept is a ``Thing``. +Checks if the concept is a ``RelationType``. [caption=""] @@ -413,19 +391,19 @@ Checks if the concept is a ``Thing``. .Code examples [source,java] ---- -concept.isThing(); +concept.isRelationType(); ---- -[#_RoleType_isThingType_] -==== isThingType +[#_RoleType_isRoleType_] +==== isRoleType [source,java] ---- @CheckReturnValue -default boolean isThingType() +default boolean isRoleType() ---- -Checks if the concept is a ``ThingType``. +Checks if the concept is a ``RoleType``. [caption=""] @@ -436,7 +414,7 @@ Checks if the concept is a ``ThingType``. .Code examples [source,java] ---- -concept.isThingType(); +concept.isRoleType(); ---- [#_RoleType_isValue_] diff --git a/docs/modules/ROOT/partials/java/schema/ThingType.adoc b/docs/modules/ROOT/partials/java/schema/ThingType.adoc deleted file mode 100644 index 1c4df2f80b..0000000000 --- a/docs/modules/ROOT/partials/java/schema/ThingType.adoc +++ /dev/null @@ -1,464 +0,0 @@ -[#_ThingType] -=== ThingType - -*Package*: `com.typedb.driver.api.concept.type` - -*Superinterfaces:* - -* `Concept` -* `Type` - -// tag::methods[] -[#_ThingType_asAttribute_] -==== asAttribute - -[source,java] ----- -default Attribute asAttribute() ----- - -Casts the concept to ``Attribute``. - - -[caption=""] -.Returns -`Attribute` - -[caption=""] -.Code examples -[source,java] ----- -concept.asAttribute(); ----- - -[#_ThingType_asAttributeType_] -==== asAttributeType - -[source,java] ----- -default AttributeType asAttributeType() ----- - -Casts the concept to ``AttributeType``. - - -[caption=""] -.Returns -`AttributeType` - -[caption=""] -.Code examples -[source,java] ----- -concept.asAttributeType(); ----- - -[#_ThingType_asEntity_] -==== asEntity - -[source,java] ----- -default Entity asEntity() ----- - -Casts the concept to ``Entity``. - - -[caption=""] -.Returns -`Entity` - -[caption=""] -.Code examples -[source,java] ----- -concept.asEntity(); ----- - -[#_ThingType_asEntityType_] -==== asEntityType - -[source,java] ----- -default EntityType asEntityType() ----- - -Casts the concept to ``EntityType``. - - -[caption=""] -.Returns -`EntityType` - -[caption=""] -.Code examples -[source,java] ----- -concept.asEntityType(); ----- - -[#_ThingType_asRelation_] -==== asRelation - -[source,java] ----- -default Relation asRelation() ----- - -Casts the concept to ``Relation``. - - -[caption=""] -.Returns -`Relation` - -[caption=""] -.Code examples -[source,java] ----- -concept.asRelation(); ----- - -[#_ThingType_asRelationType_] -==== asRelationType - -[source,java] ----- -default RelationType asRelationType() ----- - -Casts the concept to ``RelationType``. - - -[caption=""] -.Returns -`RelationType` - -[caption=""] -.Code examples -[source,java] ----- -concept.asRelationType(); ----- - -[#_ThingType_asRoleType_] -==== asRoleType - -[source,java] ----- -default RoleType asRoleType() ----- - -Casts the concept to ``RoleType``. - - -[caption=""] -.Returns -`RoleType` - -[caption=""] -.Code examples -[source,java] ----- -concept.asRoleType(); ----- - -[#_ThingType_asThing_] -==== asThing - -[source,java] ----- -default Thing asThing() ----- - -Casts the concept to ``Thing``. - - -[caption=""] -.Returns -`Thing` - -[caption=""] -.Code examples -[source,java] ----- -concept.asThing(); ----- - -[#_ThingType_asThingType_] -==== asThingType - -[source,java] ----- -@CheckReturnValue -default ThingType asThingType() ----- - -Casts the concept to ``ThingType``. - - -[caption=""] -.Returns -`ThingType` - -[caption=""] -.Code examples -[source,java] ----- -concept.asThingType(); ----- - -[#_ThingType_asValue_] -==== asValue - -[source,java] ----- -default Value asValue() ----- - -Casts the concept to ``Value``. - - -[caption=""] -.Returns -`Value` - -[caption=""] -.Code examples -[source,java] ----- -concept.asValue(); ----- - -[#_ThingType_isAttribute_] -==== isAttribute - -[source,java] ----- -@CheckReturnValue -default boolean isAttribute() ----- - -Checks if the concept is an ``Attribute``. - - -[caption=""] -.Returns -`boolean` - -[caption=""] -.Code examples -[source,java] ----- -concept.isAttribute(); ----- - -[#_ThingType_isAttributeType_] -==== isAttributeType - -[source,java] ----- -@CheckReturnValue -default boolean isAttributeType() ----- - -Checks if the concept is an ``AttributeType``. - - -[caption=""] -.Returns -`boolean` - -[caption=""] -.Code examples -[source,java] ----- -concept.isAttributeType(); ----- - -[#_ThingType_isEntity_] -==== isEntity - -[source,java] ----- -@CheckReturnValue -default boolean isEntity() ----- - -Checks if the concept is an ``Entity``. - - -[caption=""] -.Returns -`boolean` - -[caption=""] -.Code examples -[source,java] ----- -concept.isEntity(); ----- - -[#_ThingType_isEntityType_] -==== isEntityType - -[source,java] ----- -@CheckReturnValue -default boolean isEntityType() ----- - -Checks if the concept is an ``EntityType``. - - -[caption=""] -.Returns -`boolean` - -[caption=""] -.Code examples -[source,java] ----- -concept.isEntityType(); ----- - -[#_ThingType_isRelation_] -==== isRelation - -[source,java] ----- -@CheckReturnValue -default boolean isRelation() ----- - -Checks if the concept is a ``Relation``. - - -[caption=""] -.Returns -`boolean` - -[caption=""] -.Code examples -[source,java] ----- -concept.isRelation(); ----- - -[#_ThingType_isRelationType_] -==== isRelationType - -[source,java] ----- -@CheckReturnValue -default boolean isRelationType() ----- - -Checks if the concept is a ``RelationType``. - - -[caption=""] -.Returns -`boolean` - -[caption=""] -.Code examples -[source,java] ----- -concept.isRelationType(); ----- - -[#_ThingType_isRoleType_] -==== isRoleType - -[source,java] ----- -@CheckReturnValue -default boolean isRoleType() ----- - -Checks if the concept is a ``RoleType``. - - -[caption=""] -.Returns -`boolean` - -[caption=""] -.Code examples -[source,java] ----- -concept.isRoleType(); ----- - -[#_ThingType_isThing_] -==== isThing - -[source,java] ----- -@CheckReturnValue -default boolean isThing() ----- - -Checks if the concept is a ``Thing``. - - -[caption=""] -.Returns -`boolean` - -[caption=""] -.Code examples -[source,java] ----- -concept.isThing(); ----- - -[#_ThingType_isThingType_] -==== isThingType - -[source,java] ----- -@CheckReturnValue -default boolean isThingType() ----- - -Checks if the concept is a ``ThingType``. - - -[caption=""] -.Returns -`boolean` - -[caption=""] -.Code examples -[source,java] ----- -concept.isThingType(); ----- - -[#_ThingType_isValue_] -==== isValue - -[source,java] ----- -@CheckReturnValue -default boolean isValue() ----- - -Checks if the concept is a ``Value``. - - -[caption=""] -.Returns -`boolean` - -[caption=""] -.Code examples -[source,java] ----- -concept.isValue(); ----- - -// end::methods[] - diff --git a/docs/modules/ROOT/partials/java/schema/Type.adoc b/docs/modules/ROOT/partials/java/schema/Type.adoc index 6558f343b3..7c5c9b2bc5 100644 --- a/docs/modules/ROOT/partials/java/schema/Type.adoc +++ b/docs/modules/ROOT/partials/java/schema/Type.adoc @@ -96,6 +96,28 @@ Casts the concept to ``EntityType``. concept.asEntityType(); ---- +[#_Type_asInstance_] +==== asInstance + +[source,java] +---- +default Instance asInstance() +---- + +Casts the concept to ``Instance``. + + +[caption=""] +.Returns +`Instance` + +[caption=""] +.Code examples +[source,java] +---- +concept.asInstance(); +---- + [#_Type_asRelation_] ==== asRelation @@ -162,50 +184,6 @@ Casts the concept to ``RoleType``. concept.asRoleType(); ---- -[#_Type_asThing_] -==== asThing - -[source,java] ----- -default Thing asThing() ----- - -Casts the concept to ``Thing``. - - -[caption=""] -.Returns -`Thing` - -[caption=""] -.Code examples -[source,java] ----- -concept.asThing(); ----- - -[#_Type_asThingType_] -==== asThingType - -[source,java] ----- -default ThingType asThingType() ----- - -Casts the concept to ``ThingType``. - - -[caption=""] -.Returns -`ThingType` - -[caption=""] -.Code examples -[source,java] ----- -concept.asThingType(); ----- - [#_Type_asType_] ==== asType @@ -257,21 +235,21 @@ concept.asValue(); [source,java] ---- @CheckReturnValue -Label getLabel() +java.lang.String getLabel() ---- -Retrieves the unique label of the type. +Retrieves the unique label of the concept. If this is an ``Instance``, return the label of the type of this instance. If this is a ``Value``, return the label of the value type of the value. If this is a ``Type``, return the label of the type. [caption=""] .Returns -`Label` +`java.lang.String` [caption=""] .Code examples [source,java] ---- -type.getLabel(); +concept.getLabel(); ---- [#_Type_isAttribute_] @@ -366,39 +344,16 @@ Checks if the concept is an ``EntityType``. concept.isEntityType(); ---- -[#_Type_isRelation_] -==== isRelation - -[source,java] ----- -@CheckReturnValue -default boolean isRelation() ----- - -Checks if the concept is a ``Relation``. - - -[caption=""] -.Returns -`boolean` - -[caption=""] -.Code examples -[source,java] ----- -concept.isRelation(); ----- - -[#_Type_isRelationType_] -==== isRelationType +[#_Type_isInstance_] +==== isInstance [source,java] ---- @CheckReturnValue -default boolean isRelationType() +default boolean isInstance() ---- -Checks if the concept is a ``RelationType``. +Checks if the concept is an ``Instance``. [caption=""] @@ -409,19 +364,19 @@ Checks if the concept is a ``RelationType``. .Code examples [source,java] ---- -concept.isRelationType(); +concept.isInstance(); ---- -[#_Type_isRoleType_] -==== isRoleType +[#_Type_isRelation_] +==== isRelation [source,java] ---- @CheckReturnValue -default boolean isRoleType() +default boolean isRelation() ---- -Checks if the concept is a ``RoleType``. +Checks if the concept is a ``Relation``. [caption=""] @@ -432,19 +387,19 @@ Checks if the concept is a ``RoleType``. .Code examples [source,java] ---- -concept.isRoleType(); +concept.isRelation(); ---- -[#_Type_isThing_] -==== isThing +[#_Type_isRelationType_] +==== isRelationType [source,java] ---- @CheckReturnValue -default boolean isThing() +default boolean isRelationType() ---- -Checks if the concept is a ``Thing``. +Checks if the concept is a ``RelationType``. [caption=""] @@ -455,19 +410,19 @@ Checks if the concept is a ``Thing``. .Code examples [source,java] ---- -concept.isThing(); +concept.isRelationType(); ---- -[#_Type_isThingType_] -==== isThingType +[#_Type_isRoleType_] +==== isRoleType [source,java] ---- @CheckReturnValue -default boolean isThingType() +default boolean isRoleType() ---- -Checks if the concept is a ``ThingType``. +Checks if the concept is a ``RoleType``. [caption=""] @@ -478,7 +433,7 @@ Checks if the concept is a ``ThingType``. .Code examples [source,java] ---- -concept.isThingType(); +concept.isRoleType(); ---- [#_Type_isType_] diff --git a/docs/modules/ROOT/partials/java/transaction/Transaction.adoc b/docs/modules/ROOT/partials/java/transaction/Transaction.adoc index 7f08f85a38..62aaf4ceb9 100644 --- a/docs/modules/ROOT/partials/java/transaction/Transaction.adoc +++ b/docs/modules/ROOT/partials/java/transaction/Transaction.adoc @@ -61,7 +61,7 @@ transaction.commit() Transaction.Type getType() ---- -The transaction’s type (READ/WRITE/SCHEMA) +The transaction’s type (READ, WRITE, or SCHEMA) [caption=""] diff --git a/docs/modules/ROOT/partials/java/value/Duration.adoc b/docs/modules/ROOT/partials/java/value/Duration.adoc new file mode 100644 index 0000000000..6ad70914aa --- /dev/null +++ b/docs/modules/ROOT/partials/java/value/Duration.adoc @@ -0,0 +1,225 @@ +[#_Duration] +=== Duration + +*Package*: `com.typedb.driver.common` + +// tag::methods[] +[#_Duration_equals_java_lang_Object] +==== equals + +[source,java] +---- +public boolean equals​(java.lang.Object obj) +---- + +Checks if this Duration is equal to another object. + + +[caption=""] +.Input parameters +[cols=",,"] +[options="header"] +|=== +|Name |Description |Type +a| `obj` a| Object to compare with a| `java.lang.Object` +|=== + +[caption=""] +.Returns +`public boolean` + +[caption=""] +.Code examples +[source,java] +---- +label.equals(obj); +---- + +[#_Duration_getDatePart_] +==== getDatePart + +[source,java] +---- +public java.time.Period getDatePart() +---- + +Returns the date part of this duration. + + +[caption=""] +.Returns +`public java.time.Period` + +[caption=""] +.Code examples +[source,java] +---- +duration.getDatePart(); +---- + +[#_Duration_getDays_] +==== getDays + +[source,java] +---- +public int getDays() +---- + +Returns the amount of days of this ``Duration`` from the date part. + + +[caption=""] +.Returns +`public int` + +[caption=""] +.Code examples +[source,java] +---- +duration.getMonths(); +---- + +[#_Duration_getMonths_] +==== getMonths + +[source,java] +---- +public int getMonths() +---- + +Returns the amount of months of this ``Duration`` from the date part. + + +[caption=""] +.Returns +`public int` + +[caption=""] +.Code examples +[source,java] +---- +duration.getMonths(); +---- + +[#_Duration_getNano_] +==== getNano + +[source,java] +---- +public long getNano() +---- + +Returns the number of nanoseconds within the second in this ``Duration`` from the time part. + + +[caption=""] +.Returns +`public long` + +[caption=""] +.Code examples +[source,java] +---- +duration.getNano(); +---- + +[#_Duration_getSeconds_] +==== getSeconds + +[source,java] +---- +public long getSeconds() +---- + +Returns the amount of seconds of this ``Duration`` from the time part. + + +[caption=""] +.Returns +`public long` + +[caption=""] +.Code examples +[source,java] +---- +duration.getSeconds(); +---- + +[#_Duration_getTimePart_] +==== getTimePart + +[source,java] +---- +public java.time.Duration getTimePart() +---- + +Returns the time part of this duration. + + +[caption=""] +.Returns +`public java.time.Duration` + +[caption=""] +.Code examples +[source,java] +---- +duration.getTimePart(); +---- + +[#_Duration_parse_java_lang_String] +==== parse + +[source,java] +---- +public static Duration parse​(java.lang.String durationString) +---- + +Parses a ``Duration`` object from a string in ISO 8601 format. Throws java.time exceptions + + +[caption=""] +.Input parameters +[cols=",,"] +[options="header"] +|=== +|Name |Description |Type +a| `durationString` a| A string representation of the duration. Expected format: PnYnMnDTnHnMnS or PnW. a| `java.lang.String` +|=== + +[caption=""] +.Returns +`public static Duration` + +[caption=""] +.Code examples +[source,java] +---- +Duration.parse("P1Y10M7DT15H44M5.00394892S"); + Duration.parse("P55W"); +---- + +[#_Duration_toString_] +==== toString + +[source,java] +---- +public java.lang.String toString() +---- + +Returns the string representation of the duration. + + +[caption=""] +.Returns +`public java.lang.String` + +[caption=""] +.Code examples +[source,java] +---- +duration.toString(); +---- + +// end::methods[] + diff --git a/docs/modules/ROOT/partials/rust/answer/QueryAnswer.adoc b/docs/modules/ROOT/partials/rust/answer/QueryAnswer.adoc index 8c6783b969..a08f3fc6ce 100644 --- a/docs/modules/ROOT/partials/rust/answer/QueryAnswer.adoc +++ b/docs/modules/ROOT/partials/rust/answer/QueryAnswer.adoc @@ -10,7 +10,7 @@ |Variant a| `ConceptDocumentStream(Arc, BoxStream<'static, Result>)` a| `ConceptRowStream(Arc, BoxStream<'static, Result>)` -a| `Ok()` +a| `Ok(QueryType)` |=== // end::enum_constants[] diff --git a/java/BUILD b/java/BUILD index 72e2a6902f..4a3d9f42d5 100644 --- a/java/BUILD +++ b/java/BUILD @@ -27,6 +27,7 @@ load(":rules.bzl", "swig_native_java_library") load("@google_bazel_common//tools/javadoc:javadoc.bzl", "javadoc_library") load("//tool/docs:java/rules.bzl", "javadoc_to_adoc") load("//java:docs_structure.bzl", "dir_mapping") +load("//tool/docs:examples/rules.bzl", "test_to_example", "update_markdown_example") java_library( name = "driver-java", @@ -130,6 +131,31 @@ javadoc_to_adoc( docs_dirs = dir_mapping, ) +test_to_example( + name = "example_core", + input = "//java/test/integration/core:ExampleTest.java", + output = "java/core_example", + removed_lines = ["assert", "todo"], + changed_words = { + "ExampleTest": "TypeDBExample", + }, + header_comment_sign = "//", +) + +# Temporary manual usage: +# 1. Run: bazel run //java:example_core; bazel run //java:readme_example_core +# 2. Enjoy your updated README.md! +update_markdown_example( + name = "readme_example_core", + # TODO: Use ":example_core" as a rule with a single output instead + input = ":core_example", + output = ":README.md", + start_marker = "CORE_EXAMPLE_START_MARKER", + end_marker = "CORE_EXAMPLE_END_MARKER", + language_tag = "java", + removed_header = "//", +) + checkstyle_test( name = "checkstyle", size = "small", @@ -137,6 +163,7 @@ checkstyle_test( exclude = glob([ "README.md", "docs/**/*.adoc", + "core_example", ]), license_type = "apache-header", ) diff --git a/java/README.md b/java/README.md index a51ee122ea..e7d16c1a5a 100644 --- a/java/README.md +++ b/java/README.md @@ -53,6 +53,198 @@ Further documentation: https://typedb.com/docs/drivers/java/overview bazel-bin/java/pom.xml ``` +## Example usage + +### TypeDB Core + + + +```java +import com.typedb.driver.TypeDB; +import com.typedb.driver.api.Driver; +import com.typedb.driver.api.QueryType; +import com.typedb.driver.api.Transaction; +import com.typedb.driver.api.answer.ConceptRow; +import com.typedb.driver.api.answer.ConceptRowIterator; +import com.typedb.driver.api.answer.QueryAnswer; +import com.typedb.driver.api.concept.Concept; +import com.typedb.driver.api.concept.type.AttributeType; +import com.typedb.driver.api.concept.type.EntityType; +import com.typedb.driver.api.database.Database; +import com.typedb.driver.common.Promise; +import com.typedb.driver.common.exception.TypeDBDriverException; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +public class TypeDBExample { + public void example() { + // Open a driver connection. Try-with-resources can be used for automatic driver connection management + try (Driver driver = TypeDB.coreDriver(TypeDB.DEFAULT_ADDRESS)) { + // Create a database + driver.databases().create("typedb"); + Database database = driver.databases().get("typedb"); + + // Open transactions of 3 types + Transaction tx = driver.transaction(database.name(), Transaction.Type.READ); + + // Use "try" blocks to catch driver exceptions + try { + // Execute any TypeDB query using TypeQL. Wrong queries are rejected with an explicit exception + Promise promise = tx.query("define entity i-cannot-be-defined-in-read-transactions;"); + + System.out.println("The result is still promised, so it needs resolving even in case of errors!"); + promise.resolve(); + } catch (TypeDBDriverException expectedException) { + System.out.println("Once the query's promise is resolved, the exception is revealed: " + expectedException); + } finally { + // Don't forget to close the transaction! + tx.close(); + } + + // Open a schema transaction to make schema changes + // Use try-with-resources blocks to forget about "close" operations (similarly to connections) + try (Transaction transaction = driver.transaction(database.name(), Transaction.Type.SCHEMA)) { + String defineQuery = "define " + + "entity person, owns name, owns age; " + + "attribute name, value string;\n" + + "attribute age, value long;"; + + QueryAnswer answer = transaction.query(defineQuery).resolve(); + + // Commit automatically closes the transaction. It can still be safely called inside "try" blocks + transaction.commit(); + } + + + // Open a read transaction to safely read anything without database modifications + try (Transaction transaction = driver.transaction(database.name(), Transaction.Type.READ)) { + QueryAnswer entityAnswer = transaction.query("match entity $x;").resolve(); + + // Collect concept rows that represent the answer as a table + List entityRows = entityAnswer.asConceptRows().stream().collect(Collectors.toList()); + ConceptRow entityRow = entityRows.get(0); + + // Collect column names to get concepts by index if the variable names are lost + List entityHeader = entityRow.columnNames().collect(Collectors.toList()); + + String columnName = entityHeader.get(0); + + // Get concept by the variable name (column name) + Concept conceptByName = entityRow.get(columnName); + + // Get concept by the header's index + Concept conceptByIndex = entityRow.getIndex(0); + + // Check if it's an entity type before the conversion + if (conceptByName.isEntityType()) { + System.out.printf("Getting concepts by variable names and indexes is equally correct. " + + "Both represent the defined entity type: '%s' (in case of a doubt: '%s')%n", + conceptByName.asEntityType().getLabel(), + conceptByIndex.asEntityType().getLabel()); + } + + // Continue querying in the same transaction if needed + QueryAnswer attributeAnswer = transaction.query("match attribute $a;").resolve(); + + // ConceptRowIterator can be used as any other Iterator + ConceptRowIterator attributeRowIterator = attributeAnswer.asConceptRows(); + + while (attributeRowIterator.hasNext()) { + ConceptRow attributeRow = attributeRowIterator.next(); + + // Column names are a stream, so they can be used in a similar way + Iterator columnNameIterator = attributeRow.columnNames().iterator(); + columnName = columnNameIterator.next(); + + conceptByName = attributeRow.get(columnName); + + // Check if it's an attribute type before the conversion + if (conceptByName.isAttributeType()) { + AttributeType attributeType = conceptByName.asAttributeType(); + System.out.printf("Defined attribute type's label: '%s', value type: '%s'%n", attributeType.getLabel(), attributeType.getValueType()); + + System.out.printf("It is also possible to just print the concept itself: '%s'%n", conceptByName); + } + } + } + + // Open a write transaction to insert data + try (Transaction transaction = driver.transaction(database.name(), Transaction.Type.WRITE)) { + String insertQuery = "insert $z isa person, has age 10; $x isa person, has age 20, has name \"John\";"; + QueryAnswer answer = transaction.query(insertQuery).resolve(); + + // Insert queries also return concept rows + List rows = answer.asConceptRows().stream().collect(Collectors.toList()); + ConceptRow row = rows.get(0); + row.columnNames().iterator().forEachRemaining(columnName -> { + Concept insertedConcept = row.get(columnName); + System.out.printf("Successfully inserted $%s: %s%n", columnName, insertedConcept); + if (insertedConcept.isEntity()) { + System.out.println("This time, it's an entity, not a type!"); + } + }); + + // It is possible to ask for the column names again + List header = row.columnNames().collect(Collectors.toList()); + + Concept x = row.getIndex(header.indexOf("x")); + if (x.isEntity()) { + System.out.println("Each entity receives a unique IID. It can be retrieved directly: " + x.asEntity().getIID()); + } + + // Do not forget to commit if the changes should be persisted + transaction.commit(); + } + + // Open a read transaction to verify that the inserted data is saved + try (Transaction transaction = driver.transaction(database.name(), Transaction.Type.READ)) { + // A match query can be used for concept row outputs + String var = "x"; + QueryAnswer matchAnswer = transaction.query(String.format("match $%s isa person;", var)).resolve(); + + // Simple match queries always return concept rows + AtomicInteger matchCount = new AtomicInteger(0); + matchAnswer.asConceptRows().stream().forEach(row -> { + Concept x = row.get(var); + EntityType xType = x.asEntity().getType().asEntityType(); + matchCount.incrementAndGet(); + System.out.printf("Found a person %s of type %s%n", x, xType); + }); + System.out.println("Total persons found: " + matchCount.get()); + + // A fetch query can be used for concept document outputs with flexible structure + QueryAnswer fetchAnswer = transaction.query("match" + + " $x isa! person, has $a;" + + " $a isa! $t;" + + "fetch {" + + " \"single attribute type\": $t," + + " \"single attribute\": $a," + + " \"all attributes\": { $x.* }," + + "};").resolve(); + + // Fetch queries always return concept documents + AtomicInteger fetchCount = new AtomicInteger(0); + fetchAnswer.asConceptDocuments().stream().forEach(document -> { + System.out.println("Fetched a document: " + document); + System.out.print("This document contains an attribute of type: "); + System.out.println(document.asObject().get("single attribute type").asObject().get("label")); + + fetchCount.incrementAndGet(); + }); + System.out.println("Total documents fetched: " + fetchCount.get()); + } + } + + System.out.println("More examples can be found in the API reference and the documentation.\nWelcome to TypeDB!"); + } +} +``` + + + ## FAQs **Q:** I see a large number of Netty and gRPC log messages. How can I disable them? diff --git a/java/api/answer/JSON.java b/java/api/answer/JSON.java index 1b4e0b5294..0f097993d3 100644 --- a/java/api/answer/JSON.java +++ b/java/api/answer/JSON.java @@ -30,7 +30,7 @@ import java.util.stream.Collectors; import java.util.stream.StreamSupport; -import static com.typedb.driver.common.exception.ErrorMessage.Internal.ILLEGAL_CAST; +import static com.typedb.driver.common.exception.ErrorMessage.Concept.INVALID_VALUE_CASTING; import static com.typedb.driver.common.exception.ErrorMessage.Internal.ILLEGAL_STATE; import static com.typedb.driver.common.util.Objects.className; import static java.util.stream.Collectors.joining; @@ -84,23 +84,23 @@ public boolean isBoolean() { } public Map asObject() { - throw new TypeDBDriverException(ILLEGAL_CAST, className(Map.class)); + throw new TypeDBDriverException(INVALID_VALUE_CASTING, className(Map.class)); } public List asArray() { - throw new TypeDBDriverException(ILLEGAL_CAST, className(List.class)); + throw new TypeDBDriverException(INVALID_VALUE_CASTING, className(List.class)); } public double asNumber() { - throw new TypeDBDriverException(ILLEGAL_CAST, className(double.class)); + throw new TypeDBDriverException(INVALID_VALUE_CASTING, className(double.class)); } public java.lang.String asString() { - throw new TypeDBDriverException(ILLEGAL_CAST, className(java.lang.String.class)); + throw new TypeDBDriverException(INVALID_VALUE_CASTING, className(java.lang.String.class)); } public boolean asBoolean() { - throw new TypeDBDriverException(ILLEGAL_CAST, className(boolean.class)); + throw new TypeDBDriverException(INVALID_VALUE_CASTING, className(boolean.class)); } private static class Object extends JSON { diff --git a/java/common/Duration.java b/java/common/Duration.java index 3523f070b9..5e4793a6d5 100644 --- a/java/common/Duration.java +++ b/java/common/Duration.java @@ -22,6 +22,7 @@ import com.typedb.driver.common.exception.ErrorMessage; import com.typedb.driver.common.exception.TypeDBDriverException; +import java.time.format.DateTimeParseException; import java.util.Objects; public class Duration { @@ -49,6 +50,28 @@ public Duration(java.time.Period datePart, java.time.Duration timePart) { this.timePart = timePart; } + /** + * Parses a Duration object from a string in ISO 8601 format. Throws java.time exceptions + * + *

Examples

+ *
+     *     Duration.parse("P1Y10M7DT15H44M5.00394892S");
+     *     Duration.parse("P55W");
+     * 
+ * + * @param durationString A string representation of the duration. Expected format: PnYnMnDTnHnMnS or PnW. + * @throws DateTimeParseException if the text cannot be parsed to a Duration. + */ + public static Duration parse(String durationString) { + String[] durationParts = durationString.split("T"); + java.time.Period period = java.time.Period.parse(durationParts[0]); + if (durationParts.length == 1) { + return new Duration(period, java.time.Duration.ofNanos(0)); + } else { + return new Duration(period, java.time.Duration.parse("PT" + durationParts[1])); + } + } + /** * Returns the date part of this duration. * @@ -73,6 +96,54 @@ public java.time.Duration getTimePart() { return timePart; } + /** + * Returns the amount of months of this Duration from the date part. + * + *

Examples

+ *
+     * duration.getMonths();
+     * 
+ */ + public int getMonths() { + return datePart.getMonths(); + } + + /** + * Returns the amount of days of this Duration from the date part. + * + *

Examples

+ *
+     * duration.getMonths();
+     * 
+ */ + public int getDays() { + return datePart.getDays(); + } + + /** + * Returns the amount of seconds of this Duration from the time part. + * + *

Examples

+ *
+     * duration.getSeconds();
+     * 
+ */ + public long getSeconds() { + return timePart.getSeconds(); + } + + /** + * Returns the number of nanoseconds within the second in this Duration from the time part. + * + *

Examples

+ *
+     * duration.getNano();
+     * 
+ */ + public long getNano() { + return timePart.getNano(); + } + /** * Returns the string representation of the duration. * diff --git a/java/common/exception/ErrorMessage.java b/java/common/exception/ErrorMessage.java index da900b9d7f..3464906154 100644 --- a/java/common/exception/ErrorMessage.java +++ b/java/common/exception/ErrorMessage.java @@ -81,10 +81,8 @@ public static class Driver extends ErrorMessage { new Driver(7, "The database has been deleted and no further operation is allowed."); public static final Driver POSITIVE_VALUE_REQUIRED = new Driver(8, "Value cannot be less than 1, was: '%d'."); - public static final Driver MISSING_DB_NAME = - new Driver(9, "Database name cannot be null."); public static final Driver UNIMPLEMENTED = - new Driver(10, "This operation is not implemented yet."); + new Driver(9, "This operation is not implemented yet."); private static final String codePrefix = "JDR"; private static final String messagePrefix = "Driver Error"; @@ -101,6 +99,8 @@ public static class Concept extends ErrorMessage { new Concept(2, "Invalid query answer conversion from '%s' to '%s'."); public static final Concept MISSING_VARIABLE = new Concept(3, "Variable name cannot be null or empty."); + public static final Concept INVALID_VALUE_CASTING = + new Concept(4, "Invalid value casting to '%s'."); private static final String codePrefix = "JCO"; private static final String messagePrefix = "Concept Error"; @@ -129,10 +129,8 @@ public static class Internal extends ErrorMessage { new Internal(1, "Unexpected native value encountered!"); public static final Internal ILLEGAL_STATE = new Internal(2, "Illegal state has been reached!"); - public static final Internal ILLEGAL_CAST = - new Internal(3, "Illegal casting operation to '%s'."); public static final Internal NULL_NATIVE_VALUE = - new Internal(4, "Unhandled null pointer to a native object encountered!"); + new Internal(3, "Unhandled null pointer to a native object encountered!"); private static final String codePrefix = "JIN"; private static final String messagePrefix = "Java Internal Error"; diff --git a/java/concept/value/ValueImpl.java b/java/concept/value/ValueImpl.java index 274f3bcf61..32a77ced19 100644 --- a/java/concept/value/ValueImpl.java +++ b/java/concept/value/ValueImpl.java @@ -41,7 +41,7 @@ import java.util.stream.Collectors; import static com.typedb.driver.common.collection.Collections.pair; -import static com.typedb.driver.common.exception.ErrorMessage.Internal.ILLEGAL_CAST; +import static com.typedb.driver.common.exception.ErrorMessage.Concept.INVALID_VALUE_CASTING; import static com.typedb.driver.common.exception.ErrorMessage.Internal.UNEXPECTED_NATIVE_VALUE; import static com.typedb.driver.jni.typedb_driver.value_get_boolean; import static com.typedb.driver.jni.typedb_driver.value_get_date_as_seconds; @@ -144,25 +144,25 @@ public Object asUntyped() { @Override public boolean asBoolean() { - if (!isBoolean()) throw new TypeDBDriverException(ILLEGAL_CAST, "boolean"); + if (!isBoolean()) throw new TypeDBDriverException(INVALID_VALUE_CASTING, "boolean"); return value_get_boolean(nativeObject); } @Override public long asLong() { - if (!isLong()) throw new TypeDBDriverException(ILLEGAL_CAST, "long"); + if (!isLong()) throw new TypeDBDriverException(INVALID_VALUE_CASTING, "long"); return value_get_long(nativeObject); } @Override public double asDouble() { - if (!isDouble()) throw new TypeDBDriverException(ILLEGAL_CAST, "double"); + if (!isDouble()) throw new TypeDBDriverException(INVALID_VALUE_CASTING, "double"); return value_get_double(nativeObject); } @Override public BigDecimal asDecimal() { - if (!isDecimal()) throw new TypeDBDriverException(ILLEGAL_CAST, "decimal"); + if (!isDecimal()) throw new TypeDBDriverException(INVALID_VALUE_CASTING, "decimal"); com.typedb.driver.jni.Decimal nativeDecimal = value_get_decimal(nativeObject); BigInteger nativeFractional = nativeDecimal.getFractional(); BigDecimal integerPart = new BigDecimal(nativeDecimal.getInteger()); @@ -174,25 +174,25 @@ public BigDecimal asDecimal() { @Override public String asString() { - if (!isString()) throw new TypeDBDriverException(ILLEGAL_CAST, "string"); + if (!isString()) throw new TypeDBDriverException(INVALID_VALUE_CASTING, "string"); return value_get_string(nativeObject); } @Override public LocalDate asDate() { - if (!isDate()) throw new TypeDBDriverException(ILLEGAL_CAST, "date"); + if (!isDate()) throw new TypeDBDriverException(INVALID_VALUE_CASTING, "date"); return LocalDateTime.ofInstant(Instant.ofEpochSecond(value_get_date_as_seconds(nativeObject)), ZoneOffset.UTC).toLocalDate(); } @Override public LocalDateTime asDatetime() { - if (!isDatetime()) throw new TypeDBDriverException(ILLEGAL_CAST, "datetime"); + if (!isDatetime()) throw new TypeDBDriverException(INVALID_VALUE_CASTING, "datetime"); return LocalDateTime.ofInstant(instantFromNativeDatetime(value_get_datetime(nativeObject)), ZoneOffset.UTC); } @Override public ZonedDateTime asDatetimeTZ() { - if (!isDatetimeTZ()) throw new TypeDBDriverException(ILLEGAL_CAST, "datetime-tz"); + if (!isDatetimeTZ()) throw new TypeDBDriverException(INVALID_VALUE_CASTING, "datetime-tz"); com.typedb.driver.jni.DatetimeAndTimeZone nativeDatetime = value_get_datetime_tz(nativeObject); Instant naiveDatetime = instantFromNativeDatetime(nativeDatetime.getDatetime_in_nanos()); if (nativeDatetime.getIs_fixed_offset()) { @@ -205,13 +205,13 @@ public ZonedDateTime asDatetimeTZ() { @Override public Duration asDuration() { - if (!isDuration()) throw new TypeDBDriverException(ILLEGAL_CAST, "duration"); + if (!isDuration()) throw new TypeDBDriverException(INVALID_VALUE_CASTING, "duration"); return new Duration(value_get_duration(nativeObject)); } @Override public Map> asStruct() { - if (!isStruct()) throw new TypeDBDriverException(ILLEGAL_CAST, "struct"); + if (!isStruct()) throw new TypeDBDriverException(INVALID_VALUE_CASTING, "struct"); return new NativeIterator<>(value_get_struct(nativeObject)).stream().map(fieldAndValue -> { String fieldName = fieldAndValue.getString(); com.typedb.driver.jni.Concept nativeValue = fieldAndValue.getValue(); diff --git a/java/connection/DatabaseManagerImpl.java b/java/connection/DatabaseManagerImpl.java index 8c728fd613..fac8e578fc 100644 --- a/java/connection/DatabaseManagerImpl.java +++ b/java/connection/DatabaseManagerImpl.java @@ -26,7 +26,6 @@ import java.util.List; -import static com.typedb.driver.common.exception.ErrorMessage.Driver.MISSING_DB_NAME; import static com.typedb.driver.jni.typedb_driver.databases_all; import static com.typedb.driver.jni.typedb_driver.databases_contains; import static com.typedb.driver.jni.typedb_driver.databases_create; @@ -42,7 +41,6 @@ public DatabaseManagerImpl(com.typedb.driver.jni.TypeDBDriver driver) { @Override public Database get(String name) throws Error { - if (name == null || name.isEmpty()) throw new TypeDBDriverException(MISSING_DB_NAME); try { return new DatabaseImpl(databases_get(nativeDriver, name)); } catch (com.typedb.driver.jni.Error e) { @@ -52,7 +50,6 @@ public Database get(String name) throws Error { @Override public boolean contains(String name) throws Error { - if (name == null || name.isEmpty()) throw new TypeDBDriverException(MISSING_DB_NAME); try { return databases_contains(nativeDriver, name); } catch (com.typedb.driver.jni.Error e) { @@ -62,7 +59,6 @@ public boolean contains(String name) throws Error { @Override public void create(String name) throws Error { - if (name == null || name.isEmpty()) throw new TypeDBDriverException(MISSING_DB_NAME); try { databases_create(nativeDriver, name); } catch (com.typedb.driver.jni.Error e) { diff --git a/java/core_example b/java/core_example new file mode 100644 index 0000000000..0e0a35042b --- /dev/null +++ b/java/core_example @@ -0,0 +1,183 @@ +// This file is automatically generated. +// It is not intended for manual editing. +import com.typedb.driver.TypeDB; +import com.typedb.driver.api.Driver; +import com.typedb.driver.api.QueryType; +import com.typedb.driver.api.Transaction; +import com.typedb.driver.api.answer.ConceptRow; +import com.typedb.driver.api.answer.ConceptRowIterator; +import com.typedb.driver.api.answer.QueryAnswer; +import com.typedb.driver.api.concept.Concept; +import com.typedb.driver.api.concept.type.AttributeType; +import com.typedb.driver.api.concept.type.EntityType; +import com.typedb.driver.api.database.Database; +import com.typedb.driver.common.Promise; +import com.typedb.driver.common.exception.TypeDBDriverException; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +public class TypeDBExample { + public void example() { + // Open a driver connection. Try-with-resources can be used for automatic driver connection management + try (Driver driver = TypeDB.coreDriver(TypeDB.DEFAULT_ADDRESS)) { + // Create a database + driver.databases().create("typedb"); + Database database = driver.databases().get("typedb"); + + // Open transactions of 3 types + Transaction tx = driver.transaction(database.name(), Transaction.Type.READ); + + // Use "try" blocks to catch driver exceptions + try { + // Execute any TypeDB query using TypeQL. Wrong queries are rejected with an explicit exception + Promise promise = tx.query("define entity i-cannot-be-defined-in-read-transactions;"); + + System.out.println("The result is still promised, so it needs resolving even in case of errors!"); + promise.resolve(); + } catch (TypeDBDriverException expectedException) { + System.out.println("Once the query's promise is resolved, the exception is revealed: " + expectedException); + } finally { + // Don't forget to close the transaction! + tx.close(); + } + + // Open a schema transaction to make schema changes + // Use try-with-resources blocks to forget about "close" operations (similarly to connections) + try (Transaction transaction = driver.transaction(database.name(), Transaction.Type.SCHEMA)) { + String defineQuery = "define " + + "entity person, owns name, owns age; " + + "attribute name, value string;\n" + + "attribute age, value long;"; + + QueryAnswer answer = transaction.query(defineQuery).resolve(); + + // Commit automatically closes the transaction. It can still be safely called inside "try" blocks + transaction.commit(); + } + + + // Open a read transaction to safely read anything without database modifications + try (Transaction transaction = driver.transaction(database.name(), Transaction.Type.READ)) { + QueryAnswer entityAnswer = transaction.query("match entity $x;").resolve(); + + // Collect concept rows that represent the answer as a table + List entityRows = entityAnswer.asConceptRows().stream().collect(Collectors.toList()); + ConceptRow entityRow = entityRows.get(0); + + // Collect column names to get concepts by index if the variable names are lost + List entityHeader = entityRow.columnNames().collect(Collectors.toList()); + + String columnName = entityHeader.get(0); + + // Get concept by the variable name (column name) + Concept conceptByName = entityRow.get(columnName); + + // Get concept by the header's index + Concept conceptByIndex = entityRow.getIndex(0); + + // Check if it's an entity type before the conversion + if (conceptByName.isEntityType()) { + System.out.printf("Getting concepts by variable names and indexes is equally correct. " + + "Both represent the defined entity type: '%s' (in case of a doubt: '%s')%n", + conceptByName.asEntityType().getLabel(), + conceptByIndex.asEntityType().getLabel()); + } + + // Continue querying in the same transaction if needed + QueryAnswer attributeAnswer = transaction.query("match attribute $a;").resolve(); + + // ConceptRowIterator can be used as any other Iterator + ConceptRowIterator attributeRowIterator = attributeAnswer.asConceptRows(); + + while (attributeRowIterator.hasNext()) { + ConceptRow attributeRow = attributeRowIterator.next(); + + // Column names are a stream, so they can be used in a similar way + Iterator columnNameIterator = attributeRow.columnNames().iterator(); + columnName = columnNameIterator.next(); + + conceptByName = attributeRow.get(columnName); + + // Check if it's an attribute type before the conversion + if (conceptByName.isAttributeType()) { + AttributeType attributeType = conceptByName.asAttributeType(); + System.out.printf("Defined attribute type's label: '%s', value type: '%s'%n", attributeType.getLabel(), attributeType.getValueType()); + + System.out.printf("It is also possible to just print the concept itself: '%s'%n", conceptByName); + } + } + } + + // Open a write transaction to insert data + try (Transaction transaction = driver.transaction(database.name(), Transaction.Type.WRITE)) { + String insertQuery = "insert $z isa person, has age 10; $x isa person, has age 20, has name \"John\";"; + QueryAnswer answer = transaction.query(insertQuery).resolve(); + + // Insert queries also return concept rows + List rows = answer.asConceptRows().stream().collect(Collectors.toList()); + ConceptRow row = rows.get(0); + row.columnNames().iterator().forEachRemaining(columnName -> { + Concept insertedConcept = row.get(columnName); + System.out.printf("Successfully inserted $%s: %s%n", columnName, insertedConcept); + if (insertedConcept.isEntity()) { + System.out.println("This time, it's an entity, not a type!"); + } + }); + + // It is possible to ask for the column names again + List header = row.columnNames().collect(Collectors.toList()); + + Concept x = row.getIndex(header.indexOf("x")); + if (x.isEntity()) { + System.out.println("Each entity receives a unique IID. It can be retrieved directly: " + x.asEntity().getIID()); + } + + // Do not forget to commit if the changes should be persisted + transaction.commit(); + } + + // Open a read transaction to verify that the inserted data is saved + try (Transaction transaction = driver.transaction(database.name(), Transaction.Type.READ)) { + // A match query can be used for concept row outputs + String var = "x"; + QueryAnswer matchAnswer = transaction.query(String.format("match $%s isa person;", var)).resolve(); + + // Simple match queries always return concept rows + AtomicInteger matchCount = new AtomicInteger(0); + matchAnswer.asConceptRows().stream().forEach(row -> { + Concept x = row.get(var); + EntityType xType = x.asEntity().getType().asEntityType(); + matchCount.incrementAndGet(); + System.out.printf("Found a person %s of type %s%n", x, xType); + }); + System.out.println("Total persons found: " + matchCount.get()); + + // A fetch query can be used for concept document outputs with flexible structure + QueryAnswer fetchAnswer = transaction.query("match" + + " $x isa! person, has $a;" + + " $a isa! $t;" + + "fetch {" + + " \"single attribute type\": $t," + + " \"single attribute\": $a," + + " \"all attributes\": { $x.* }," + + "};").resolve(); + + // Fetch queries always return concept documents + AtomicInteger fetchCount = new AtomicInteger(0); + fetchAnswer.asConceptDocuments().stream().forEach(document -> { + System.out.println("Fetched a document: " + document); + System.out.print("This document contains an attribute of type: "); + System.out.println(document.asObject().get("single attribute type").asObject().get("label")); + + fetchCount.incrementAndGet(); + }); + System.out.println("Total documents fetched: " + fetchCount.get()); + } + } + + System.out.println("More examples can be found in the API reference and the documentation.\nWelcome to TypeDB!"); + } +} diff --git a/java/docs_structure.bzl b/java/docs_structure.bzl index b6124bc1be..0b7655a348 100644 --- a/java/docs_structure.bzl +++ b/java/docs_structure.bzl @@ -18,7 +18,6 @@ dir_mapping = { "ConceptRow.adoc": "answer", "ConceptRowIterator.adoc": "answer", - "ConceptDocument.adoc": "answer", "ConceptDocumentIterator.adoc": "answer", "JSON.adoc": "answer", "OkQueryAnswer.adoc": "answer", @@ -26,6 +25,7 @@ dir_mapping = { "QueryType.adoc": "answer", "ValueGroup.adoc": "answer", "Promise_T_.adoc": "answer", + "Concept.adoc": "concept", "TypeDB.adoc": "connection", "Driver.adoc": "connection", # "TypeDBCredential.adoc": "connection", @@ -34,6 +34,11 @@ dir_mapping = { # "Database.Replica.adoc": "connection", "Database.adoc": "connection", "DatabaseManager.adoc": "connection", + "Relation.adoc": "data", + "Entity.adoc": "data", + "Attribute.adoc": "data", + "Instance.adoc": "data", + "Value.adoc": "data", "Value.Type.adoc": "schema", "RelationType.adoc": "schema", "AttributeType.adoc": "schema", @@ -42,14 +47,7 @@ dir_mapping = { "Type.adoc": "schema", "Transaction.adoc": "transaction", "Transaction.Type.adoc": "transaction", - "QueryManager.adoc": "transaction", # "Options.adoc": "transaction", - "Concept.adoc": "concept", - "ConceptManager.adoc": "concept", - "Relation.adoc": "data", - "Entity.adoc": "data", - "Attribute.adoc": "data", - "Instance.adoc": "data", - "Value.adoc": "data", + "Duration.adoc": "value", "TypeDBDriverException.adoc": "errors", } diff --git a/java/test/behaviour/config/Parameters.java b/java/test/behaviour/config/Parameters.java index f0c1c61d3d..3a5730b30d 100644 --- a/java/test/behaviour/config/Parameters.java +++ b/java/test/behaviour/config/Parameters.java @@ -19,6 +19,7 @@ package com.typedb.driver.test.behaviour.config; +import com.typedb.driver.api.QueryType; import com.typedb.driver.api.Transaction; import io.cucumber.java.DataTableType; import io.cucumber.java.ParameterType; @@ -26,12 +27,16 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import static com.typedb.driver.api.Transaction.Type.READ; import static com.typedb.driver.api.Transaction.Type.SCHEMA; import static com.typedb.driver.api.Transaction.Type.WRITE; import static com.typedb.driver.test.behaviour.util.Util.assertThrows; +import static com.typedb.driver.test.behaviour.util.Util.assertThrowsWithMessage; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; public class Parameters { @@ -42,27 +47,27 @@ public Boolean bool(String bool) { } @ParameterType("[0-9]+") - public Integer number(String number) { + public Integer integer(String number) { return Integer.parseInt(number); } + @ParameterType("[^;]+") + public String non_semicolon(String non_semicolon) { + return non_semicolon; + } + @ParameterType("\\d\\d\\d\\d-\\d\\d-\\d\\d \\d\\d:\\d\\d:\\d\\d") public LocalDateTime datetime(String dateTime) { DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); return LocalDateTime.parse(dateTime, formatter); } - @ParameterType("entity|attribute|relation|instance") - public Kind kind(String type) { - return Kind.of(type); - } - - @ParameterType("[a-zA-Z0-9-_]+") - public String type_label(String typeLabel) { - return typeLabel; + @ParameterType("concept|variable|type|instance|entity type|relation type|attribute type|role type|entity|relation|attribute|value") + public ConceptKind concept_kind(String type) { + return ConceptKind.of(type); } - @ParameterType("\\$([a-zA-Z0-9]+)") + @ParameterType("([a-zA-Z0-9]*)") public String var(String variable) { return variable; } @@ -79,6 +84,18 @@ public Transaction.Type transaction_type(String type) { return null; } + @ParameterType("read|write|schema") + public QueryType query_type(String type) { + if (type.equals("read")) { + return QueryType.READ; + } else if (type.equals("write")) { + return QueryType.WRITE; + } else if (type.equals("schema")) { + return QueryType.SCHEMA; + } + return null; + } + @DataTableType public List transaction_types(List values) { List typeList = new ArrayList<>(); @@ -91,57 +108,246 @@ public List transaction_types(List values) { return typeList; } - @ParameterType("; fails|; parsing fails|") - public MayError may_error(String result) { - if (result.equals("")) { - return MayError.FALSE; - } else if (result.equals("; fails") || result.equals("; parsing fails")) { - return MayError.TRUE; + @ParameterType("ok|concept rows|concept documents") + public QueryAnswerType query_answer_type(String value) { + return QueryAnswerType.of(value); + } + + + @ParameterType("boolean|long|double|decimal|string|date|datetime|datetime-tz|duration|struct") + public ValueType value_type(String value) { + return ValueType.of(value); + } + + @ParameterType("|; fails|; parsing fails|; fails with a message containing: \".*\"") + public MayError may_error(String value) { + if (value.equals("")) { + return new MayError(false); + } else if (value.equals("; fails") || value.equals("; parsing fails")) { + return new MayError(true); + } else if (value.startsWith("; fails with a message containing:")) { + String pattern = value.substring("; fails with a message containing: ".length()).replaceAll("^\"|\"$", ""); + return new MayError(true, pattern); } return null; } - public enum Kind { + @ParameterType("is|is not") + public IsOrNot is_or_not(String value) { + if (value.equals("is")) { + return IsOrNot.IS; + } else if (value.equals("is not")) { + return IsOrNot.IS_NOT; + } + return null; + } + + @ParameterType("contains|does not contain") + public ContainsOrDoesnt contains_or_doesnt(String value) { + if (value.equals("contains")) { + return ContainsOrDoesnt.DOES; + } else if (value.equals("does not contain")) { + return ContainsOrDoesnt.DOES_NOT; + } + return null; + } + + @ParameterType("| by index of variable") + public IsByVarIndex by_index_of_var(String value) { + if (value.equals(" by index of variable")) { + return IsByVarIndex.IS; + } else if (value.equals("")) { + return IsByVarIndex.IS_NOT; + } + return null; + } + + public enum ConceptKind { + CONCEPT("concept"), + TYPE("type"), + INSTANCE("instance"), + ENTITY_TYPE("entity type"), + RELATION_TYPE("relation type"), + ATTRIBUTE_TYPE("attribute type"), + ROLE_TYPE("role type"), ENTITY("entity"), + RELATION("relation"), ATTRIBUTE("attribute"), - RELATION("relation"); + VALUE("value"); - private final String label; + private final String name; - Kind(String label) { - this.label = label; + ConceptKind(String name) { + this.name = name; } - public static Kind of(String label) { - for (Kind t : Kind.values()) { - if (t.label.equals(label)) { + public static ConceptKind of(String name) { + if (name.equals("variable")) { + return ConceptKind.CONCEPT; + } + for (ConceptKind t : ConceptKind.values()) { + if (t.name.equals(name)) { return t; } } return null; } - public String label() { - return label; + public String toString() { + return name; + } + } + + public enum QueryAnswerType { + OK("ok"), + CONCEPT_ROWS("concept rows"), + CONCEPT_DOCUMENTS("concept documents"); + + private final String name; + + QueryAnswerType(String name) { + this.name = name; + } + + public static QueryAnswerType of(String name) { + for (QueryAnswerType type : QueryAnswerType.values()) { + if (type.name.equals(name)) { + return type; + } + } + return null; + } + + public String toString() { + return name; + } + } + + public enum ValueType { + BOOLEAN("boolean"), + LONG("long"), + DOUBLE("double"), + DECIMAL("decimal"), + STRING("string"), + DATE("date"), + DATETIME("datetime"), + DATETIME_TZ("datetime-tz"), + DURATION("duration"), + STRUCT("struct"); + + private final String name; + + ValueType(String name) { + this.name = name; + } + + public static ValueType of(String name) { + for (ValueType v : ValueType.values()) { + if (v.name.equals(name)) { + return v; + } + } + return null; + } + + public String toString() { + return name; } } - public enum MayError { - TRUE(true), - FALSE(false); + public class MayError { + final boolean mayError; + final String message; - boolean mayError; + public MayError(boolean mayError) { + this(mayError, ""); + } - MayError(boolean mayError) { + public MayError(boolean mayError, String message) { this.mayError = mayError; + this.message = message; } public void check(Runnable function) { if (mayError) { - assertThrows(function); + if (message.isEmpty()) { + assertThrows(function); + } else { + assertThrowsWithMessage(function, message); + } } else { function.run(); } } } + + public enum IsOrNot { + IS(true), + IS_NOT(false); + + private final boolean is; + + IsOrNot(boolean is) { + this.is = is; + } + + public boolean toBoolean() { + return is; + } + + public void compare(Object lhs, Object rhs) { + if (is) assertEquals(lhs, rhs); + else assertNotEquals(lhs, rhs); + } + + public void check(boolean toCheck) { + assertEquals(is, toCheck); + } + } + + public enum ContainsOrDoesnt { + DOES(true), + DOES_NOT(false); + + private final boolean does; + + ContainsOrDoesnt(boolean is) { + this.does = is; + } + + public boolean toBoolean() { + return does; + } + + public void check(boolean toCheck) { + assertEquals(does, toCheck); + } + } + + public enum IsByVarIndex { + IS(true), + IS_NOT(false); + + private final boolean is; + + IsByVarIndex(boolean is) { + this.is = is; + } + + public boolean toBoolean() { + return is; + } + + public void check(boolean toCheck) { + assertEquals(is, toCheck); + } + } + + public static final List DATETIME_TZ_FORMATTERS = Arrays.asList( + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSZ"), + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ"), + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss VV"), + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSS VV"), + DateTimeFormatter.ISO_ZONED_DATE_TIME + ); } diff --git a/java/test/behaviour/connection/BUILD b/java/test/behaviour/connection/BUILD index 147bab8e57..ad0fa52faf 100644 --- a/java/test/behaviour/connection/BUILD +++ b/java/test/behaviour/connection/BUILD @@ -24,6 +24,7 @@ java_library( srcs = ["ConnectionStepsBase.java"], deps = [ # Package dependencies + "//java:driver-java", "//java/api", # External dependencies from Maven @@ -41,6 +42,7 @@ java_library( deps = [ # Package dependencies ":steps-base", + "//java/test/behaviour/config:parameters", "//java:driver-java", "//java/api", # "@maven//:com_vaticle_typedb_typedb_runner", diff --git a/java/test/behaviour/connection/ConnectionStepsBase.java b/java/test/behaviour/connection/ConnectionStepsBase.java index d42ca4ffe7..4b3943f2f5 100644 --- a/java/test/behaviour/connection/ConnectionStepsBase.java +++ b/java/test/behaviour/connection/ConnectionStepsBase.java @@ -19,8 +19,7 @@ package com.typedb.driver.test.behaviour.connection; -//import com.typedb.core.tool.runner.TypeDBSingleton; - +import com.typedb.driver.TypeDB; import com.typedb.driver.api.Driver; import com.typedb.driver.api.Transaction; import com.typedb.driver.api.database.Database; @@ -29,13 +28,13 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.stream.Stream; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; public abstract class ConnectionStepsBase { public static final Map serverOptions = Collections.emptyMap(); @@ -55,10 +54,17 @@ public static Transaction tx() { return transactions.get(0); } - void beforeAll() { -// TypeDBSingleton.deleteTypeDBRunner(); + public static Transaction txPop() { + return transactions.remove(0); + } + + public static Optional txOpt() { + return transactions.isEmpty() ? Optional.empty() : Optional.of(transactions.get(0)); } + void beforeAll() { + } // Can add "before all" setup steps here + void before() { if (!isBeforeAllRan) { try { @@ -67,11 +73,17 @@ void before() { isBeforeAllRan = true; } } - - System.out.println("ConnectionSteps.before"); } void after() { + cleanupTransactions(); + + driver = createTypeDBDriver(TypeDB.DEFAULT_ADDRESS); + driver.databases().all().forEach(Database::delete); + driver.close(); + } + + void cleanupTransactions() { transactions.parallelStream().forEach(Transaction::close); transactions.clear(); @@ -82,12 +94,6 @@ void after() { })); CompletableFuture.allOf(closures.toArray(CompletableFuture[]::new)).join(); transactionsParallel.clear(); -// driver = createTypeDBDriver(TypeDBSingleton.getTypeDBRunner().address()); - driver = createTypeDBDriver("127.0.0.1:1729"); - driver.databases().all().forEach(Database::delete); - driver.close(); - - System.out.println("ConnectionSteps.after"); } abstract Driver createTypeDBDriver(String address); @@ -96,19 +102,16 @@ void after() { abstract void connection_opens_with_default_authentication(); - void driver_closes() { + void connection_closes() { + cleanupTransactions(); driver.close(); - driver = null; } - void connection_has_been_opened() { - assertNotNull(driver); - assertTrue(driver.isOpen()); + void connection_is_open(boolean isOpen) { + assertEquals(isOpen, driver != null && driver.isOpen()); } - void connection_does_not_have_any_database() { - assertNotNull(driver); - assertTrue(driver.isOpen()); - assertTrue(driver.databases().all().isEmpty()); + void connection_has_count_databases(int count) { + assertEquals(count, driver.databases().all().size()); } } diff --git a/java/test/behaviour/connection/ConnectionStepsCloud.java b/java/test/behaviour/connection/ConnectionStepsCloud.java index b443a5614d..a6bcb28c14 100644 --- a/java/test/behaviour/connection/ConnectionStepsCloud.java +++ b/java/test/behaviour/connection/ConnectionStepsCloud.java @@ -113,15 +113,15 @@ // } // // @Override -// @Given("connection does not have any database") -// public void connection_does_not_have_any_database() { -// super.connection_does_not_have_any_database(); +// @Given("connection has {integer} database(s)") +// public void connection_has_count_databases() { +// super.connection_has_count_databases(); // } // // @Override // @When("connection closes") -// public void driver_closes() { -// super.driver_closes(); +// public void connection_closes() { +// super.connection_closes(); // } // // @Given("typedb has configuration") diff --git a/java/test/behaviour/connection/ConnectionStepsCore.java b/java/test/behaviour/connection/ConnectionStepsCore.java index 18b03e1a76..9852399533 100644 --- a/java/test/behaviour/connection/ConnectionStepsCore.java +++ b/java/test/behaviour/connection/ConnectionStepsCore.java @@ -19,12 +19,9 @@ package com.typedb.driver.test.behaviour.connection; -//import com.typedb.core.tool.runner.TypeDBRunner; -//import com.typedb.core.tool.runner.TypeDBSingleton; -//import com.typedb.core.tool.runner.TypeDBCoreRunner; - import com.typedb.driver.TypeDB; import com.typedb.driver.api.Driver; +import com.typedb.driver.test.behaviour.config.Parameters; import io.cucumber.java.After; import io.cucumber.java.Before; import io.cucumber.java.en.Given; @@ -34,13 +31,6 @@ public class ConnectionStepsCore extends ConnectionStepsBase { @Override public void beforeAll() { super.beforeAll(); -// try { -// TypeDBCoreRunner typeDBCoreRunner = new TypeDBCoreRunner(serverOptions); -// TypeDBSingleton.setTypeDBRunner(typeDBCoreRunner); -// typeDBCoreRunner.start(); -// } catch (InterruptedException | java.util.concurrent.TimeoutException | java.io.IOException e) { -// e.printStackTrace(); -// } } @Before @@ -65,35 +55,39 @@ Driver createTypeDBDriver(String address) { @When("typedb starts") public void typedb_starts() { -// TypeDBRunner runner = TypeDBSingleton.getTypeDBRunner(); -// if (runner != null && runner.isStopped()) { -// runner.start(); -// } } @Override @When("connection opens with default authentication") public void connection_opens_with_default_authentication() { -// driver = createTypeDBDriver(TypeDBSingleton.getTypeDBRunner().address()); - driver = createTypeDBDriver("127.0.0.1:1729"); + driver = createTypeDBDriver(TypeDB.DEFAULT_ADDRESS); + } + + @When("connection opens with a wrong host{may_error}") + public void connection_opens_with_a_wrong_host(Parameters.MayError mayError) { + mayError.check(() -> driver = createTypeDBDriver(TypeDB.DEFAULT_ADDRESS.replace("localhost", "surely-not-localhost"))); + } + + @When("connection opens with a wrong port{may_error}") + public void connection_opens_with_a_wrong_port(Parameters.MayError mayError) { + mayError.check(() -> driver = createTypeDBDriver(TypeDB.DEFAULT_ADDRESS.replace("localhost", "surely-not-localhost"))); } @Override @When("connection closes") - public void driver_closes() { - super.driver_closes(); + public void connection_closes() { + super.connection_closes(); } @Override - @Given("connection has been opened") - public void connection_has_been_opened() { - super.connection_has_been_opened(); + @Given("connection is open: {bool}") + public void connection_is_open(boolean isOpen) { + super.connection_is_open(isOpen); } @Override - @Given("connection does not have any database") - public void connection_does_not_have_any_database() { - super.connection_does_not_have_any_database(); + @Given("connection has {integer} database(s)") + public void connection_has_count_databases(int count) { + super.connection_has_count_databases(count); } - } diff --git a/java/test/behaviour/connection/database/BUILD b/java/test/behaviour/connection/database/BUILD index cff63f0ce0..d6a480138a 100644 --- a/java/test/behaviour/connection/database/BUILD +++ b/java/test/behaviour/connection/database/BUILD @@ -30,6 +30,7 @@ java_library( # Internal Package Dependencies "//java/api:api", "//java/common:common", + "//java/test/behaviour/config:parameters", "//java/test/behaviour/connection:steps-base", # External Maven Dependencies diff --git a/java/test/behaviour/connection/database/DatabaseSteps.java b/java/test/behaviour/connection/database/DatabaseSteps.java index 57e427b0fc..4f1033c740 100644 --- a/java/test/behaviour/connection/database/DatabaseSteps.java +++ b/java/test/behaviour/connection/database/DatabaseSteps.java @@ -20,6 +20,7 @@ package com.typedb.driver.test.behaviour.connection.database; import com.typedb.driver.api.database.Database; +import com.typedb.driver.test.behaviour.config.Parameters; import io.cucumber.java.en.Then; import io.cucumber.java.en.When; @@ -39,9 +40,14 @@ import static org.junit.Assert.fail; public class DatabaseSteps { - @When("connection create database: {word}") - public void connection_create_database(String name) { - connection_create_databases(list(name)); + @When("connection create database: {non_semicolon}{may_error}") + public void connection_create_database(String name, Parameters.MayError mayError) { + mayError.check(() -> connection_create_databases(list(name))); + } + + @When("connection create database with empty name{may_error}") + public void connection_create_database_with_empty_name(Parameters.MayError mayError) { + mayError.check(() -> connection_create_databases(list(""))); } @When("connection create database(s):") @@ -62,9 +68,9 @@ public void connection_create_databases_in_parallel(List names) { CompletableFuture.allOf(creations).join(); } - @When("connection delete database: {word}") - public void connection_delete_database(String name) { - connection_delete_databases(list(name)); + @When("connection delete database: {word}{may_error}") + public void connection_delete_database(String name, Parameters.MayError mayError) { + mayError.check(() -> connection_delete_databases(list(name))); } @When("connection delete database(s):") diff --git a/java/test/behaviour/connection/transaction/TransactionSteps.java b/java/test/behaviour/connection/transaction/TransactionSteps.java index 77c5bffa72..617f3c7806 100644 --- a/java/test/behaviour/connection/transaction/TransactionSteps.java +++ b/java/test/behaviour/connection/transaction/TransactionSteps.java @@ -25,119 +25,131 @@ import io.cucumber.java.en.When; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; +import java.util.Optional; import java.util.concurrent.CompletableFuture; -import java.util.function.Consumer; import static com.typedb.driver.test.behaviour.connection.ConnectionStepsBase.THREAD_POOL_SIZE; import static com.typedb.driver.test.behaviour.connection.ConnectionStepsBase.driver; +import static com.typedb.driver.test.behaviour.connection.ConnectionStepsBase.threadPool; import static com.typedb.driver.test.behaviour.connection.ConnectionStepsBase.transactions; +import static com.typedb.driver.test.behaviour.connection.ConnectionStepsBase.transactionsParallel; import static com.typedb.driver.test.behaviour.connection.ConnectionStepsBase.tx; -import static java.util.Objects.isNull; +import static com.typedb.driver.test.behaviour.connection.ConnectionStepsBase.txOpt; +import static com.typedb.driver.test.behaviour.connection.ConnectionStepsBase.txPop; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @SuppressWarnings("CheckReturnValue") public class TransactionSteps { - // ======================= // - // sequential transactions // - // ======================= // + void assertTransactionIsOpen(Optional transaction, boolean isOpen) { + assertEquals(isOpen, transaction.isPresent() && transaction.get().isOpen()); + } + + void assertTransactionHasType(Transaction transaction, Transaction.Type type) { + assertEquals(type, transaction.getType()); + } - @When("connection open {transaction_type} transaction for database: {string}{may_error}") + @When("connection open {transaction_type} transaction for database: {non_semicolon}{may_error}") public void connection_open_transaction_for_database(Transaction.Type type, String databaseName, Parameters.MayError mayError) { mayError.check(() -> { - Transaction transaction = driver.transaction(databaseName, type/*, transactionOptions*/); + Transaction transaction = driver.transaction(databaseName, type); transactions.add(transaction); }); } + @When("connection open transaction(s) for database: {non_semicolon}, of type:") + public void connection_open_transactions_for_database_of_type(String databaseName, List types) { + for (Transaction.Type type : types) { + Transaction transaction = driver.transaction(databaseName, type); + transactions.add(transaction); + } + } + @Then("transaction is open: {bool}") public void transaction_is_open(boolean isOpen) { - assertEquals(isOpen, tx().isOpen()); + assertTransactionIsOpen(txOpt(), isOpen); + } + + @Then("transactions are open: {bool}") + public void transactions_are_open(boolean isOpen) { + for (Transaction transaction : transactions) { + assertTransactionIsOpen(Optional.of(transaction), isOpen); + } } @Then("transaction has type: {transaction_type}") public void transaction_has_type(Transaction.Type type) { - assertEquals(type, tx().getType()); + assertTransactionHasType(tx(), type); + } + + @Then("transactions have type:") + public void transactions_have_type(List types) { + Iterator typeIterator = types.iterator(); + for (Transaction transaction : transactions) { + assertTrue("types list is shorter than saved transactions", typeIterator.hasNext()); + assertTransactionHasType(transaction, typeIterator.next()); + } + assertFalse("types list is longer than saved transactions", typeIterator.hasNext()); } @Then("transaction commits{may_error}") public void transaction_commits(Parameters.MayError mayError) { - mayError.check(() -> tx().commit()); + mayError.check(() -> txPop().commit()); } @Then("transaction closes{may_error}") public void transaction_closes(Parameters.MayError mayError) { - mayError.check(() -> tx().close()); + mayError.check(() -> txPop().close()); } - // ===================== // - // parallel transactions // - // ===================== // + @Then("transaction rollbacks{may_error}") + public void transaction_rollbacks(Parameters.MayError mayError) { + mayError.check(() -> txPop().rollback()); + } - @When("open transactions in parallel of type:") - public void open_transactions_in_parallel_of_type(List types) { + @When("connection open transactions in parallel for database: {non_semicolon}, of type:") + public void open_transactions_in_parallel_of_type(String name, List types) { assertTrue(THREAD_POOL_SIZE >= types.size()); - List> transactionsParallel = new ArrayList<>(); -// for (Transaction.Type type : types) { -// transactionsParallel.add(CompletableFuture.supplyAsync(() -> session.transaction(type), threadPool)); -// } + for (Transaction.Type type : types) { + transactionsParallel.add(CompletableFuture.supplyAsync(() -> driver.transaction(name, type), threadPool)); + } } - @Then("for each session, transactions in parallel are null: {bool}") - public void for_each_session_transactions_in_parallel_are_null(boolean isNull) { - for_each_session_transactions_in_parallel_are(transaction -> assertEquals(isNull, isNull(transaction))); - } + @Then("transactions in parallel are open: {bool}") + public void transactions_in_parallel_are_open(boolean isOpen) { + List> assertions = new ArrayList<>(); - @Then("for each session, transactions in parallel are open: {bool}") - public void for_each_session_transactions_in_parallel_are_open(boolean isOpen) { - for_each_session_transactions_in_parallel_are(transaction -> assertEquals(isOpen, transaction.isOpen())); - } + for (CompletableFuture futureTransaction : transactionsParallel) { + assertions.add(futureTransaction.thenApply(transaction -> { + assertTransactionIsOpen(Optional.of(transaction), isOpen); + return null; + })); + } - private void for_each_session_transactions_in_parallel_are(Consumer assertion) { - List> assertions = new ArrayList<>(); -// for (TypeDBSession session : sessions) { -// for (CompletableFuture futureTransaction : -// sessionsToTransactionsParallel.get(session)) { -// -// assertions.add(futureTransaction.thenApply(transaction -> { -// assertion.accept(transaction); -// return null; -// })); -// } -// } CompletableFuture.allOf(assertions.toArray(new CompletableFuture[0])).join(); } - @Then("for each session, transactions in parallel have type:") - public void for_each_session_transactions_in_parallel_have_type(List types) { + @Then("transactions in parallel have type:") + public void transactions_in_parallel_have_type(List types) { + Iterator typeIterator = types.iterator(); List> assertions = new ArrayList<>(); -// for (TypeDBSession session : sessions) { -// List> futureTxs = -// sessionsToTransactionsParallel.get(session); -// -// assertEquals(types.size(), futureTxs.size()); -// -// Iterator typesIter = types.iterator(); -// Iterator> futureTxsIter = futureTxs.iterator(); -// -// while (typesIter.hasNext()) { -// Transaction.Type type = typesIter.next(); -// futureTxsIter.next().thenApplyAsync(tx -> { -// assertEquals(type, tx.type()); -// return null; -// }); -// } -// } + + for (CompletableFuture futureTransaction : transactionsParallel) { + assertions.add(futureTransaction.thenApply(transaction -> { + assertTrue("types list is shorter than saved transactions", typeIterator.hasNext()); + assertTransactionHasType(transaction, typeIterator.next()); + return null; + })); + } CompletableFuture.allOf(assertions.toArray(new CompletableFuture[0])).join(); + assertFalse("types list is longer than saved transactions", typeIterator.hasNext()); } - // ========================= // - // transaction configuration // - // ========================= // - // @Given("set transaction option {word} to: {word}") // public void set_transaction_option_to(String option, String value) { // if (!optionSetters.containsKey(option)) { diff --git a/java/test/behaviour/connection/user/BUILD b/java/test/behaviour/connection/user/BUILD index df96be2f5c..0deace6324 100644 --- a/java/test/behaviour/connection/user/BUILD +++ b/java/test/behaviour/connection/user/BUILD @@ -20,24 +20,24 @@ package(default_visibility = ["//java/test/behaviour:__subpackages__"]) load("@vaticle_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test") load("@vaticle_dependencies//builder/java:rules.bzl", "typedb_java_test") -java_library( - name = "steps", - srcs = [ - "UserSteps.java", - ], - visibility = ["//visibility:public"], - deps = [ - # Internal Package Dependencies - "//java/api", - "//java/test/behaviour/connection:steps-base", - "//java/test/behaviour/util", - - # External Maven Dependencies - "@maven//:junit_junit", - "@maven//:io_cucumber_cucumber_java", - ], -) - +#java_library( +# name = "steps", +# srcs = [ +# "UserSteps.java", +# ], +# visibility = ["//visibility:public"], +# deps = [ +# # Internal Package Dependencies +# "//java/api", +# "//java/test/behaviour/connection:steps-base", +# "//java/test/behaviour/util", +# +# # External Maven Dependencies +# "@maven//:junit_junit", +# "@maven//:io_cucumber_cucumber_java", +# ], +#) +# #typedb_java_test( # name = "test-cloud", # size = "large", diff --git a/java/test/behaviour/driver/query/BUILD b/java/test/behaviour/driver/driver/BUILD similarity index 97% rename from java/test/behaviour/driver/query/BUILD rename to java/test/behaviour/driver/driver/BUILD index c61944408c..f0771759b4 100644 --- a/java/test/behaviour/driver/query/BUILD +++ b/java/test/behaviour/driver/driver/BUILD @@ -22,9 +22,9 @@ load("//java/test/behaviour:rules.bzl", "typedb_behaviour_java_test") typedb_behaviour_java_test( name = "test", srcs = [ - "QueryTest.java", + "DriverTest.java", ], - test_class = "com.typedb.driver.test.behaviour.driver.query.QueryTest", + test_class = "com.typedb.driver.test.behaviour.driver.query.DriverTest", data = [ "@vaticle_typedb_behaviour//driver:driver.feature", ], diff --git a/java/test/behaviour/driver/query/QueryTest.java b/java/test/behaviour/driver/driver/DriverTest.java similarity index 95% rename from java/test/behaviour/driver/query/QueryTest.java rename to java/test/behaviour/driver/driver/DriverTest.java index 32d96a647f..0d780314e6 100644 --- a/java/test/behaviour/driver/query/QueryTest.java +++ b/java/test/behaviour/driver/driver/DriverTest.java @@ -29,10 +29,10 @@ strict = true, plugin = "pretty", glue = "com.typedb.driver.test.behaviour", - features = "external/vaticle_typedb_behaviour/driver/query.feature", + features = "external/vaticle_typedb_behaviour/driver/driver.feature", tags = "not @ignore and not @ignore-typedb-driver and not @ignore-typedb-driver-java" ) -public class QueryTest extends BehaviourTest { +public class DriverTest extends BehaviourTest { // ATTENTION: // When you click RUN from within this class through Intellij IDE, it will fail. // You can fix it by doing: diff --git a/java/test/behaviour/query/QuerySteps.java b/java/test/behaviour/query/QuerySteps.java index 5771fb7572..ec59a44748 100644 --- a/java/test/behaviour/query/QuerySteps.java +++ b/java/test/behaviour/query/QuerySteps.java @@ -19,308 +19,980 @@ package com.typedb.driver.test.behaviour.query; +import com.typedb.driver.api.QueryType; +import com.typedb.driver.api.answer.ConceptDocumentIterator; import com.typedb.driver.api.answer.ConceptRow; +import com.typedb.driver.api.answer.ConceptRowIterator; import com.typedb.driver.api.answer.JSON; -import com.typedb.driver.api.answer.ValueGroup; +import com.typedb.driver.api.answer.OkQueryAnswer; +import com.typedb.driver.api.answer.QueryAnswer; +import com.typedb.driver.api.concept.Concept; +import com.typedb.driver.api.concept.instance.Attribute; +import com.typedb.driver.api.concept.instance.Entity; +import com.typedb.driver.api.concept.instance.Instance; +import com.typedb.driver.api.concept.instance.Relation; +import com.typedb.driver.api.concept.type.AttributeType; +import com.typedb.driver.api.concept.type.EntityType; +import com.typedb.driver.api.concept.type.RelationType; +import com.typedb.driver.api.concept.type.RoleType; +import com.typedb.driver.api.concept.type.Type; import com.typedb.driver.api.concept.value.Value; +import com.typedb.driver.common.Duration; import com.typedb.driver.test.behaviour.config.Parameters; import io.cucumber.java.en.Given; import io.cucumber.java.en.Then; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; import java.util.ArrayList; import java.util.List; -import java.util.Map; +import java.util.NoSuchElementException; import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; -import static com.typedb.driver.test.behaviour.util.Util.JSONListMatches; +import static com.typedb.driver.api.concept.value.Value.DECIMAL_SCALE; +import static com.typedb.driver.test.behaviour.config.Parameters.DATETIME_TZ_FORMATTERS; +import static com.typedb.driver.test.behaviour.connection.ConnectionStepsBase.threadPool; +import static com.typedb.driver.test.behaviour.connection.ConnectionStepsBase.tx; +import static com.typedb.driver.test.behaviour.util.Util.JSONListContains; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; public class QuerySteps { - private static List answerRows; - private static List fetchAnswers; - private static Optional valueAnswer; - private static List valueAnswerGroups; - private Map> rules; + private static QueryAnswer queryAnswer; + private static List collectedRows; + private static List collectedDocuments; + private static List> queryAnswersParallel = null; - public static List answers() { - return answerRows; + private void clearAnswers() { + queryAnswer = null; + if (queryAnswersParallel != null) { + CompletableFuture.allOf(queryAnswersParallel.toArray(CompletableFuture[]::new)).join(); + queryAnswersParallel = null; + } + + collectedRows = null; + collectedDocuments = null; } - @Given("typeql define{may_error}") - public void typeql_define(String defineQueryStatements, Parameters.MayError mayError) { -// TypeQLDefine typeQLQuery = TypeQL.parseQuery(String.join("\n", defineQueryStatements)); -// mayError.check(() -> tx().query(String.join("\n", defineQueryStatements)).resolve()); + private void collectAnswerIfNeeded() { + if (collectedRows != null || collectedDocuments != null) { + return; + } + + if (queryAnswer.isConceptRows()) { + collectedRows = queryAnswer.asConceptRows().stream().collect(Collectors.toList()); + } else if (queryAnswer.isConceptDocuments()) { + collectedDocuments = queryAnswer.asConceptDocuments().stream().collect(Collectors.toList()); + } else { + throw new AssertionError("Query answer is not collectable"); + } } - private void clearAnswers() { - answerRows = null; - valueAnswer = null; - fetchAnswers = null; - valueAnswerGroups = null; - } - -// @When("get answers of typeql insert") -// public void get_answers_of_typeql_insert(String typeQLQueryStatements) { -// TypeQLInsert typeQLQuery = TypeQL.parseQuery(String.join("\n", typeQLQueryStatements)); -// clearAnswers(); -// answers = tx().query().insert(String.join("\n", typeQLQueryStatements)).collect(Collectors.toList()); -// } - -// @When("get answers of typeql get") -// public void typeql_get(String typeQLQueryStatements) { -// try { -// TypeQLGet typeQLQuery = TypeQL.parseQuery(String.join("\n", typeQLQueryStatements)).asGet(); -// clearAnswers(); -// answers = tx().query().get(String.join("\n", typeQLQueryStatements)).collect(Collectors.toList()); -// } catch (TypeQLException e) { -// // NOTE: We manually close transaction here, because we want to align with all non-java drivers, -// // where parsing happens at server-side which closes transaction if they fail -// tx().close(); -// throw e; -// } -// } - - @Then("answer size is: {number}") - public void answer_quantity_assertion(int expectedAnswers) { - assertEquals(String.format("Expected [%d] answers, but got [%d]", expectedAnswers, answerRows.size()), - expectedAnswers, answerRows.size()); - } - - @Then("uniquely identify answer concepts") - public void uniquely_identify_answer_concepts(List> answerConcepts) { - assertEquals( - String.format("The number of identifier entries (rows) should match the number of answers, but found %d identifier entries and %d answers.", - answerConcepts.size(), answerRows.size()), - answerConcepts.size(), answerRows.size() - ); - - for (ConceptRow row : answerRows) { - List> matchingIdentifiers = new ArrayList<>(); - - for (Map answerIdentifier : answerConcepts) { - - if (matchAnswerConcept(answerIdentifier, row)) { - matchingIdentifiers.add(answerIdentifier); + private void collectRowsAnswerIfNeeded() { + collectAnswerIfNeeded(); + assertNotNull("Expected to collect ConceptRows, but the answer is not ConceptRows", collectedRows); + } + + private void collectDocumentsAnswerIfNeeded() { + collectAnswerIfNeeded(); + assertNotNull("Expected to collect ConceptDocuments, but the answer is not ConceptDocuments", collectedDocuments); + } + + public List getRowGetConcepts(int rowIndex) { + ConceptRow row = collectedRows.get(rowIndex); + return row.concepts().collect(Collectors.toList()); + } + + public Concept getRowGetConcept(int rowIndex, Parameters.IsByVarIndex isByVarIndex, String var) { + ConceptRow row = collectedRows.get(rowIndex); + switch (isByVarIndex) { + case IS: + return row.getIndex(row.columnNames().collect(Collectors.toList()).indexOf(var)); + case IS_NOT: + return row.get(var); + default: + throw new AssertionError("Unexpected isByVarIndex: " + isByVarIndex); + } + } + + public Value getRowGetValue(int rowIndex, Parameters.ConceptKind varKind, Parameters.IsByVarIndex isByVarIndex, String var) { + Concept concept = getRowGetConcept(rowIndex, isByVarIndex, var); + switch (varKind) { + case ATTRIBUTE: + return concept.asAttribute().getValue(); + case VALUE: + return concept.asValue(); + default: + throw new IllegalStateException("ConceptKind does not have values: " + varKind); + } + } + + public boolean isConceptKind(Concept concept, Parameters.ConceptKind checkedKind) { + switch (checkedKind) { + case CONCEPT: + return true; + case TYPE: + return concept.isType(); + case INSTANCE: + return concept.isInstance(); + case ENTITY_TYPE: + return concept.isEntityType(); + case RELATION_TYPE: + return concept.isRelationType(); + case ATTRIBUTE_TYPE: + return concept.isAttributeType(); + case ROLE_TYPE: + return concept.isRoleType(); + case ENTITY: + return concept.isEntity(); + case RELATION: + return concept.isRelation(); + case ATTRIBUTE: + return concept.isAttribute(); + case VALUE: + return concept.isValue(); + default: + throw new AssertionError("Not covered ConceptKind: " + checkedKind); + } + } + + // We want to call the checks on specific interfaces, not "Concept"s, so the boilerplate is needed + public boolean isUnwrappedConceptKind(Concept concept, Parameters.ConceptKind conceptKind, Parameters.ConceptKind checkedKind) { + switch (conceptKind) { + case CONCEPT: + switch (checkedKind) { + case CONCEPT: + return true; + case TYPE: + return concept.isType(); + case INSTANCE: + return concept.isInstance(); + case ENTITY_TYPE: + return concept.isEntityType(); + case RELATION_TYPE: + return concept.isRelationType(); + case ATTRIBUTE_TYPE: + return concept.isAttributeType(); + case ROLE_TYPE: + return concept.isRoleType(); + case ENTITY: + return concept.isEntity(); + case RELATION: + return concept.isRelation(); + case ATTRIBUTE: + return concept.isAttribute(); + case VALUE: + return concept.isValue(); + default: + throw new AssertionError("Not covered ConceptKind: " + checkedKind); + } + case TYPE: + Type type = concept.asType(); + switch (checkedKind) { + case CONCEPT: + return true; + case TYPE: + return type.isType(); + case INSTANCE: + return type.isInstance(); + case ENTITY_TYPE: + return type.isEntityType(); + case RELATION_TYPE: + return type.isRelationType(); + case ATTRIBUTE_TYPE: + return type.isAttributeType(); + case ROLE_TYPE: + return type.isRoleType(); + case ENTITY: + return type.isEntity(); + case RELATION: + return type.isRelation(); + case ATTRIBUTE: + return type.isAttribute(); + case VALUE: + return type.isValue(); + default: + throw new AssertionError("Not covered ConceptKind: " + checkedKind); + } + case INSTANCE: + Instance instance = concept.asInstance(); + switch (checkedKind) { + case CONCEPT: + return true; + case TYPE: + return instance.isType(); + case INSTANCE: + return instance.isInstance(); + case ENTITY_TYPE: + return instance.isEntityType(); + case RELATION_TYPE: + return instance.isRelationType(); + case ATTRIBUTE_TYPE: + return instance.isAttributeType(); + case ROLE_TYPE: + return instance.isRoleType(); + case ENTITY: + return instance.isEntity(); + case RELATION: + return instance.isRelation(); + case ATTRIBUTE: + return instance.isAttribute(); + case VALUE: + return instance.isValue(); + default: + throw new AssertionError("Not covered ConceptKind: " + checkedKind); + } + case ENTITY_TYPE: + EntityType entityType = concept.asEntityType(); + switch (checkedKind) { + case CONCEPT: + return true; + case TYPE: + return entityType.isType(); + case INSTANCE: + return entityType.isInstance(); + case ENTITY_TYPE: + return entityType.isEntityType(); + case RELATION_TYPE: + return entityType.isRelationType(); + case ATTRIBUTE_TYPE: + return entityType.isAttributeType(); + case ROLE_TYPE: + return entityType.isRoleType(); + case ENTITY: + return entityType.isEntity(); + case RELATION: + return entityType.isRelation(); + case ATTRIBUTE: + return entityType.isAttribute(); + case VALUE: + return entityType.isValue(); + default: + throw new AssertionError("Not covered ConceptKind: " + checkedKind); + } + case RELATION_TYPE: + RelationType relationType = concept.asRelationType(); + switch (checkedKind) { + case CONCEPT: + return true; + case TYPE: + return relationType.isType(); + case INSTANCE: + return relationType.isInstance(); + case ENTITY_TYPE: + return relationType.isEntityType(); + case RELATION_TYPE: + return relationType.isRelationType(); + case ATTRIBUTE_TYPE: + return relationType.isAttributeType(); + case ROLE_TYPE: + return relationType.isRoleType(); + case ENTITY: + return relationType.isEntity(); + case RELATION: + return relationType.isRelation(); + case ATTRIBUTE: + return relationType.isAttribute(); + case VALUE: + return relationType.isValue(); + default: + throw new AssertionError("Not covered ConceptKind: " + checkedKind); + } + case ATTRIBUTE_TYPE: + AttributeType attributeType = concept.asAttributeType(); + switch (checkedKind) { + case CONCEPT: + return true; + case TYPE: + return attributeType.isType(); + case INSTANCE: + return attributeType.isInstance(); + case ENTITY_TYPE: + return attributeType.isEntityType(); + case RELATION_TYPE: + return attributeType.isRelationType(); + case ATTRIBUTE_TYPE: + return attributeType.isAttributeType(); + case ROLE_TYPE: + return attributeType.isRoleType(); + case ENTITY: + return attributeType.isEntity(); + case RELATION: + return attributeType.isRelation(); + case ATTRIBUTE: + return attributeType.isAttribute(); + case VALUE: + return attributeType.isValue(); + default: + throw new AssertionError("Not covered ConceptKind: " + checkedKind); + } + case ROLE_TYPE: + RoleType roleType = concept.asRoleType(); + switch (checkedKind) { + case CONCEPT: + return true; + case TYPE: + return roleType.isType(); + case INSTANCE: + return roleType.isInstance(); + case ENTITY_TYPE: + return roleType.isEntityType(); + case RELATION_TYPE: + return roleType.isRelationType(); + case ATTRIBUTE_TYPE: + return roleType.isAttributeType(); + case ROLE_TYPE: + return roleType.isRoleType(); + case ENTITY: + return roleType.isEntity(); + case RELATION: + return roleType.isRelation(); + case ATTRIBUTE: + return roleType.isAttribute(); + case VALUE: + return roleType.isValue(); + default: + throw new AssertionError("Not covered ConceptKind: " + checkedKind); + } + case ENTITY: + Entity entity = concept.asEntity(); + switch (checkedKind) { + case CONCEPT: + return true; + case TYPE: + return entity.isType(); + case INSTANCE: + return entity.isInstance(); + case ENTITY_TYPE: + return entity.isEntityType(); + case RELATION_TYPE: + return entity.isRelationType(); + case ATTRIBUTE_TYPE: + return entity.isAttributeType(); + case ROLE_TYPE: + return entity.isRoleType(); + case ENTITY: + return entity.isEntity(); + case RELATION: + return entity.isRelation(); + case ATTRIBUTE: + return entity.isAttribute(); + case VALUE: + return entity.isValue(); + default: + throw new AssertionError("Not covered ConceptKind: " + checkedKind); + } + case RELATION: + Relation relation = concept.asRelation(); + switch (checkedKind) { + case CONCEPT: + return true; + case TYPE: + return relation.isType(); + case INSTANCE: + return relation.isInstance(); + case ENTITY_TYPE: + return relation.isEntityType(); + case RELATION_TYPE: + return relation.isRelationType(); + case ATTRIBUTE_TYPE: + return relation.isAttributeType(); + case ROLE_TYPE: + return relation.isRoleType(); + case ENTITY: + return relation.isEntity(); + case RELATION: + return relation.isRelation(); + case ATTRIBUTE: + return relation.isAttribute(); + case VALUE: + return relation.isValue(); + default: + throw new AssertionError("Not covered ConceptKind: " + checkedKind); } + case ATTRIBUTE: + Attribute attribute = concept.asAttribute(); + switch (checkedKind) { + case CONCEPT: + return true; + case TYPE: + return attribute.isType(); + case INSTANCE: + return attribute.isInstance(); + case ENTITY_TYPE: + return attribute.isEntityType(); + case RELATION_TYPE: + return attribute.isRelationType(); + case ATTRIBUTE_TYPE: + return attribute.isAttributeType(); + case ROLE_TYPE: + return attribute.isRoleType(); + case ENTITY: + return attribute.isEntity(); + case RELATION: + return attribute.isRelation(); + case ATTRIBUTE: + return attribute.isAttribute(); + case VALUE: + return attribute.isValue(); + default: + throw new AssertionError("Not covered ConceptKind: " + checkedKind); + } + case VALUE: + Value value = concept.asValue(); + switch (checkedKind) { + case CONCEPT: + return true; + case TYPE: + return value.isType(); + case INSTANCE: + return value.isInstance(); + case ENTITY_TYPE: + return value.isEntityType(); + case RELATION_TYPE: + return value.isRelationType(); + case ATTRIBUTE_TYPE: + return value.isAttributeType(); + case ROLE_TYPE: + return value.isRoleType(); + case ENTITY: + return value.isEntity(); + case RELATION: + return value.isRelation(); + case ATTRIBUTE: + return value.isAttribute(); + case VALUE: + return value.isValue(); + default: + throw new AssertionError("Not covered ConceptKind: " + checkedKind); + } + default: + throw new AssertionError("Not covered ConceptKind: " + conceptKind); + } + } + + public void unwrapConceptAs(Concept concept, Parameters.ConceptKind conceptKind) { + switch (conceptKind) { + case CONCEPT: + break; + case TYPE: + assertNotNull(concept.asType()); + break; + case INSTANCE: + assertNotNull(concept.asInstance()); + break; + case ENTITY_TYPE: + assertNotNull(concept.asEntityType()); + break; + case RELATION_TYPE: + assertNotNull(concept.asRelationType()); + break; + case ATTRIBUTE_TYPE: + assertNotNull(concept.asAttributeType()); + break; + case ROLE_TYPE: + assertNotNull(concept.asRoleType()); + break; + case ENTITY: + assertNotNull(concept.asEntity()); + break; + case RELATION: + assertNotNull(concept.asRelation()); + break; + case ATTRIBUTE: + assertNotNull(concept.asAttribute()); + break; + case VALUE: + assertNotNull(concept.asValue()); + break; + default: + throw new AssertionError("Not covered ConceptKind: " + conceptKind); + } + } + + public String getLabelOfUnwrappedConcept(Concept concept, Parameters.ConceptKind conceptKind) { + switch (conceptKind) { + case CONCEPT: + return concept.getLabel(); + case TYPE: + return concept.asType().getLabel(); + case INSTANCE: + return concept.asInstance().getLabel(); + case ENTITY_TYPE: + return concept.asEntityType().getLabel(); + case RELATION_TYPE: + return concept.asRelationType().getLabel(); + case ATTRIBUTE_TYPE: + return concept.asAttributeType().getLabel(); + case ROLE_TYPE: + return concept.asRoleType().getLabel(); + case ENTITY: + return concept.asEntity().getLabel(); + case RELATION: + return concept.asRelation().getLabel(); + case ATTRIBUTE: + return concept.asAttribute().getLabel(); + case VALUE: + return concept.asValue().getLabel(); + default: + throw new AssertionError("Not covered ConceptKind: " + conceptKind); + } + } + + public String getLabelOfUnwrappedConceptsType(Concept concept, Parameters.ConceptKind conceptKind) { + switch (conceptKind) { + case INSTANCE: + return concept.asInstance().getType().getLabel(); + case ENTITY: + return concept.asEntity().getType().getLabel(); + case RELATION: + return concept.asRelation().getType().getLabel(); + case ATTRIBUTE: + return concept.asAttribute().getType().getLabel(); + default: + throw new AssertionError("ConceptKind does not have a type: " + conceptKind); + } + } + + private boolean isConceptValueType(Concept concept, Parameters.ConceptKind varKind, Parameters.ValueType valueType) { + switch (varKind) { + case ATTRIBUTE_TYPE: + AttributeType varAttributeType = concept.asAttributeType(); + switch (valueType) { + case BOOLEAN: + return varAttributeType.isBoolean(); + case LONG: + return varAttributeType.isLong(); + case DOUBLE: + return varAttributeType.isDouble(); + case DECIMAL: + return varAttributeType.isDecimal(); + case STRING: + return varAttributeType.isString(); + case DATE: + return varAttributeType.isDate(); + case DATETIME: + return varAttributeType.isDatetime(); + case DATETIME_TZ: + return varAttributeType.isDatetimeTZ(); + case DURATION: + return varAttributeType.isDuration(); + case STRUCT: + return varAttributeType.isStruct(); + default: + throw new IllegalStateException("Not covered ValueType: " + valueType); + } + case ATTRIBUTE: + Attribute varAttribute = concept.asAttribute(); + switch (valueType) { + case BOOLEAN: + return varAttribute.isBoolean(); + case LONG: + return varAttribute.isLong(); + case DOUBLE: + return varAttribute.isDouble(); + case DECIMAL: + return varAttribute.isDecimal(); + case STRING: + return varAttribute.isString(); + case DATE: + return varAttribute.isDate(); + case DATETIME: + return varAttribute.isDatetime(); + case DATETIME_TZ: + return varAttribute.isDatetimeTZ(); + case DURATION: + return varAttribute.isDuration(); + case STRUCT: + return varAttribute.isStruct(); + default: + throw new IllegalStateException("Not covered ValueType: " + valueType); + } + case VALUE: + Value varValue = concept.asValue(); + switch (valueType) { + case BOOLEAN: + return varValue.isBoolean(); + case LONG: + return varValue.isLong(); + case DOUBLE: + return varValue.isDouble(); + case DECIMAL: + return varValue.isDecimal(); + case STRING: + return varValue.isString(); + case DATE: + return varValue.isDate(); + case DATETIME: + return varValue.isDatetime(); + case DATETIME_TZ: + return varValue.isDatetimeTZ(); + case DURATION: + return varValue.isDuration(); + case STRUCT: + return varValue.isStruct(); + default: + throw new IllegalStateException("Not covered ValueType: " + valueType); + } + default: + throw new AssertionError("ConceptKind does not have a value or a value type: " + varKind); + } + } + + public Object parseExpectedValue(String value, Optional valueTypeOpt) { + Parameters.ValueType valueType; + valueType = valueTypeOpt.orElse(Parameters.ValueType.STRUCT); + + switch (valueType) { + case BOOLEAN: + return Boolean.parseBoolean(value); + case LONG: + return Long.parseLong(value); + case DOUBLE: + return Double.parseDouble(value); + case DECIMAL: + return new BigDecimal(value).setScale(DECIMAL_SCALE, RoundingMode.UNNECESSARY); + case STRING: + return value.substring(1, value.length() - 1).replace("\\\"", "\""); + case DATE: + return LocalDate.parse(value); + case DATETIME: + return LocalDateTime.parse(value); + case DATETIME_TZ: + for (DateTimeFormatter formatter : DATETIME_TZ_FORMATTERS) { + try { + return ZonedDateTime.parse(value, formatter); + } catch (DateTimeParseException e) { + // Continue to the next formatter if parsing fails + } + } + throw new AssertionError("DatetimeTZ format is not supported"); + case DURATION: + return Duration.parse(value); + case STRUCT: + return value; // compare string representations + default: + throw new AssertionError("Not covered ValueType: " + valueType); + } + } + + public Object unwrapValueAs(Value value, Parameters.ValueType valueType) { + switch (valueType) { + case BOOLEAN: + return value.asBoolean(); + case LONG: + return value.asLong(); + case DOUBLE: + return value.asDouble(); + case DECIMAL: + return value.asDecimal(); + case STRING: + return value.asString(); + case DATE: + return value.asDate(); + case DATETIME: + return value.asDatetime(); + case DATETIME_TZ: + return value.asDatetimeTZ(); + case DURATION: + return value.asDuration(); + case STRUCT: + return value.asStruct().toString(); // compare string representations + default: + throw new AssertionError("Not covered ValueType: " + valueType); + } + } + + @Given("typeql write query{may_error}") + @Given("typeql read query{may_error}") + @Given("typeql schema query{may_error}") + public void typeql_query(Parameters.MayError mayError, String query) { + clearAnswers(); + mayError.check(() -> tx().query(query).resolve()); + } + + @Given("get answers of typeql write query{may_error}") + @Given("get answers of typeql read query{may_error}") + @Given("get answers of typeql schema query{may_error}") + public void get_answers_of_typeql_query(Parameters.MayError mayError, String query) { + clearAnswers(); + mayError.check(() -> queryAnswer = tx().query(query).resolve()); + } + + @Given("concurrently get answers of typeql write query {integer} times") + @Given("concurrently get answers of typeql read query {integer} times") + @Given("concurrently get answers of typeql schema query {integer} times") + public void concurrently_get_answers_of_typeql_query_count_times(int count, String query) { + clearAnswers(); + queryAnswersParallel = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + queryAnswersParallel.add(CompletableFuture.supplyAsync(() -> tx().query(query).resolve(), threadPool)); + } + } + + @Then("answer type {is_or_not}: {query_answer_type}") + public void answer_type_is(Parameters.IsOrNot isOrNot, Parameters.QueryAnswerType queryAnswerType) { + switch (queryAnswerType) { + case OK: + isOrNot.check(queryAnswer.isOk()); + break; + case CONCEPT_ROWS: + isOrNot.check(queryAnswer.isConceptRows()); + break; + case CONCEPT_DOCUMENTS: + isOrNot.check(queryAnswer.isConceptDocuments()); + break; + default: + throw new AssertionError("Unknown query answer type: " + queryAnswerType); + } + } + + @Then("answer unwraps as {query_answer_type}{may_error}") + public void answer_unwraps_as(Parameters.QueryAnswerType queryAnswerType, Parameters.MayError mayError) { + mayError.check(() -> { + switch (queryAnswerType) { + case OK: + OkQueryAnswer _ok = queryAnswer.asOk(); + break; + case CONCEPT_ROWS: + ConceptRowIterator _rows = queryAnswer.asConceptRows(); + break; + case CONCEPT_DOCUMENTS: + ConceptDocumentIterator _documents = queryAnswer.asConceptDocuments(); + break; + default: + throw new AssertionError("Unknown query answer type: " + queryAnswerType); } -// assertEquals(String.format("An identifier entry (row) should match 1-to-1 to an row, but there were %d matching identifier entries for row with variables %s.", matchingIdentifiers.size(), row.variables().collect(Collectors.toSet())), 1, matchingIdentifiers.size()); + }); + } + + @Then("answer query type {is_or_not}: {query_type}") + public void answer_type_is(Parameters.IsOrNot isOrNot, QueryType queryType) { + isOrNot.compare(queryType, queryAnswer.getQueryType()); + } + + @Then("answer size is: {integer}") + public void answer_size_is(int size) { + collectAnswerIfNeeded(); + int answerSize; + if (collectedRows != null) { + answerSize = collectedRows.size(); + } else if (collectedDocuments != null) { + answerSize = collectedDocuments.size(); + } else { + throw new AssertionError("Query answer is not collected: the size is NULL"); + } + assertEquals(String.format("Expected [%d] answers, but got [%d]", size, answerSize), size, answerSize); + } + + @Then("answer column names are:") + public void answer_column_names_are(List names) { + collectAnswerIfNeeded(); + List answerColumnNames = collectedRows.get(0).columnNames().sorted().collect(Collectors.toList()); + assertEquals(names.stream().sorted().collect(Collectors.toList()), answerColumnNames); + } + + @Given("concurrently process {integer} row(s) from answers{may_error}") + public void concurrently_process_count_rows_from_answers(int count, Parameters.MayError mayError) { + queryAnswersParallel = new ArrayList<>(); + + List> jobs = queryAnswersParallel.stream() + .map(futureAnswer -> futureAnswer.thenComposeAsync(answer -> { + ConceptRowIterator iterator = answer.asConceptRows(); + List rows = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + try { + rows.add(iterator.next()); + } catch (NoSuchElementException e) { + mayError.check(() -> { + throw e; + }); + } + } + + return null; + }, threadPool)) + .collect(Collectors.toList()); + + CompletableFuture.allOf(jobs.toArray(new CompletableFuture[0])).join(); + } + + @Then("answer get row\\({integer}) query type {is_or_not}: {query_type}") + public void answer_get_row_query_type_is(int rowIndex, Parameters.IsOrNot isOrNot, QueryType queryType) { + collectRowsAnswerIfNeeded(); + isOrNot.compare(queryType, collectedRows.get(rowIndex).getQueryType()); + } + + @Then("answer get row\\({integer}) get variable{by_index_of_var}\\({var}){may_error}") + public void answer_get_row_get_variable_is_kind(int rowIndex, Parameters.IsByVarIndex isByVarIndex, String var, Parameters.MayError mayError) { + collectRowsAnswerIfNeeded(); + mayError.check(() -> getRowGetConcept(rowIndex, isByVarIndex, var)); + } + + @Then("answer get row\\({integer}) get variable{by_index_of_var}\\({var}) as {concept_kind}{may_error}") + public void answer_get_row_get_variable_is_kind(int rowIndex, Parameters.IsByVarIndex isByVarIndex, String var, Parameters.ConceptKind varKind, Parameters.MayError mayError) { + collectRowsAnswerIfNeeded(); + Concept varConcept = getRowGetConcept(rowIndex, isByVarIndex, var); + mayError.check(() -> unwrapConceptAs(varConcept, varKind)); + } + + @Then("answer get row\\({integer}) get {concept_kind}{by_index_of_var}\\({var}) is {concept_kind}: {bool}") + public void answer_get_row_get_variable_is_kind(int rowIndex, Parameters.ConceptKind varKind, Parameters.IsByVarIndex isByVarIndex, String var, Parameters.ConceptKind checkedKind, boolean isCheckedKind) { + collectRowsAnswerIfNeeded(); + Concept varConcept = getRowGetConcept(rowIndex, isByVarIndex, var); + assertEquals(isCheckedKind, isUnwrappedConceptKind(varConcept, varKind, checkedKind)); + } + + @Then("answer get row\\({integer}) get {concept_kind}{by_index_of_var}\\({var}) get type is {concept_kind}: {bool}") + public void answer_get_row_get_variable_get_type_is_kind(int rowIndex, Parameters.ConceptKind varKind, Parameters.IsByVarIndex isByVarIndex, String var, Parameters.ConceptKind checkedKind, boolean isCheckedKind) { + collectRowsAnswerIfNeeded(); + Concept varConcept = getRowGetConcept(rowIndex, isByVarIndex, var); + boolean isTypeKind; + switch (varKind) { + case INSTANCE: + isTypeKind = isConceptKind(varConcept.asInstance().getType(), checkedKind); + break; + case ENTITY: + isTypeKind = isConceptKind(varConcept.asEntity().getType(), checkedKind); + break; + case RELATION: + isTypeKind = isConceptKind(varConcept.asRelation().getType(), checkedKind); + break; + case ATTRIBUTE: + isTypeKind = isConceptKind(varConcept.asAttribute().getType(), checkedKind); + break; + default: + throw new AssertionError("ConceptKind does not have a type: " + varKind); } + assertEquals(isCheckedKind, isTypeKind); + } + + @Then("answer get row\\({integer}) get {concept_kind}{by_index_of_var}\\({var}) get label: {non_semicolon}") + public void answer_get_row_get_variable_get_label(int rowIndex, Parameters.ConceptKind varKind, Parameters.IsByVarIndex isByVarIndex, String var, String label) { + collectRowsAnswerIfNeeded(); + Concept varConcept = getRowGetConcept(rowIndex, isByVarIndex, var); + assertEquals(label, getLabelOfUnwrappedConcept(varConcept, varKind)); + } + + @Then("answer get row\\({integer}) get {concept_kind}{by_index_of_var}\\({var}) get type get label: {non_semicolon}") + public void answer_get_row_get_variable_get_type_get_label(int rowIndex, Parameters.ConceptKind varKind, Parameters.IsByVarIndex isByVarIndex, String var, String label) { + collectRowsAnswerIfNeeded(); + Concept varConcept = getRowGetConcept(rowIndex, isByVarIndex, var); + assertEquals(label, getLabelOfUnwrappedConceptsType(varConcept, varKind)); } - @Then("order of answer concepts is") - public void order_of_answer_concepts_is(List> answersIdentifiers) { - assertEquals( - String.format("The number of identifier entries (rows) should match the number of answers, but found %d identifier entries and %d answers.", - answersIdentifiers.size(), answerRows.size()), - answersIdentifiers.size(), answerRows.size() - ); - for (int i = 0; i < answerRows.size(); i++) { - ConceptRow answer = answerRows.get(i); - Map answerIdentifiers = answersIdentifiers.get(i); - assertTrue( - String.format("The answer at index %d does not match the identifier entry (row) at index %d.", i, i), - matchAnswerConcept(answerIdentifiers, answer) - ); + @Then("answer get row\\({integer}) get {concept_kind}{by_index_of_var}\\({var}) get value type: {non_semicolon}") + public void answer_get_row_get_variable_get_value_type(int rowIndex, Parameters.ConceptKind varKind, Parameters.IsByVarIndex isByVarIndex, String var, String valueType) { + collectRowsAnswerIfNeeded(); + Concept varConcept = getRowGetConcept(rowIndex, isByVarIndex, var); + + String varValueType; + switch (varKind) { + case ATTRIBUTE_TYPE: + varValueType = varConcept.asAttributeType().getValueType(); + break; + case VALUE: + varValueType = varConcept.asValue().getType(); + break; + default: + throw new AssertionError("ConceptKind does not have a value type: " + varKind); } + + assertEquals(valueType, varValueType); } - @Then("aggregate value is: {double}") - public void aggregate_value_is(double expectedAnswer) { - assertNotNull("The last executed query was not an aggregate query", valueAnswer); - assertTrue("The last executed aggregate query returned NaN", valueAnswer.isPresent()); - double value = valueAnswer.get().isDouble() ? valueAnswer.get().asDouble() : valueAnswer.get().asLong(); - assertEquals(String.format("Expected answer to equal %f, but it was %f.", expectedAnswer, value), - expectedAnswer, value, 0.001); - } - - @Then("aggregate answer is empty") - public void aggregate_answer_is_empty() { - assertNotNull("The last executed query was not an aggregate query", valueAnswer); - assertTrue(valueAnswer.isEmpty()); - } - -// @Then("answer groups are") -// public void answer_groups_are(List> answerIdentifierTable) { -// Set answerIdentifierGroups = answerIdentifierTable.stream() -// .collect(Collectors.groupingBy(x -> x.get(AnswerIdentifierGroup.GROUP_COLUMN_NAME))) -// .values() -// .stream() -// .map(AnswerIdentifierGroup::new) -// .collect(Collectors.toSet()); -// -// assertEquals(String.format("Expected [%d] answer groups, but found [%d].", -// answerIdentifierGroups.size(), answerGroups.size()), -// answerIdentifierGroups.size(), answerGroups.size() -// ); -// -// for (AnswerIdentifierGroup answerIdentifierGroup : answerIdentifierGroups) { -// String[] identifier = answerIdentifierGroup.ownerIdentifier.split(":", 2); -// UniquenessCheck checker; -// switch (identifier[0]) { -// case "label": -// checker = new LabelUniquenessCheck(identifier[1]); -// break; -// case "key": -// checker = new KeyUniquenessCheck(identifier[1]); -// break; -// case "attr": -// checker = new AttributeValueUniquenessCheck(identifier[1]); -// break; -// case "value": -// checker = new ValueUniquenessCheck(identifier[1]); -// break; -// default: -// throw new IllegalStateException("Unexpected value: " + identifier[0]); -// } -// ConceptMapGroup answerGroup = answerGroups.stream() -// .filter(ag -> checker.check(ag.owner())) -// .findAny() -// .orElse(null); -// assertNotNull(String.format("The group identifier [%s] does not match any of the answer group owners.", answerIdentifierGroup.ownerIdentifier), answerGroup); -// -// List> answersIdentifiers = answerIdentifierGroup.answersIdentifiers; -// answerGroup.conceptMaps().forEach(answer -> { -// List> matchingIdentifiers = new ArrayList<>(); -// -// for (Map answerIdentifiers : answersIdentifiers) { -// -// if (matchAnswerConcept(answerIdentifiers, answer)) { -// matchingIdentifiers.add(answerIdentifiers); -// } -// } -// assertEquals(String.format("An identifier entry (row) should match 1-to-1 to an answer, but there were [%d] matching identifier entries for answer with variables %s.", matchingIdentifiers.size(), answer.variables().collect(Collectors.toSet())), 1, matchingIdentifiers.size()); -// }); -// } -// } - -// @Then("group aggregate values are") -// public void group_aggregate_values_are(List> answerIdentifierTable) { -// Map expectations = new HashMap<>(); -// for (Map answerIdentifierRow : answerIdentifierTable) { -// String groupOwnerIdentifier = answerIdentifierRow.get(AnswerIdentifierGroup.GROUP_COLUMN_NAME); -// double expectedAnswer = Double.parseDouble(answerIdentifierRow.get("value")); -// expectations.put(groupOwnerIdentifier, expectedAnswer); -// } -// -// assertEquals(String.format("Expected [%d] answer groups, but found [%d].", expectations.size(), valueAnswerGroups.size()), -// expectations.size(), valueAnswerGroups.size() -// ); -// -// for (Map.Entry expectation : expectations.entrySet()) { -// String[] identifier = expectation.getKey().split(":", 2); -// UniquenessCheck checker; -// switch (identifier[0]) { -// case "label": -// checker = new LabelUniquenessCheck(identifier[1]); -// break; -// case "key": -// checker = new KeyUniquenessCheck(identifier[1]); -// break; -// case "attr": -// checker = new AttributeValueUniquenessCheck(identifier[1]); -// break; -// case "value": -// checker = new ValueUniquenessCheck(identifier[1]); -// break; -// default: -// throw new IllegalStateException("Unexpected value: " + identifier[0]); -// } -// double expectedAnswer = expectation.getValue(); -// ValueGroup answerGroup = valueAnswerGroups.stream() -// .filter(ag -> checker.check(ag.owner())) -// .findAny() -// .orElse(null); -// assertNotNull(String.format("The group identifier [%s] does not match any of the answer group owners.", expectation.getKey()), answerGroup); -// -// Value value = answerGroup.value().get(); -// double actualAnswer = value.isDouble() ? value.asDouble() : value.asLong(); -// assertEquals( -// String.format("Expected answer [%f] for group [%s], but got [%f]", -// expectedAnswer, expectation.getKey(), actualAnswer), -// expectedAnswer, actualAnswer, 0.001 -// ); -// } -// } - -// @Then("number of groups is: {int}") -// public void number_of_groups_is(int expectedGroupCount) { -// assertEquals(expectedGroupCount, answerGroups.size()); -// } -// -// public static class AnswerIdentifierGroup { -// private final String ownerIdentifier; -// private final List> answersIdentifiers; -// -// private static final String GROUP_COLUMN_NAME = "owner"; -// -// public AnswerIdentifierGroup(List> answerIdentifierTable) { -// ownerIdentifier = answerIdentifierTable.get(0).get(GROUP_COLUMN_NAME); -// answersIdentifiers = new ArrayList<>(); -// for (Map rawAnswerIdentifiers : answerIdentifierTable) { -// answersIdentifiers.add(rawAnswerIdentifiers.entrySet().stream() -// .filter(e -> !e.getKey().equals(GROUP_COLUMN_NAME)) -// .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); -// } -// } -// } - - -// @Then("group aggregate answer value is empty") -// public void group_aggregate_answer_value_not_a_number() { -// assertEquals("Step requires exactly 1 grouped answer", 1, valueAnswerGroups.size()); -// assertTrue(valueAnswerGroups.get(0).value().isEmpty()); -// } - - private boolean matchAnswerConcept(Map answerIdentifiers, ConceptRow row) { - for (Map.Entry entry : answerIdentifiers.entrySet()) { - String var = entry.getKey(); - String[] identifier = entry.getValue().split(":", 2); -// switch (identifier[0]) { -// case "label": -// if (!new LabelUniquenessCheck(identifier[1]).check(row.get(var))) { -// return false; -// } -// break; -// case "key": -// if (!new KeyUniquenessCheck(identifier[1]).check(row.get(var))) { -// return false; -// } -// break; -// case "attr": -// if (!new AttributeValueUniquenessCheck(identifier[1]).check(row.get(var))) { -// return false; -// } -// break; -// case "value": -// if (!new ValueUniquenessCheck(identifier[1]).check(row.get(var))) { -// return false; -// } -// break; -// } + @Then("answer get row\\({integer}) get {concept_kind}{by_index_of_var}\\({var}) get type get value type: {non_semicolon}") + public void answer_get_row_get_variable_get_type_get_value_type(int rowIndex, Parameters.ConceptKind varKind, Parameters.IsByVarIndex isByVarIndex, String var, String valueType) { + collectRowsAnswerIfNeeded(); + Concept varConcept = getRowGetConcept(rowIndex, isByVarIndex, var); + + String varValueType; + switch (varKind) { + case ATTRIBUTE: + varValueType = varConcept.asAttribute().getType().getValueType(); + break; + default: + throw new AssertionError("ConceptKind does not have a type with a value type: " + varKind); } - return true; - } - -// @When("get answers of typeql fetch") -// public void typeql_fetch(String typeQLQueryStatements) { -// try { -// TypeQLFetch typeQLQuery = TypeQL.parseQuery(String.join("\n", typeQLQueryStatements)).asFetch(); -// clearAnswers(); -// fetchAnswers = tx().query(String.join("\n", typeQLQueryStatements)).collect(Collectors.toList()); -// } catch (TypeQLException e) { -// // NOTE: We manually close transaction here, because we want to align with all non-java drivers, -// // where parsing happens at server-side which closes transaction if they fail -// tx().close(); -// throw e; -// } -// } - - @Then("fetch answers are") - public void fetch_answers_are(String expectedJSON) { - JSON expected = JSON.parse(expectedJSON); - assertTrue("Fetch response is a list of JSON objects, but the behaviour test expects something else", expected.isArray()); - assertTrue(JSONListMatches(fetchAnswers, expected.asArray())); + + assertEquals(valueType, varValueType); + } + + @Then("answer get row\\({integer}) get attribute type{by_index_of_var}\\({var}) is untyped: {bool}") + public void answer_get_row_get_attribute_type_is_untyped(int rowIndex, Parameters.IsByVarIndex isByVarIndex, String var, boolean isUntyped) { + collectRowsAnswerIfNeeded(); + Concept varConcept = getRowGetConcept(rowIndex, isByVarIndex, var); + assertEquals(isUntyped, varConcept.asAttributeType().isUntyped()); + } + + @Then("answer get row\\({integer}) get {concept_kind}{by_index_of_var}\\({var}) is {value_type}: {bool}") + public void answer_get_row_get_variable_is_value_type(int rowIndex, Parameters.ConceptKind varKind, Parameters.IsByVarIndex isByVarIndex, String var, Parameters.ValueType valueType, boolean isValueType) { + collectRowsAnswerIfNeeded(); + Concept varConcept = getRowGetConcept(rowIndex, isByVarIndex, var); + assertEquals(isValueType, isConceptValueType(varConcept, varKind, valueType)); + } + + @Then("answer get row\\({integer}) get {concept_kind}{by_index_of_var}\\({var}) {contains_or_doesnt} iid") + public void answer_get_row_get_variable_get_iid_exists(int rowIndex, Parameters.ConceptKind varKind, Parameters.IsByVarIndex isByVarIndex, String var, Parameters.ContainsOrDoesnt containsOrDoesnt) { + collectRowsAnswerIfNeeded(); + Concept varConcept = getRowGetConcept(rowIndex, isByVarIndex, var); + String iid; + switch (varKind) { + case ENTITY: + iid = varConcept.asEntity().getIID(); + break; + case RELATION: + iid = varConcept.asRelation().getIID(); + break; + default: + throw new IllegalStateException("ConceptKind does not have iids: " + varKind); + } + containsOrDoesnt.check(iid != null && !iid.isEmpty()); + } + + @Then("answer get row\\({integer}) get {concept_kind}{by_index_of_var}\\({var}) as {value_type}{may_error}") + public void answer_get_row_get_variable_as_value_type(int rowIndex, Parameters.ConceptKind varKind, Parameters.IsByVarIndex isByVarIndex, String var, Parameters.ValueType valueType, Parameters.MayError mayError) { + collectRowsAnswerIfNeeded(); + Value varValue = getRowGetValue(rowIndex, varKind, isByVarIndex, var); + assertNotNull(varValue.asUntyped()); + mayError.check(() -> unwrapValueAs(varValue, valueType)); + } + + @Then("answer get row\\({integer}) get {concept_kind}{by_index_of_var}\\({var}) get value {is_or_not}: {non_semicolon}") + public void answer_get_row_get_variable_get_value_is(int rowIndex, Parameters.ConceptKind varKind, Parameters.IsByVarIndex isByVarIndex, String var, Parameters.IsOrNot isOrNot, String value) { + collectRowsAnswerIfNeeded(); + Value varValue = getRowGetValue(rowIndex, varKind, isByVarIndex, var); + Parameters.ValueType valueType = Parameters.ValueType.of(varValue.getType()); + isOrNot.compare(parseExpectedValue(value, valueType == null ? Optional.empty() : Optional.of(valueType)), varValue.asUntyped()); + } + + @Then("answer get row\\({integer}) get {concept_kind}{by_index_of_var}\\({var}) as {value_type} {is_or_not}: {non_semicolon}") + public void answer_get_row_get_variable_as_value_type_is(int rowIndex, Parameters.ConceptKind varKind, Parameters.IsByVarIndex isByVarIndex, String var, Parameters.ValueType valueType, Parameters.IsOrNot isOrNot, String value) { + collectRowsAnswerIfNeeded(); + Value varValue = getRowGetValue(rowIndex, varKind, isByVarIndex, var); + isOrNot.compare(parseExpectedValue(value, Optional.of(valueType)), unwrapValueAs(varValue, valueType)); + } + + @Then("answer get row\\({integer}) get concepts size is: {integer}") + public void answer_get_row_get_concepts_size_is(int rowIndex, int size) { + collectRowsAnswerIfNeeded(); + int conceptsSize = getRowGetConcepts(rowIndex).size(); + assertEquals(String.format("Expected [%d] answers, but got [%d]", size, conceptsSize), size, conceptsSize); + } + + @Then("answer {contains_or_doesnt} document:") + public void answer_contains_document(Parameters.ContainsOrDoesnt containsOrDoesnt, String expectedDocument) { + collectDocumentsAnswerIfNeeded(); + JSON expectedJSON = JSON.parse(expectedDocument); + containsOrDoesnt.check(JSONListContains(collectedDocuments, expectedJSON)); } } diff --git a/java/test/behaviour/util/Util.java b/java/test/behaviour/util/Util.java index 80ed8b0cce..6f79842767 100644 --- a/java/test/behaviour/util/Util.java +++ b/java/test/behaviour/util/Util.java @@ -46,7 +46,8 @@ public static void assertThrowsWithMessage(Runnable function, String message) { function.run(); fail(); } catch (RuntimeException e) { - assert e.toString().toLowerCase().contains(message.toLowerCase()); + assertTrue(String.format("Expected message '%s', but got '%s'", message, e), + e.toString().toLowerCase().contains(message.toLowerCase())); } } @@ -66,9 +67,15 @@ public static boolean JSONListMatches(List lhs, List rhs) { return rhsMatches.size() == rhs.size(); } + public static boolean JSONListContains(List list, JSON json) { + return list.stream().anyMatch(listJSON -> JSONMatches(listJSON, json)); + } + public static boolean JSONMatches(JSON lhs, JSON rhs) { - if (lhs.isObject()) { - if (!rhs.isObject()) return false; + if (lhs == null) { + return rhs == null; + } else if (lhs.isObject()) { + if (rhs == null || !rhs.isObject()) return false; Map lhsMap = lhs.asObject(); Map rhsMap = rhs.asObject(); if (lhsMap.size() != rhsMap.size()) return false; diff --git a/java/test/behaviour/util/UtilSteps.java b/java/test/behaviour/util/UtilSteps.java index e1f5c61553..23eea30740 100644 --- a/java/test/behaviour/util/UtilSteps.java +++ b/java/test/behaviour/util/UtilSteps.java @@ -31,7 +31,7 @@ public void set_timezone(String value) { TimeZone.setDefault(TimeZone.getTimeZone(value)); } - @Then("wait {int} seconds") + @Then("wait {integer} seconds") public void wait_seconds(int seconds) throws InterruptedException { Thread.sleep(seconds * 1000L); } diff --git a/java/test/integration/AddressTranslationTest.java b/java/test/integration/AddressTranslationTest.java deleted file mode 100644 index 02519ff5cc..0000000000 --- a/java/test/integration/AddressTranslationTest.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package com.typedb.driver.test.integration; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@SuppressWarnings("Duplicates") -public class AddressTranslationTest { - private static final Logger LOG = LoggerFactory.getLogger(AddressTranslationTest.class); - -// private static final Credential credential = new Credential("admin", "password", false); - -// @Test -// public void testAddressTranslation() { -// TypeDBCloudRunner typedb = TypeDBCloudRunner.create(Paths.get("."), 3); -// typedb.start(); -// Map addresses = typedb.externalAddresses().stream().map(address -> pair(address, address)) -// .collect(Collectors.toMap(Pair::first, Pair::second)); -// TypeDBDriver driver = TypeDB.cloudDriver(addresses, credential); -// driver.databases().create("typedb"); -// TypeDBSession session = driver.session("typedb", TypeDBSession.Type.DATA); -// Transaction tx = session.transaction(Transaction.Type.WRITE); -// EntityType root = tx.concepts().getRootEntityType(); -// assertNotNull(root); -// assertEquals(1, root.getSubtypes(tx).collect(Collectors.toList()).size()); -// tx.close(); -// session.close(); -// driver.close(); -// } -} diff --git a/java/test/integration/BUILD b/java/test/integration/BUILD index 40e5f6d359..fa074942a8 100644 --- a/java/test/integration/BUILD +++ b/java/test/integration/BUILD @@ -16,52 +16,6 @@ # under the License. load("@vaticle_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test") -load("@vaticle_dependencies//builder/java:rules.bzl", "typedb_java_test") - -typedb_java_test( - name = "test-query-core", - srcs = ["DriverQueryTest.java"], - server_artifacts = { - "@vaticle_bazel_distribution//platform:is_linux_arm64": "@vaticle_typedb_artifact_linux-arm64//file", - "@vaticle_bazel_distribution//platform:is_linux_x86_64": "@vaticle_typedb_artifact_linux-x86_64//file", - "@vaticle_bazel_distribution//platform:is_mac_arm64": "@vaticle_typedb_artifact_mac-arm64//file", - "@vaticle_bazel_distribution//platform:is_mac_x86_64": "@vaticle_typedb_artifact_mac-x86_64//file", -# "@vaticle_bazel_distribution//platform:is_windows_x86_64": "@vaticle_typedb_artifact_windows-x86_64//file", - }, - test_class = "com.typedb.driver.test.integration.DriverQueryTest", - deps = [ - # Internal dependencies - "//java:driver-java", - "//java/api", - "//java/common", - - # External dependencies from @vaticle - "@maven//:org_slf4j_slf4j_api", -# "@maven//:com_vaticle_typedb_typedb_runner", - ], -) - -#typedb_java_test( -# name = "test-address-translation-cloud", -# srcs = ["AddressTranslationTest.java"], -# server_artifacts = { -# "@vaticle_bazel_distribution//platform:is_linux_arm64": "@vaticle_typedb_cloud_artifact_linux-arm64//file", -# "@vaticle_bazel_distribution//platform:is_linux_x86_64": "@vaticle_typedb_cloud_artifact_linux-x86_64//file", -# "@vaticle_bazel_distribution//platform:is_mac_arm64": "@vaticle_typedb_cloud_artifact_mac-arm64//file", -# "@vaticle_bazel_distribution//platform:is_mac_x86_64": "@vaticle_typedb_cloud_artifact_mac-x86_64//file", -# "@vaticle_bazel_distribution//platform:is_windows_x86_64": "@vaticle_typedb_cloud_artifact_windows-x86_64//file", -# }, -# test_class = "com.typedb.driver.test.integration.AddressTranslationTest", -# deps = [ -# # Internal dependencies -# "//java:driver-java", -# "//java/api", -# -# # External dependencies from @vaticle -# "@maven//:org_slf4j_slf4j_api", -# "@maven//:com_vaticle_typedb_typedb_cloud_runner", -# ], -#) checkstyle_test( name = "checkstyle", diff --git a/java/test/integration/cloud/BUILD b/java/test/integration/cloud/BUILD new file mode 100644 index 0000000000..fa074942a8 --- /dev/null +++ b/java/test/integration/cloud/BUILD @@ -0,0 +1,24 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +load("@vaticle_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test") + +checkstyle_test( + name = "checkstyle", + include = glob(["*"]), + license_type = "apache-header", +) diff --git a/java/test/integration/concept/.gitkeep b/java/test/integration/concept/.gitkeep deleted file mode 100644 index 63d60c9136..0000000000 --- a/java/test/integration/concept/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -Placeholder for upcoming integration tests - delete this file once the directory has other files in it \ No newline at end of file diff --git a/java/test/integration/core/BUILD b/java/test/integration/core/BUILD new file mode 100644 index 0000000000..9a3de36c68 --- /dev/null +++ b/java/test/integration/core/BUILD @@ -0,0 +1,76 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +load("@vaticle_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test") +load("@vaticle_dependencies//builder/java:rules.bzl", "typedb_java_test") + +exports_files( + ["ExampleTest.java"], + visibility = ["//java:__subpackages__"], +) + +typedb_java_test( + name = "test-example", + srcs = ["ExampleTest.java"], + server_artifacts = { + "@vaticle_bazel_distribution//platform:is_linux_arm64": "@vaticle_typedb_artifact_linux-arm64//file", + "@vaticle_bazel_distribution//platform:is_linux_x86_64": "@vaticle_typedb_artifact_linux-x86_64//file", + "@vaticle_bazel_distribution//platform:is_mac_arm64": "@vaticle_typedb_artifact_mac-arm64//file", + "@vaticle_bazel_distribution//platform:is_mac_x86_64": "@vaticle_typedb_artifact_mac-x86_64//file", +# "@vaticle_bazel_distribution//platform:is_windows_x86_64": "@vaticle_typedb_artifact_windows-x86_64//file", + }, + test_class = "com.typedb.driver.test.integration.core.ExampleTest", + deps = [ + # Internal dependencies + "//java:driver-java", + "//java/api", + "//java/common", + + # External dependencies from @vaticle + "@maven//:org_slf4j_slf4j_api", +# "@maven//:com_vaticle_typedb_typedb_runner", + ], +) + +typedb_java_test( + name = "test-value", + srcs = ["ValueTest.java"], + server_artifacts = { + "@vaticle_bazel_distribution//platform:is_linux_arm64": "@vaticle_typedb_artifact_linux-arm64//file", + "@vaticle_bazel_distribution//platform:is_linux_x86_64": "@vaticle_typedb_artifact_linux-x86_64//file", + "@vaticle_bazel_distribution//platform:is_mac_arm64": "@vaticle_typedb_artifact_mac-arm64//file", + "@vaticle_bazel_distribution//platform:is_mac_x86_64": "@vaticle_typedb_artifact_mac-x86_64//file", +# "@vaticle_bazel_distribution//platform:is_windows_x86_64": "@vaticle_typedb_artifact_windows-x86_64//file", + }, + test_class = "com.typedb.driver.test.integration.core.ValueTest", + deps = [ + # Internal dependencies + "//java:driver-java", + "//java/api", + "//java/common", + + # External dependencies from @vaticle + "@maven//:org_slf4j_slf4j_api", +# "@maven//:com_vaticle_typedb_typedb_runner", + ], +) + +checkstyle_test( + name = "checkstyle", + include = glob(["*"]), + license_type = "apache-header", +) diff --git a/java/test/integration/core/ExampleTest.java b/java/test/integration/core/ExampleTest.java new file mode 100644 index 0000000000..2e3d45cd3e --- /dev/null +++ b/java/test/integration/core/ExampleTest.java @@ -0,0 +1,278 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package com.typedb.driver.test.integration.core; + +// EXAMPLE START MARKER +import com.typedb.driver.TypeDB; +import com.typedb.driver.api.Driver; +import com.typedb.driver.api.QueryType; +import com.typedb.driver.api.Transaction; +import com.typedb.driver.api.answer.ConceptRow; +import com.typedb.driver.api.answer.ConceptRowIterator; +import com.typedb.driver.api.answer.QueryAnswer; +import com.typedb.driver.api.concept.Concept; +import com.typedb.driver.api.concept.type.AttributeType; +import com.typedb.driver.api.concept.type.EntityType; +import com.typedb.driver.api.database.Database; +import com.typedb.driver.common.Promise; +import com.typedb.driver.common.exception.TypeDBDriverException; +// EXAMPLE END MARKER + +import org.junit.BeforeClass; +import org.junit.Test; + +// EXAMPLE START MARKER +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; + +// EXAMPLE END MARKER + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@SuppressWarnings("Duplicates") +// EXAMPLE START MARKER +public class ExampleTest { + // EXAMPLE END MARKER + @BeforeClass + public static void setUpClass() { + Driver typedbDriver = TypeDB.coreDriver(TypeDB.DEFAULT_ADDRESS); + if (typedbDriver.databases().contains("typedb")) { + typedbDriver.databases().get("typedb").delete(); + } + typedbDriver.close(); + } + + @Test + // EXAMPLE START MARKER + public void example() { + // Open a driver connection. Try-with-resources can be used for automatic driver connection management + try (Driver driver = TypeDB.coreDriver(TypeDB.DEFAULT_ADDRESS)) { + // Create a database + driver.databases().create("typedb"); + Database database = driver.databases().get("typedb"); + assertEquals(database.name(), "typedb"); + + // Open transactions of 3 types + Transaction tx = driver.transaction(database.name(), Transaction.Type.READ); + + // Use "try" blocks to catch driver exceptions + try { + // Execute any TypeDB query using TypeQL. Wrong queries are rejected with an explicit exception + Promise promise = tx.query("define entity i-cannot-be-defined-in-read-transactions;"); + + System.out.println("The result is still promised, so it needs resolving even in case of errors!"); + promise.resolve(); + } catch (TypeDBDriverException expectedException) { + System.out.println("Once the query's promise is resolved, the exception is revealed: " + expectedException); + } finally { + // Don't forget to close the transaction! + tx.close(); + } + + // Open a schema transaction to make schema changes + // Use try-with-resources blocks to forget about "close" operations (similarly to connections) + try (Transaction transaction = driver.transaction(database.name(), Transaction.Type.SCHEMA)) { + String defineQuery = "define " + + "entity person, owns name, owns age; " + + "attribute name, value string;\n" + + "attribute age, value long;"; + + QueryAnswer answer = transaction.query(defineQuery).resolve(); + assertTrue(answer.isOk()); + assertEquals(QueryType.SCHEMA, answer.getQueryType()); + + // Commit automatically closes the transaction. It can still be safely called inside "try" blocks + transaction.commit(); + } + + + // Open a read transaction to safely read anything without database modifications + try (Transaction transaction = driver.transaction(database.name(), Transaction.Type.READ)) { + QueryAnswer entityAnswer = transaction.query("match entity $x;").resolve(); + assertTrue(entityAnswer.isConceptRows()); + assertFalse(entityAnswer.isConceptDocuments()); + assertEquals(QueryType.READ, entityAnswer.getQueryType()); + + // Collect concept rows that represent the answer as a table + List entityRows = entityAnswer.asConceptRows().stream().collect(Collectors.toList()); + assertEquals(entityRows.size(), 1); + ConceptRow entityRow = entityRows.get(0); + + // Collect column names to get concepts by index if the variable names are lost + List entityHeader = entityRow.columnNames().collect(Collectors.toList()); + assertEquals(entityHeader.size(), 1); + + String columnName = entityHeader.get(0); + assertEquals(columnName, "x"); + + // Get concept by the variable name (column name) + Concept conceptByName = entityRow.get(columnName); + + // Get concept by the header's index + Concept conceptByIndex = entityRow.getIndex(0); + assertEquals(conceptByName, conceptByIndex); + + // Check if it's an entity type before the conversion + if (conceptByName.isEntityType()) { + System.out.printf("Getting concepts by variable names and indexes is equally correct. " + + "Both represent the defined entity type: '%s' (in case of a doubt: '%s')%n", + conceptByName.asEntityType().getLabel(), + conceptByIndex.asEntityType().getLabel()); + } + assertTrue(conceptByName.isEntityType()); + assertTrue(conceptByName.isType()); + assertEquals(conceptByName.asEntityType().getLabel(), "person"); + assertNotEquals(conceptByName.asEntityType().getLabel(), "not person"); + assertNotEquals(conceptByName.asEntityType().getLabel(), "age"); + + // Continue querying in the same transaction if needed + QueryAnswer attributeAnswer = transaction.query("match attribute $a;").resolve(); + assertTrue(attributeAnswer.isConceptRows()); + assertEquals(QueryType.READ, attributeAnswer.getQueryType()); + + // ConceptRowIterator can be used as any other Iterator + ConceptRowIterator attributeRowIterator = attributeAnswer.asConceptRows(); + + while (attributeRowIterator.hasNext()) { + ConceptRow attributeRow = attributeRowIterator.next(); + + // Column names are a stream, so they can be used in a similar way + Iterator columnNameIterator = attributeRow.columnNames().iterator(); + columnName = columnNameIterator.next(); + assertFalse(columnNameIterator.hasNext()); + + conceptByName = attributeRow.get(columnName); + + // Check if it's an attribute type before the conversion + if (conceptByName.isAttributeType()) { + AttributeType attributeType = conceptByName.asAttributeType(); + System.out.printf("Defined attribute type's label: '%s', value type: '%s'%n", attributeType.getLabel(), attributeType.getValueType()); + assertTrue(attributeType.isLong() || attributeType.isString()); + assertTrue(Objects.equals(attributeType.getValueType(), "long") || Objects.equals(attributeType.getValueType(), "string")); + assertTrue(Objects.equals(attributeType.getLabel(), "age") || Objects.equals(attributeType.getLabel(), "name")); + assertNotEquals(attributeType.getLabel(), "person"); + assertNotEquals(attributeType.getLabel(), "person:age"); + + System.out.printf("It is also possible to just print the concept itself: '%s'%n", conceptByName); + assertTrue(conceptByName.isAttributeType()); + assertTrue(conceptByName.isType()); + } + } + } + + // Open a write transaction to insert data + try (Transaction transaction = driver.transaction(database.name(), Transaction.Type.WRITE)) { + String insertQuery = "insert $z isa person, has age 10; $x isa person, has age 20, has name \"John\";"; + QueryAnswer answer = transaction.query(insertQuery).resolve(); + assertTrue(answer.isConceptRows()); + assertEquals(QueryType.WRITE, answer.getQueryType()); + + // Insert queries also return concept rows + List rows = answer.asConceptRows().stream().collect(Collectors.toList()); + assertEquals(rows.size(), 1); + ConceptRow row = rows.get(0); + row.columnNames().iterator().forEachRemaining(columnName -> { + Concept insertedConcept = row.get(columnName); + System.out.printf("Successfully inserted $%s: %s%n", columnName, insertedConcept); + if (insertedConcept.isEntity()) { + System.out.println("This time, it's an entity, not a type!"); + } + }); + + // It is possible to ask for the column names again + List header = row.columnNames().collect(Collectors.toList()); + assertEquals(header.size(), 2); + assertTrue(header.contains("x")); + assertTrue(header.contains("z")); + + Concept x = row.getIndex(header.indexOf("x")); + if (x.isEntity()) { + System.out.println("Each entity receives a unique IID. It can be retrieved directly: " + x.asEntity().getIID()); + } + + // Do not forget to commit if the changes should be persisted + transaction.commit(); + } + + // Open a read transaction to verify that the inserted data is saved + try (Transaction transaction = driver.transaction(database.name(), Transaction.Type.READ)) { + // A match query can be used for concept row outputs + String var = "x"; + QueryAnswer matchAnswer = transaction.query(String.format("match $%s isa person;", var)).resolve(); + assertTrue(matchAnswer.isConceptRows()); + assertEquals(QueryType.READ, matchAnswer.getQueryType()); + + // Simple match queries always return concept rows + AtomicInteger matchCount = new AtomicInteger(0); + matchAnswer.asConceptRows().stream().forEach(row -> { + assertEquals(QueryType.READ, row.getQueryType()); + Concept x = row.get(var); + assertTrue(x.isEntity()); + assertFalse(x.isEntityType()); + assertFalse(x.isAttribute()); + assertFalse(x.isType()); + assertTrue(x.isInstance()); + EntityType xType = x.asEntity().getType().asEntityType(); + assertEquals(xType.getLabel(), "person"); + assertNotEquals(xType.getLabel(), "not person"); + matchCount.incrementAndGet(); + System.out.printf("Found a person %s of type %s%n", x, xType); + }); + assertEquals(matchCount.get(), 2); + System.out.println("Total persons found: " + matchCount.get()); + + // A fetch query can be used for concept document outputs with flexible structure + QueryAnswer fetchAnswer = transaction.query("match" + + " $x isa! person, has $a;" + + " $a isa! $t;" + + "fetch {" + + " \"single attribute type\": $t," + + " \"single attribute\": $a," + + " \"all attributes\": { $x.* }," + + "};").resolve(); + assertTrue(fetchAnswer.isConceptDocuments()); + assertEquals(QueryType.READ, fetchAnswer.getQueryType()); + + // Fetch queries always return concept documents + AtomicInteger fetchCount = new AtomicInteger(0); + fetchAnswer.asConceptDocuments().stream().forEach(document -> { + assertNotNull(document); + System.out.println("Fetched a document: " + document); + System.out.print("This document contains an attribute of type: "); + System.out.println(document.asObject().get("single attribute type").asObject().get("label")); + + fetchCount.incrementAndGet(); + }); + assertEquals(fetchCount.get(), 3); + System.out.println("Total documents fetched: " + fetchCount.get()); + } + } + + System.out.println("More examples can be found in the API reference and the documentation.\nWelcome to TypeDB!"); + } +} +// EXAMPLE END MARKER diff --git a/java/test/integration/DriverQueryTest.java b/java/test/integration/core/ValueTest.java similarity index 56% rename from java/test/integration/DriverQueryTest.java rename to java/test/integration/core/ValueTest.java index 333fc1e0b2..23a5191c0e 100644 --- a/java/test/integration/DriverQueryTest.java +++ b/java/test/integration/core/ValueTest.java @@ -17,27 +17,22 @@ * under the License. */ -package com.typedb.driver.test.integration; +package com.typedb.driver.test.integration.core; import com.typedb.driver.TypeDB; import com.typedb.driver.api.Driver; -import com.typedb.driver.api.QueryType; import com.typedb.driver.api.Transaction; import com.typedb.driver.api.answer.ConceptRow; import com.typedb.driver.api.answer.QueryAnswer; import com.typedb.driver.api.concept.Concept; import com.typedb.driver.api.concept.instance.Attribute; -import com.typedb.driver.api.concept.instance.Entity; import com.typedb.driver.api.concept.type.AttributeType; -import com.typedb.driver.api.concept.type.EntityType; import com.typedb.driver.api.concept.value.Value; import com.typedb.driver.api.database.Database; import com.typedb.driver.common.Duration; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.math.BigDecimal; import java.math.RoundingMode; @@ -55,14 +50,11 @@ import java.util.stream.Collectors; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @SuppressWarnings("Duplicates") -public class DriverQueryTest { - private static final Logger LOG = LoggerFactory.getLogger(DriverQueryTest.class); +public class ValueTest { private static final String DB_NAME = "typedb"; private static final String ADDRESS = "0.0.0.0:1729"; private static Driver typedbDriver; @@ -80,165 +72,7 @@ public static void close() { } @Test - public void basicTest() { - if (typedbDriver.databases().contains(DB_NAME)) { - typedbDriver.databases().get(DB_NAME).delete(); - } - typedbDriver.databases().create(DB_NAME); - Database database = typedbDriver.databases().get(DB_NAME); - assertEquals(database.name(), DB_NAME); - - localhostTypeDBTX(tx -> { - QueryAnswer answer = tx.query("define entity person, owns age; attribute age, value long;").resolve(); - assertTrue(answer.isOk()); - assertEquals(answer.getQueryType(), QueryType.SCHEMA); - - tx.commit(); - }, Transaction.Type.SCHEMA); - - localhostTypeDBTX(tx -> { - QueryAnswer answer = tx.query("match entity $x;").resolve(); - assertTrue(answer.isConceptRows()); - assertFalse(answer.isConceptDocuments()); - assertEquals(answer.getQueryType(), QueryType.READ); - - List rows = answer.asConceptRows().stream().collect(Collectors.toList()); - assertEquals(rows.size(), 1); - - ConceptRow row = rows.get(0); - List header = row.columnNames().collect(Collectors.toList()); - assertEquals(header.size(), 1); - - String columnName = header.get(0); - Concept conceptByName = row.get(columnName); - Concept conceptByIndex = row.getIndex(0); - assertEquals(conceptByName, conceptByIndex); - - assertTrue(conceptByName.isEntityType()); - assertFalse(conceptByName.isEntity()); - assertFalse(conceptByName.isAttributeType()); - assertTrue(conceptByName.isType()); - assertFalse(conceptByName.isInstance()); - assertEquals(conceptByName.asEntityType().getLabel(), "person"); - assertNotEquals(conceptByName.asEntityType().getLabel(), "not person"); - assertNotEquals(conceptByName.asEntityType().getLabel(), "age"); - }, Transaction.Type.READ); - - localhostTypeDBTX(tx -> { - QueryAnswer answer = tx.query("match attribute $a;").resolve(); - assertTrue(answer.isConceptRows()); - assertEquals(answer.getQueryType(), QueryType.READ); - - List rows = answer.asConceptRows().stream().collect(Collectors.toList()); - assertEquals(rows.size(), 1); - - ConceptRow row = rows.get(0); - List header = row.columnNames().collect(Collectors.toList()); - assertEquals(header.size(), 1); - - String columnName = header.get(0); - Concept conceptByName = row.get(columnName); - Concept conceptByIndex = row.getIndex(0); - assertEquals(conceptByName, conceptByIndex); - - assertTrue(conceptByName.isAttributeType()); - assertFalse(conceptByName.isAttribute()); - assertFalse(conceptByName.isEntityType()); - assertTrue(conceptByName.isType()); - assertFalse(conceptByName.isInstance()); - assertFalse(conceptByName.asAttributeType().isBoolean()); - assertFalse(conceptByName.asAttributeType().isStruct()); - assertFalse(conceptByName.asAttributeType().isString()); - assertFalse(conceptByName.asAttributeType().isDecimal()); - assertFalse(conceptByName.asAttributeType().isDouble()); - assertTrue(conceptByName.asAttributeType().isLong()); - assertEquals(conceptByName.asAttributeType().getLabel(), "age"); - assertNotEquals(conceptByName.asAttributeType().getLabel(), "person"); - }, Transaction.Type.READ); - - localhostTypeDBTX(tx -> { - QueryAnswer answer = tx.query("insert $z isa person, has age 10; $x isa person, has age 20;").resolve(); - assertTrue(answer.isConceptRows()); - assertEquals(answer.getQueryType(), QueryType.WRITE); - - List rows = answer.asConceptRows().stream().collect(Collectors.toList()); - assertEquals(rows.size(), 1); - - ConceptRow row = rows.get(0); - List header = row.columnNames().collect(Collectors.toList()); - assertEquals(header.size(), 2); - assertTrue(header.contains("x")); - assertTrue(header.contains("z")); - - Concept x = row.getIndex(header.indexOf("x")); - assertTrue(x.isEntity()); - assertFalse(x.isEntityType()); - assertFalse(x.isAttribute()); - assertFalse(x.isType()); - assertTrue(x.isInstance()); - assertEquals(x.asEntity().getType().asEntityType().getLabel(), "person"); - assertNotEquals(x.asEntity().getType().asEntityType().getLabel(), "not person"); - - Concept z = row.get("z"); - assertTrue(z.isEntity()); - assertFalse(z.isEntityType()); - assertFalse(z.isAttribute()); - assertFalse(z.isType()); - assertTrue(z.isInstance()); - Entity zEntity = z.asEntity(); - assertEquals(zEntity.getType().asEntityType().getLabel(), "person"); - assertNotEquals(zEntity.getType().asEntityType().getLabel(), "not person"); - - tx.commit(); - }, Transaction.Type.WRITE); - - localhostTypeDBTX(tx -> { - String var = "x"; - QueryAnswer matchAnswer = tx.query(String.format("match $%s isa person;", var)).resolve(); - assertTrue(matchAnswer.isConceptRows()); - assertEquals(matchAnswer.getQueryType(), QueryType.READ); - - AtomicInteger matchCount = new AtomicInteger(0); - matchAnswer.asConceptRows().stream().forEach(row -> { - assertEquals(row.getQueryType(), QueryType.READ); - Concept x = row.get(var); - assertTrue(x.isEntity()); - assertFalse(x.isEntityType()); - assertFalse(x.isAttribute()); - assertFalse(x.isType()); - assertTrue(x.isInstance()); - EntityType xType = x.asEntity().getType().asEntityType(); - assertEquals(xType.getLabel(), "person"); - assertNotEquals(xType.getLabel(), "not person"); - matchCount.incrementAndGet(); - }); - assertEquals(matchCount.get(), 2); - - QueryAnswer fetchAnswer = tx.query("match" + - " $x isa! person, has $a;" + - " $a isa! $t;" + - "fetch {" + - " \"single attribute type\": $t," + - " \"single attribute\": $a," + - " \"all attributes\": { $x.* }," + - "};").resolve(); - assertTrue(fetchAnswer.isConceptDocuments()); - assertEquals(fetchAnswer.getQueryType(), QueryType.READ); - - // TODO: It will become a mature test when all the other tests are refactored in Java - System.out.println("Fetch results for manual testing:"); - AtomicInteger fetchCount = new AtomicInteger(0); - fetchAnswer.asConceptDocuments().stream().forEach(document -> { - assertNotNull(document); - System.out.println(document); - fetchCount.incrementAndGet(); - }); - assertEquals(fetchCount.get(), 2); - }, Transaction.Type.READ); - } - - @Test - public void attributesTest() { + public void attributes() { Database db = typedbDriver.databases().get(DB_NAME); db.delete(); typedbDriver.databases().create(DB_NAME); @@ -359,8 +193,7 @@ public void attributesTest() { assertEquals(expected, value.asDatetimeTZ()); checked.incrementAndGet(); } else if (value.isDuration()) { - String[] expectedValue = attributeValues.get(attributeName).split("T"); - assertEquals(new Duration(java.time.Period.parse(expectedValue[0]), java.time.Duration.parse("PT" + expectedValue[1])), value.asDuration()); + assertEquals(Duration.parse(attributeValues.get(attributeName)), value.asDuration()); checked.incrementAndGet(); } // TODO: Add structs @@ -370,6 +203,136 @@ public void attributesTest() { }, Transaction.Type.READ); } + @Test + public void duration() { + // parse examples do not fail + Duration.parse("P1Y10M7DT15H44M5.00394892S"); + Duration.parse("P55W"); + + localhostTypeDBTX(tx -> { + tx.query("define attribute d, value duration;").resolve(); + tx.commit(); + }, Transaction.Type.SCHEMA); + + localhostTypeDBTX(tx -> { + QueryAnswer answer = tx.query("insert $d P1Y isa d;").resolve(); + Duration typedbDuration = answer.asConceptRows().next().get("d").asAttribute().asDuration(); + assertEquals("P12M PT0S", typedbDuration.toString()); // we just reuse the java's classes + assertEquals(12, typedbDuration.getMonths()); + assertEquals(0, typedbDuration.getDays()); + assertEquals(0, typedbDuration.getSeconds()); + assertEquals(0, typedbDuration.getNano()); + assertEquals(Duration.parse("P1Y0M0DT0H0M0S"), typedbDuration); + assertEquals(Duration.parse("P0Y12M0DT0H0M0S"), typedbDuration); + assertNotEquals(Duration.parse("P0Y1M0DT0H0M0S"), typedbDuration); + }, Transaction.Type.WRITE); + + localhostTypeDBTX(tx -> { + QueryAnswer answer = tx.query("insert $d P1M isa d;").resolve(); + Duration typedbDuration = answer.asConceptRows().next().get("d").asAttribute().asDuration(); + assertEquals("P1M PT0S", typedbDuration.toString()); // we just reuse the java's classes + assertEquals(1, typedbDuration.getMonths()); + assertEquals(0, typedbDuration.getDays()); + assertEquals(0, typedbDuration.getSeconds()); + assertEquals(0, typedbDuration.getNano()); + assertEquals(Duration.parse("P0Y1M0DT0H0M0S"), typedbDuration); + assertNotEquals(Duration.parse("P0Y0M31DT0H0M0S"), typedbDuration); + assertNotEquals(Duration.parse("P0Y0M30DT0H0M0S"), typedbDuration); + assertNotEquals(Duration.parse("P0Y0M29DT0H0M0S"), typedbDuration); + assertNotEquals(Duration.parse("P0Y0M28DT0H0M0S"), typedbDuration); + }, Transaction.Type.WRITE); + + localhostTypeDBTX(tx -> { + QueryAnswer answer = tx.query("insert $d P1D isa d;").resolve(); + Duration typedbDuration = answer.asConceptRows().next().get("d").asAttribute().asDuration(); + assertEquals("P1D PT0S", typedbDuration.toString()); // we just reuse the java's classes + assertEquals(0, typedbDuration.getMonths()); + assertEquals(1, typedbDuration.getDays()); + assertEquals(0, typedbDuration.getSeconds()); + assertEquals(0, typedbDuration.getNano()); + assertEquals(Duration.parse("P0Y0M1DT0H0M0S"), typedbDuration); + }, Transaction.Type.WRITE); + + localhostTypeDBTX(tx -> { + QueryAnswer answer = tx.query("insert $d P0DT1H isa d;").resolve(); + Duration typedbDuration = answer.asConceptRows().next().get("d").asAttribute().asDuration(); + assertEquals("P0D PT1H", typedbDuration.toString()); // we just reuse the java's classes + assertEquals(0, typedbDuration.getMonths()); + assertEquals(0, typedbDuration.getDays()); + assertEquals(3600, typedbDuration.getSeconds()); + assertEquals(0, typedbDuration.getNano()); + assertEquals(Duration.parse("P0Y0M0DT1H0M0S"), typedbDuration); + }, Transaction.Type.WRITE); + + localhostTypeDBTX(tx -> { + QueryAnswer answer = tx.query("insert $d P0DT1S isa d;").resolve(); + Duration typedbDuration = answer.asConceptRows().next().get("d").asAttribute().asDuration(); + assertEquals("P0D PT1S", typedbDuration.toString()); // we just reuse the java's classes + assertEquals(0, typedbDuration.getMonths()); + assertEquals(0, typedbDuration.getDays()); + assertEquals(1, typedbDuration.getSeconds()); + assertEquals(0, typedbDuration.getNano()); + assertEquals(Duration.parse("P0Y0M0DT0H0M1S"), typedbDuration); + }, Transaction.Type.WRITE); + + localhostTypeDBTX(tx -> { + QueryAnswer answer = tx.query("insert $d P0DT0.000000001S isa d;").resolve(); + Duration typedbDuration = answer.asConceptRows().next().get("d").asAttribute().asDuration(); + assertEquals("P0D PT0.000000001S", typedbDuration.toString()); // we just reuse the java's classes + assertEquals(0, typedbDuration.getMonths()); + assertEquals(0, typedbDuration.getDays()); + assertEquals(0, typedbDuration.getSeconds()); + assertEquals(1, typedbDuration.getNano()); + assertEquals(Duration.parse("P0DT0.000000001S"), typedbDuration); + }, Transaction.Type.WRITE); + + localhostTypeDBTX(tx -> { + QueryAnswer answer = tx.query("insert $d P0DT0.0000001S isa d;").resolve(); + Duration typedbDuration = answer.asConceptRows().next().get("d").asAttribute().asDuration(); + assertEquals("P0D PT0.0000001S", typedbDuration.toString()); // we just reuse the java's classes + assertEquals(0, typedbDuration.getMonths()); + assertEquals(0, typedbDuration.getDays()); + assertEquals(0, typedbDuration.getSeconds()); + assertEquals(100, typedbDuration.getNano()); + assertEquals(Duration.parse("P0DT0.0000001S"), typedbDuration); + }, Transaction.Type.WRITE); + + localhostTypeDBTX(tx -> { + QueryAnswer answer = tx.query("insert $d P0DT0S isa d;").resolve(); + Duration typedbDuration = answer.asConceptRows().next().get("d").asAttribute().asDuration(); + assertEquals("P0D PT0S", typedbDuration.toString()); // we just reuse the java's classes + assertEquals(0, typedbDuration.getMonths()); + assertEquals(0, typedbDuration.getDays()); + assertEquals(0, typedbDuration.getSeconds()); + assertEquals(0, typedbDuration.getNano()); + assertEquals(Duration.parse("P0DT0S"), typedbDuration); + assertEquals(Duration.parse("P0W"), typedbDuration); + }, Transaction.Type.WRITE); + + localhostTypeDBTX(tx -> { + QueryAnswer answer = tx.query("insert $d P7W isa d;").resolve(); + Duration typedbDuration = answer.asConceptRows().next().get("d").asAttribute().asDuration(); + assertEquals("P49D PT0S", typedbDuration.toString()); // we just reuse the java's classes + assertEquals(0, typedbDuration.getMonths()); + assertEquals(49, typedbDuration.getDays()); + assertEquals(0, typedbDuration.getSeconds()); + assertEquals(0, typedbDuration.getNano()); + assertEquals(Duration.parse("P7W"), typedbDuration); + assertEquals(Duration.parse("P0Y0M49DT0H0M0S"), typedbDuration); + }, Transaction.Type.WRITE); + + localhostTypeDBTX(tx -> { + QueryAnswer answer = tx.query("insert $d P999Y12M31DT24H59M59.999999999S isa d;").resolve(); + Duration typedbDuration = answer.asConceptRows().next().get("d").asAttribute().asDuration(); + assertEquals("P12000M31D PT24H59M59.999999999S", typedbDuration.toString()); // we just reuse the java's classes + assertEquals(12000, typedbDuration.getMonths()); + assertEquals(31, typedbDuration.getDays()); + assertEquals(89999, typedbDuration.getSeconds()); + assertEquals(999999999, typedbDuration.getNano()); + assertEquals(Duration.parse("P999Y12M31DT24H59M59.999999999S"), typedbDuration); + }, Transaction.Type.WRITE); + } + private void localhostTypeDBTX(Consumer fn, Transaction.Type type/*, Options options*/) { try (Transaction transaction = typedbDriver.transaction(DB_NAME, type/*, options*/)) { fn.accept(transaction); diff --git a/python/README.md b/python/README.md index 6565566dc1..39200ff728 100644 --- a/python/README.md +++ b/python/README.md @@ -74,7 +74,7 @@ from typedb.driver import * print(f"OK results do not give any extra interesting information, but they mean that the query " f"is successfully executed!") - # Commit automatically closes the transaction. You can still safely call for it inside "with" blocks + # Commit automatically closes the transaction. It can still be safely called inside "with" blocks tx.commit() # Open a read transaction to safely read anything without database modifications diff --git a/python/core_example b/python/core_example index a8cac45fb5..b7ce32518b 100644 --- a/python/core_example +++ b/python/core_example @@ -40,7 +40,7 @@ from typedb.driver import * print(f"OK results do not give any extra interesting information, but they mean that the query " f"is successfully executed!") - # Commit automatically closes the transaction. You can still safely call for it inside "with" blocks + # Commit automatically closes the transaction. It can still be safely called inside "with" blocks tx.commit() # Open a read transaction to safely read anything without database modifications diff --git a/python/tests/behaviour/config/parameters.py b/python/tests/behaviour/config/parameters.py index 4ea4f10e31..56aa5bd6c1 100644 --- a/python/tests/behaviour/config/parameters.py +++ b/python/tests/behaviour/config/parameters.py @@ -267,6 +267,19 @@ def parse_is_or_not(value: str) -> bool: register_type(IsOrNot=parse_is_or_not) +@parse.with_pattern("contains|does not contain") +def parse_contains_or_doesnt(value: str) -> bool: + if value == "contains": + return True + elif value == "does not contain": + return False + else: + raise ValueError(f"Unrecognised ContainsOrDoesnt: {value}") + + +register_type(ContainsOrDoesnt=parse_contains_or_doesnt) + + def is_or_not_reason(is_or_not: bool, real, expected) -> str: intro_str = "Expected that real value" is_or_not_str = "is" if is_or_not else "is not" diff --git a/python/tests/behaviour/query/query_steps.py b/python/tests/behaviour/query/query_steps.py index 7992d98460..acb3430d6f 100644 --- a/python/tests/behaviour/query/query_steps.py +++ b/python/tests/behaviour/query/query_steps.py @@ -14,7 +14,7 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. - +import json from concurrent.futures.thread import ThreadPoolExecutor from datetime import datetime from decimal import Decimal @@ -26,6 +26,7 @@ from tests.behaviour.config.parameters import ConceptKind, MayError, ValueType, parse_bool, parse_list, \ try_parse_value_type, is_or_not_reason from tests.behaviour.context import Context +from tests.behaviour.util.util import list_contains_json from typedb.api.answer.query_type import QueryType from typedb.api.concept.concept import Concept from typedb.api.concept.instance.attribute import Attribute @@ -78,7 +79,7 @@ def step_impl(context: Context, is_or_not: bool, answer_type: str): assert_that(context.answer.is_ok(), is_(is_or_not), "Expected is_ok()") elif answer_type == "concept rows": assert_that(context.answer.is_concept_rows(), is_(is_or_not), "Expected is_concept_rows()") - elif answer_type == "concept trees": + elif answer_type == "concept documents": assert_that(context.answer.is_concept_documents(), is_(is_or_not), "Expected is_concept_documents()") @@ -90,7 +91,7 @@ def unwrap_answer_rows(context: Context): context.unwrapped_answer = context.answer.as_concept_rows() -def unwrap_answer_trees(context: Context): +def unwrap_answer_documents(context: Context): context.unwrapped_answer = context.answer.as_concept_documents() @@ -104,9 +105,9 @@ def step_impl(context: Context, may_error: MayError): may_error.check(lambda: unwrap_answer_rows(context)) -@step("answer unwraps as concept trees{may_error:MayError}") +@step("answer unwraps as concept documents{may_error:MayError}") def step_impl(context: Context, may_error: MayError): - may_error.check(lambda: unwrap_answer_trees(context)) + may_error.check(lambda: unwrap_answer_documents(context)) def unwrap_answer_if_needed(context: Context): @@ -119,7 +120,7 @@ def unwrap_answer_if_needed(context: Context): elif context.answer.is_concept_rows(): unwrap_answer_rows(context) elif context.answer.is_concept_documents(): - unwrap_answer_trees(context) + unwrap_answer_documents(context) else: raise ValueError( "Cannot unwrap answer: it should be in Ok/ConceptRows/ConceptDocuments, but appeared to be something else") @@ -148,26 +149,21 @@ def collect_answer_if_needed(context: Context): unwrap_answer_if_needed(context) if context.collected_answer is None: - if context.unwrapped_answer.is_concept_rows(): + if context.unwrapped_answer.is_concept_rows() or context.unwrapped_answer.is_concept_documents(): context.collected_answer = list(context.unwrapped_answer) else: raise NotImplemented("Cannot collect answer") -def assert_answer_size(answer, size: int): - assert_that(len(answer), is_(size)) - - @step("answer size is: {size:Int}") def step_impl(context: Context, size: int): collect_answer_if_needed(context) - assert_answer_size(context.collected_answer, size) + assert_that(len(context.collected_answer), is_(size)) @step("answer query type {is_or_not:IsOrNot}: {query_type:QueryType}") def step_impl(context: Context, is_or_not: bool, query_type: QueryType): - collect_answer_if_needed(context) - answer_query_type = context.collected_answer[0].query_type + answer_query_type = context.answer.query_type assert_that(answer_query_type == query_type, is_(is_or_not), is_or_not_reason(is_or_not, real=answer_query_type, expected=query_type)) @@ -197,6 +193,12 @@ def step_impl(context: Context): assert_that(sorted(list(column_names)), equal_to(sorted(parse_list(context.table)))) +def get_row_get_concepts(context: Context, row_index: int) -> [Concept]: + collect_answer_if_needed(context) + row = context.collected_answer[row_index] + return list(row.concepts()) + + def get_row_get_concept(context: Context, row_index: int, var: str, is_by_var_index: bool) -> Concept: collect_answer_if_needed(context) row = context.collected_answer[row_index] @@ -271,6 +273,14 @@ def get_row_get_concept_of_kind(context: Context, row_index: int, var: str, is_b raise ValueError(f"Not all ConceptKind variants are covered! Found {kind}") +@step("answer get row({row_index:Int}) query type {is_or_not:IsOrNot}: {query_type:QueryType}") +def step_impl(context: Context, row_index: int, is_or_not: bool, query_type: QueryType): + collect_answer_if_needed(context) + answer_query_type = context.collected_answer[row_index].query_type + assert_that(answer_query_type == query_type, is_(is_or_not), + is_or_not_reason(is_or_not, real=answer_query_type, expected=query_type)) + + @step("answer get row({row_index:Int}) get variable{is_by_var_index:IsByVarIndex}({var:Var}){may_error:MayError}") def step_impl(context: Context, row_index: int, is_by_var_index: bool, var: str, may_error: MayError): may_error.check(lambda: get_row_get_concept(context, row_index, var, is_by_var_index)) @@ -526,10 +536,15 @@ def step_impl(context: Context, row_index: int, kind: ConceptKind, is_by_var_ind @step( - "answer get row({row_index:Int}) get {kind:ConceptKind}{is_by_var_index:IsByVarIndex}({var:Var}) get iid exists") -def step_impl(context: Context, row_index: int, kind: ConceptKind, is_by_var_index: bool, var: str): + "answer get row({row_index:Int}) get {kind:ConceptKind}{is_by_var_index:IsByVarIndex}({var:Var}) {contains_or_doesnt:ContainsOrDoesnt} iid") +def step_impl(context: Context, row_index: int, kind: ConceptKind, is_by_var_index: bool, var: str, + contains_or_doesnt: bool): concept_iid = get_row_get_concept_of_kind(context, row_index, var, is_by_var_index, kind).get_iid() - assert_that(concept_iid, is_not(None)) + if contains_or_doesnt: + assert_that(concept_iid, is_not(None)) + assert_that(concept_iid, is_not("")) + else: + assert_that(concept_iid, is_(None)) def parse_expected_value(value: str, value_type: Optional[ValueType]): @@ -644,3 +659,18 @@ def step_impl(context: Context, row_index: int, is_by_var_index: bool, var: str, expected_value = parse_expected_value(value, value_type) assert_that(real_value_as_value_type == expected_value, is_(is_or_not), is_or_not_reason(is_or_not, real=real_value_as_value_type, expected=expected_value)) + + +@step("answer get row({row_index:Int}) get concepts size is: {size:Int}") +def step_impl(context: Context, row_index: int, size: int): + collect_answer_if_needed(context) + assert_that(len(get_row_get_concepts(context, row_index)), is_(size)) + + +@step("answer {contains_or_doesnt:ContainsOrDoesnt} document") +def step_impl(context: Context, contains_or_doesnt: bool): + collect_answer_if_needed(context) + expected = json.loads(context.text) + assert_that(list_contains_json(context.collected_answer, expected), is_(contains_or_doesnt), + "expected?({}) document: {}\nin answer: {}".format(contains_or_doesnt, expected, + context.collected_answer)) diff --git a/python/tests/behaviour/util/util.py b/python/tests/behaviour/util/util.py index 1a4c2c6d1d..ffd61c6566 100644 --- a/python/tests/behaviour/util/util.py +++ b/python/tests/behaviour/util/util.py @@ -47,3 +47,10 @@ def json_matches(lhs, rhs) -> bool: return len(rhs_matches) == len(rhs) else: return lhs == rhs + + +def list_contains_json(json_list: list, json: dict) -> bool: + for json_from_list in json_list: + if json_matches(json_from_list, json): + return True + return False diff --git a/python/tests/integration/core/test_example.py b/python/tests/integration/core/test_example.py index 6bf098f9f8..d61c571a1f 100644 --- a/python/tests/integration/core/test_example.py +++ b/python/tests/integration/core/test_example.py @@ -74,13 +74,14 @@ def test_example(self): assert_that(answer.is_ok(), is_(True)) assert_that(answer.query_type, is_(QueryType.SCHEMA)) - # Commit automatically closes the transaction. You can still safely call for it inside "with" blocks + # Commit automatically closes the transaction. It can still be safely called inside "with" blocks tx.commit() # Open a read transaction to safely read anything without database modifications with driver.transaction(database.name, TransactionType.READ) as tx: answer = tx.query("match entity $x;").resolve() assert_that(answer.is_concept_rows(), is_(True)) + assert_that(answer.is_concept_documents(), is_(False)) assert_that(answer.query_type, is_(QueryType.READ)) # Collect concept rows that represent the answer as a table diff --git a/rust/README.md b/rust/README.md index f2bf76c2e4..f69fd81940 100644 --- a/rust/README.md +++ b/rust/README.md @@ -116,7 +116,7 @@ fn typedb_example() { let answer = transaction.query(define_query).await.unwrap(); // Work with the driver's enums in a classic way or using helper methods - if answer.is_ok() && matches!(answer, QueryAnswer::Ok()) { + if answer.is_ok() && matches!(answer, QueryAnswer::Ok(_)) { println!("OK results do not give any extra interesting information, but they mean that the query is successfully executed!"); } diff --git a/rust/core_example b/rust/core_example index 83b23f142d..7fc0e95498 100644 --- a/rust/core_example +++ b/rust/core_example @@ -51,7 +51,7 @@ fn typedb_example() { let answer = transaction.query(define_query).await.unwrap(); // Work with the driver's enums in a classic way or using helper methods - if answer.is_ok() && matches!(answer, QueryAnswer::Ok()) { + if answer.is_ok() && matches!(answer, QueryAnswer::Ok(_)) { println!("OK results do not give any extra interesting information, but they mean that the query is successfully executed!"); } diff --git a/rust/src/answer/mod.rs b/rust/src/answer/mod.rs index 1421cc0927..91f375227e 100644 --- a/rust/src/answer/mod.rs +++ b/rust/src/answer/mod.rs @@ -33,7 +33,7 @@ mod json; mod value_group; pub enum QueryAnswer { - Ok(), + Ok(QueryType), ConceptRowStream(Arc, BoxStream<'static, Result>), ConceptDocumentStream(Arc, BoxStream<'static, Result>), } @@ -48,7 +48,7 @@ impl QueryAnswer { /// ``` pub fn get_query_type(&self) -> QueryType { match &self { - QueryAnswer::Ok() => QueryType::SchemaQuery, // TODO: Get it from protocol + QueryAnswer::Ok(query_type) => query_type.clone(), QueryAnswer::ConceptRowStream(header, _) => header.query_type, QueryAnswer::ConceptDocumentStream(header, _) => header.query_type, } @@ -62,7 +62,7 @@ impl QueryAnswer { /// query_answer.is_ok() /// ``` pub fn is_ok(&self) -> bool { - matches!(self, Self::Ok()) + matches!(self, Self::Ok(_)) } /// Check if the QueryAnswer is a ConceptRowStream. @@ -123,7 +123,7 @@ impl QueryAnswer { impl fmt::Debug for QueryAnswer { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - QueryAnswer::Ok() => write!(f, "QueryAnswer::Ok"), + QueryAnswer::Ok(_) => write!(f, "QueryAnswer::Ok"), QueryAnswer::ConceptRowStream(_, _) => write!(f, "QueryAnswer::ConceptRowStream()"), QueryAnswer::ConceptDocumentStream(_, _) => write!(f, "QueryAnswer::ConceptDocumentStream()"), } diff --git a/rust/src/connection/message.rs b/rust/src/connection/message.rs index 2b69d9e579..9e876a799a 100644 --- a/rust/src/connection/message.rs +++ b/rust/src/connection/message.rs @@ -28,6 +28,7 @@ use crate::{ answer::{ concept_document::{ConceptDocumentHeader, Node}, concept_row::ConceptRowHeader, + QueryType, }, common::{address::Address, info::DatabaseInfo, RequestID}, concept::Concept, @@ -145,7 +146,7 @@ pub(super) enum QueryRequest { #[derive(Debug)] pub(super) enum QueryResponse { - Ok(), + Ok(QueryType), ConceptRowsHeader(ConceptRowHeader), ConceptDocumentsHeader(ConceptDocumentHeader), StreamConceptRows(Vec>>), diff --git a/rust/src/connection/network/proto/message.rs b/rust/src/connection/network/proto/message.rs index 9cc338400d..985c46cb7a 100644 --- a/rust/src/connection/network/proto/message.rs +++ b/rust/src/connection/network/proto/message.rs @@ -354,7 +354,9 @@ impl TryFromProto for QueryResponse { impl TryFromProto for QueryResponse { fn try_from_proto(proto: typedb_protocol::query::initial_res::ok::Ok) -> Result { match proto { - typedb_protocol::query::initial_res::ok::Ok::Empty(_) => Ok(QueryResponse::Ok()), + typedb_protocol::query::initial_res::ok::Ok::Done(done_header) => { + Ok(QueryResponse::Ok(QueryType::try_from_proto(done_header.query_type)?)) + } typedb_protocol::query::initial_res::ok::Ok::ConceptDocumentStream(document_stream_header) => { Ok(QueryResponse::ConceptDocumentsHeader(ConceptDocumentHeader { query_type: QueryType::try_from_proto(document_stream_header.query_type)?, diff --git a/rust/src/connection/transaction_stream.rs b/rust/src/connection/transaction_stream.rs index 521b609570..c561841f6d 100644 --- a/rust/src/connection/transaction_stream.rs +++ b/rust/src/connection/transaction_stream.rs @@ -104,7 +104,7 @@ impl TransactionStream { }; match header { - QueryResponse::Ok() => Ok(QueryAnswer::Ok()), + QueryResponse::Ok(query_type) => Ok(QueryAnswer::Ok(query_type)), QueryResponse::ConceptDocumentsHeader(documents_header) => { let header = Arc::new(documents_header); let stream_header = header.clone(); diff --git a/rust/tests/behaviour/steps/lib.rs b/rust/tests/behaviour/steps/lib.rs index d3cd37c2c7..03bfc9a4af 100644 --- a/rust/tests/behaviour/steps/lib.rs +++ b/rust/tests/behaviour/steps/lib.rs @@ -38,12 +38,14 @@ use futures::{ use itertools::Itertools; use tokio::time::{sleep, Duration}; use typedb_driver::{ - answer::{ConceptRow, QueryAnswer, JSON}, + answer::{ConceptDocument, ConceptRow, QueryAnswer, QueryType, JSON}, concept::Value, BoxStream, Credential, Database, DatabaseManager, Options, Result as TypeDBResult, Transaction, TypeDBDriver, UserManager, }; +use crate::params::QueryAnswerType; + mod connection; mod params; mod query; @@ -99,11 +101,12 @@ pub struct Context { pub driver: Option, pub transactions: VecDeque, pub answer: Option, + pub answer_type: Option, + pub answer_query_type: Option, pub collected_rows: Option>, + pub collected_documents: Option>, pub concurrent_answers: Vec, pub concurrent_rows_streams: Option>>>, - pub fetch_answer: Option, - pub value_answer: Option>, } impl fmt::Debug for Context { @@ -115,14 +118,15 @@ impl fmt::Debug for Context { .field("driver", &self.driver) .field("transactions", &self.transactions) .field("answer", &self.answer) + .field("answer_type", &self.answer_type) + .field("answer_query_type", &self.answer_query_type) .field("collected_rows", &self.collected_rows) + .field("collected_documents", &self.collected_documents) .field("concurrent_answers", &self.concurrent_answers) .field( "concurrent_rows_streams", &self.concurrent_rows_streams.as_ref().map(|streams| format!("{} streams", streams.len())), ) - .field("fetch_answer", &self.fetch_answer) - .field("value_answer", &self.value_answer) .finish() } } @@ -233,9 +237,10 @@ impl Context { pub async fn cleanup_answers(&mut self) { self.answer = None; + self.answer_type = None; + self.answer_query_type = None; self.collected_rows = None; - self.fetch_answer = None; - self.value_answer = None; + self.collected_documents = None; } pub async fn cleanup_concurrent_answers(&mut self) { @@ -270,7 +275,14 @@ impl Context { } pub fn set_answer(&mut self, answer: TypeDBResult) -> TypeDBResult { - self.answer = Some(answer?); + let answer = answer?; + self.answer_query_type = Some(answer.get_query_type()); + self.answer_type = Some(match &answer { + QueryAnswer::Ok(_) => QueryAnswerType::Ok, + QueryAnswer::ConceptRowStream(_, _) => QueryAnswerType::ConceptRows, + QueryAnswer::ConceptDocumentStream(_, _) => QueryAnswerType::ConceptDocuments, + }); + self.answer = Some(answer); Ok(()) } @@ -278,9 +290,13 @@ impl Context { self.concurrent_answers = answers; } - pub async fn unwrap_answer_into_rows_if_needed(&mut self) { - if self.collected_rows.is_none() { - self.unwrap_answer_into_rows().await + pub async fn unwrap_answer_if_needed(&mut self) { + if self.collected_rows.is_none() && self.collected_documents.is_none() { + match self.answer_type.expect("Nothing to unwrap: no answer") { + QueryAnswerType::Ok => panic!("Nothing to unwrap: cannot unwrap Ok"), + QueryAnswerType::ConceptRows => self.unwrap_answer_into_rows().await, + QueryAnswerType::ConceptDocuments => self.unwrap_answer_into_documents().await, + } } } @@ -300,8 +316,25 @@ impl Context { Some(self.concurrent_answers.drain(..).map(|answer| answer.into_rows()).collect()); } + pub async fn unwrap_answer_into_documents(&mut self) { + self.collected_documents = + Some(self.answer.take().unwrap().into_documents().map(|result| result.unwrap()).collect::>().await); + } + + pub async fn get_answer(&self) -> Option<&QueryAnswer> { + self.answer.as_ref() + } + + pub async fn get_answer_query_type(&self) -> Option { + self.answer_query_type + } + + pub async fn get_answer_type(&self) -> Option { + self.answer_type + } + pub async fn try_get_collected_rows(&mut self) -> Option<&Vec> { - self.unwrap_answer_into_rows_if_needed().await; + self.unwrap_answer_if_needed().await; self.collected_rows.as_ref() } @@ -309,8 +342,17 @@ impl Context { self.try_get_collected_rows().await.unwrap() } + pub async fn try_get_collected_documents(&mut self) -> Option<&Vec> { + self.unwrap_answer_if_needed().await; + self.collected_documents.as_ref() + } + + pub async fn get_collected_documents(&mut self) -> &Vec { + self.try_get_collected_documents().await.unwrap() + } + pub async fn get_collected_answer_row_index(&mut self, index: usize) -> &ConceptRow { - self.unwrap_answer_into_rows_if_needed().await; + self.unwrap_answer_if_needed().await; self.collected_rows.as_ref().unwrap().get(index).unwrap() } @@ -386,11 +428,12 @@ impl Default for Context { driver: None, transactions: VecDeque::new(), answer: None, + answer_type: None, + answer_query_type: None, collected_rows: None, + collected_documents: None, concurrent_answers: Vec::new(), concurrent_rows_streams: None, - fetch_answer: None, - value_answer: None, } } } diff --git a/rust/tests/behaviour/steps/params.rs b/rust/tests/behaviour/steps/params.rs index 0c990ddbd3..9efef00cdf 100644 --- a/rust/tests/behaviour/steps/params.rs +++ b/rust/tests/behaviour/steps/params.rs @@ -408,18 +408,18 @@ impl FromStr for IsOrNot { } #[derive(Debug, Parameter)] -#[param(name = "exists_or_doesnt", regex = "(exists|does not exist)")] -pub(crate) enum ExistsOrDoesnt { - Exists, - DoesNotExist, +#[param(name = "contains_or_doesnt", regex = "(contains|does not contain)")] +pub(crate) enum ContainsOrDoesnt { + Contains, + DoesNotContain, } -impl ExistsOrDoesnt { +impl ContainsOrDoesnt { pub fn check(&self, scrutinee: &Option, message: &str) { match (self, scrutinee) { - (Self::Exists, Some(_)) | (Self::DoesNotExist, None) => (), - (Self::Exists, None) => panic!("{message} does not exist"), - (Self::DoesNotExist, Some(value)) => panic!("{message} exists: {value:?}"), + (Self::Contains, Some(_)) | (Self::DoesNotContain, None) => (), + (Self::Contains, None) => panic!("Expected to contain, not found: {message}"), + (Self::DoesNotContain, Some(value)) => panic!("Expected not to contain, {value:?} is found: {message}"), } } @@ -430,15 +430,22 @@ impl ExistsOrDoesnt { }; self.check(&option, message) } + + pub fn check_bool(&self, contains: bool, message: &str) { + match self { + ContainsOrDoesnt::Contains => assert!(contains, "Expected to contain, not found: {message}"), + ContainsOrDoesnt::DoesNotContain => assert!(!contains, "Expected not to contain, but found: {message}"), + } + } } -impl FromStr for ExistsOrDoesnt { +impl FromStr for ContainsOrDoesnt { type Err = String; fn from_str(s: &str) -> Result { Ok(match s { - "exists" => Self::Exists, - "does not exist" => Self::DoesNotExist, - invalid => return Err(format!("Invalid `ExistsOrDoesnt`: {invalid}")), + "contains" => Self::Contains, + "does not contain" => Self::DoesNotContain, + invalid => return Err(format!("Invalid `ContainsOrDoesnt`: {invalid}")), }) } } @@ -461,8 +468,8 @@ impl FromStr for IsByVarIndex { } } -#[derive(Debug, Parameter)] -#[param(name = "query_answer_type", regex = "(ok|concept rows|concept trees)")] +#[derive(Debug, Clone, Copy, Parameter)] +#[param(name = "query_answer_type", regex = "(ok|concept rows|concept documents)")] pub(crate) enum QueryAnswerType { Ok, ConceptRows, @@ -475,7 +482,7 @@ impl FromStr for QueryAnswerType { Ok(match s { "ok" => Self::Ok, "concept rows" => Self::ConceptRows, - "concept trees" => Self::ConceptDocuments, // TODO: "concept documents" + "concept documents" => Self::ConceptDocuments, invalid => return Err(format!("Invalid `QueryAnswerType`: {invalid}")), }) } diff --git a/rust/tests/behaviour/steps/query.rs b/rust/tests/behaviour/steps/query.rs index 09ff78f518..4f387b31d8 100644 --- a/rust/tests/behaviour/steps/query.rs +++ b/rust/tests/behaviour/steps/query.rs @@ -33,7 +33,7 @@ use crate::{ assert_err, generic_step, params, params::check_boolean, util, - util::iter_table, + util::{iter_table, list_contains_json, parse_json}, BehaviourTestOptionalError, BehaviourTestOptionalError::{InvalidValueCasting, VariableDoesNotExist}, Context, @@ -229,7 +229,7 @@ pub async fn answer_unwraps_as( ) { let expect = !may_error.expects_error(); match context.answer.as_ref().unwrap() { - QueryAnswer::Ok() => { + QueryAnswer::Ok(_) => { assert_eq!( expect, matches!(query_answer_type, params::QueryAnswerType::Ok), @@ -257,8 +257,10 @@ pub async fn answer_unwraps_as( #[step(expr = r"answer size is: {int}")] pub async fn answer_size_is(context: &mut Context, size: usize) { let actual_size = match context.try_get_collected_rows().await { - // When trees are implemented: match context.try_get_collected_trees() -> ... - None => panic!("No collected answer to check its size"), + None => match context.try_get_collected_documents().await { + None => panic!("No collected answer to check its size"), + Some(documents) => documents.len(), + }, Some(rows) => rows.len(), }; assert_eq!(actual_size, size, "Expected {size} answers, got {actual_size}"); @@ -307,7 +309,19 @@ pub async fn answer_column_names_are(context: &mut Context, step: &Step) { #[apply(generic_step)] #[step(expr = r"answer query type {is_or_not}: {query_type}")] pub async fn answer_query_type_is(context: &mut Context, is_or_not: params::IsOrNot, query_type: params::QueryType) { - let real_query_type = context.get_collected_rows().await.get(0).unwrap().get_query_type(); + let real_query_type = context.get_answer_query_type().await.unwrap(); + is_or_not.compare(real_query_type, query_type.query_type); +} + +#[apply(generic_step)] +#[step(expr = r"answer get row\({int}\) query type {is_or_not}: {query_type}")] +pub async fn answer_get_row_query_type_is( + context: &mut Context, + index: usize, + is_or_not: params::IsOrNot, + query_type: params::QueryType, +) { + let real_query_type = context.get_collected_answer_row_index(index).await.get_query_type(); is_or_not.compare(real_query_type, query_type.query_type); } @@ -408,18 +422,18 @@ pub async fn answer_get_row_get_variable_get_type_get_label( } #[apply(generic_step)] -#[step(expr = r"answer get row\({int}\) get {concept_kind}{is_by_var_index}\({var}\) get iid {exists_or_doesnt}")] +#[step(expr = r"answer get row\({int}\) get {concept_kind}{is_by_var_index}\({var}\) {contains_or_doesnt} iid")] pub async fn answer_get_row_get_variable_get_iid_exists( context: &mut Context, index: usize, var_kind: params::ConceptKind, is_by_var_index: params::IsByVarIndex, var: params::Var, - exists_or_doesnt: params::ExistsOrDoesnt, + contains_or_doesnt: params::ContainsOrDoesnt, ) { let concept = get_answer_rows_var(context, index, is_by_var_index, var).await.unwrap(); check_concept_is_kind(concept, var_kind, params::Boolean::True); - exists_or_doesnt.check(&concept.get_iid(), &format!("iid for concept {}", concept.get_label())); + contains_or_doesnt.check(&concept.get_iid(), &format!("iid for concept {}", concept.get_label())); } #[apply(generic_step)] @@ -769,3 +783,26 @@ pub async fn answer_get_row_get_variable_is_struct( check_concept_is_kind(concept, var_kind, params::Boolean::True); check_boolean!(is_struct, concept.is_struct()); } + +#[apply(generic_step)] +#[step(expr = r"answer get row\({int}\) get concepts size is: {int}")] +pub async fn answer_get_row_get_concepts_size_is(context: &mut Context, index: usize, size: usize) { + let concept_row = context.get_collected_answer_row_index(index).await; + assert_eq!(size, concept_row.get_concepts().collect_vec().len()); +} + +#[apply(generic_step)] +#[step(expr = r"answer {contains_or_doesnt} document:")] +pub async fn answer_contains_document( + context: &mut Context, + contains_or_doesnt: params::ContainsOrDoesnt, + step: &Step, +) { + let expected_document = parse_json(step.docstring().unwrap()).expect("Given docstring is not a JSON document!"); + let concept_documents = + context.get_collected_documents().await.clone().into_iter().map(|document| document.into_json()).collect_vec(); + contains_or_doesnt.check_bool( + list_contains_json(&concept_documents, &expected_document), + &format!("Concept documents: {:?}", concept_documents), + ); +} diff --git a/rust/tests/behaviour/steps/util.rs b/rust/tests/behaviour/steps/util.rs index 15f9f49673..e0bd3274e7 100644 --- a/rust/tests/behaviour/steps/util.rs +++ b/rust/tests/behaviour/steps/util.rs @@ -53,128 +53,11 @@ pub fn iter_table_map(step: &Step) -> impl Iterator> rows.iter().map(|row| keys.iter().zip(row).map(|(k, v)| (k.as_str(), v.as_str())).collect()) } -pub async fn match_answer_concept_map( - context: &Context, - answer_identifiers: &HashMap<&str, &str>, - answer: &ConceptRow, -) -> bool { - // stream::iter(answer_identifiers.keys()) - // .all(|key| async { - // answer.map.contains_key(*key) - // && match_answer_concept(context, answer_identifiers.get(key).unwrap(), answer.get(key).unwrap()).await - // }) - // .await - todo!() +pub fn list_contains_json(list: &Vec, json: &JSON) -> bool { + list.iter().any(|list_json| jsons_equal_up_to_reorder(list_json, json)) } -pub async fn match_answer_concept(context: &Context, answer_identifier: &str, answer: &Concept) -> bool { - let identifiers: Vec<&str> = answer_identifier.splitn(2, ':').collect(); - match identifiers[0] { - "key" => key_values_equal(context, identifiers[1], answer).await, - "label" => labels_equal(identifiers[1], answer), - "value" => values_equal(identifiers[1], answer), - "attr" => attribute_values_equal(identifiers[1], answer), - _ => unreachable!(), - } -} - -async fn key_values_equal(context: &Context, expected_label_and_value: &str, answer: &Concept) -> bool { - let identifiers: Vec<&str> = expected_label_and_value.splitn(2, ':').collect(); - assert_eq!(identifiers.len(), 2, "Unexpected table cell format: {expected_label_and_value}."); - // - // let res = match answer { - // Concept::Entity(entity) => { - // async { entity.get_has(context.transaction(), vec![], vec![Annotation::Key]) } - // .and_then(|stream| async { stream.try_collect::>().await }) - // .await - // } - // Concept::Relation(rel) => { - // async { rel.get_has(context.transaction(), vec![], vec![Annotation::Key]) } - // .and_then(|stream| async { stream.try_collect::>().await }) - // .await - // } - // Concept::Attribute(attr) => { - // async { attr.get_has(context.transaction(), vec![], vec![Annotation::Key]) } - // .and_then(|stream| async { stream.try_collect::>().await }) - // .await - // } - // _ => unreachable!("Unexpected Concept type: {answer:?}"), - // }; - // match res { - // Ok(keys) => keys - // .into_iter() - // .find(|key| key.type_.label == identifiers[0]) - // .map(|attr| value_equals_str(&attr.value, identifiers[1])) - // .unwrap_or(false), - // Err(_) => false, - // } - todo!() -} - -fn labels_equal(expected_label: &str, answer: &Concept) -> bool { - match answer { - Concept::EntityType(EntityType { label, .. }) => expected_label == label, - Concept::RelationType(RelationType { label, .. }) => expected_label == label, - Concept::RoleType(RoleType { label, .. }) => expected_label == label.to_string(), - Concept::AttributeType(AttributeType { label, .. }) => expected_label == label, - _ => unreachable!(), - } -} - -fn attribute_values_equal(expected_label_and_value: &str, answer: &Concept) -> bool { - let identifiers: Vec<&str> = expected_label_and_value.splitn(2, ':').collect(); - assert_eq!(identifiers.len(), 2, "Unexpected table cell format: {expected_label_and_value}."); - let Concept::Attribute(Attribute { value, .. }) = answer else { unreachable!() }; - value_equals_str(value, identifiers[1]) -} - -fn values_equal(expected_label_and_value: &str, answer: &Concept) -> bool { - let identifiers: Vec<&str> = expected_label_and_value.splitn(2, ':').collect(); - assert_eq!(identifiers.len(), 2, "Unexpected table cell format: {expected_label_and_value}."); - let Concept::Value(value) = answer else { unreachable!() }; - value_equals_str(value, identifiers[1].trim()) -} - -fn value_equals_str(value: &Value, expected: &str) -> bool { - match value { - Value::String(val) => val == expected, - Value::Long(val) => expected.parse::().map(|expected| expected.eq(val)).unwrap_or(false), - Value::Double(val) => { - expected.parse::().map(|expected| equals_approximate(expected, *val)).unwrap_or(false) - } - Value::Boolean(val) => expected.parse::().map(|expected| expected.eq(val)).unwrap_or(false), - Value::Datetime(val) => { - if expected.contains(':') { - val == &NaiveDateTime::parse_from_str(expected, "%Y-%m-%dT%H:%M:%S").unwrap() - } else { - let my_date = NaiveDate::parse_from_str(expected, "%Y-%m-%d").unwrap(); - let my_time = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); - val == &NaiveDateTime::new(my_date, my_time) - } - } - Value::Decimal(_) => { - todo!() - } - Value::Date(_) => { - todo!() - } - Value::DatetimeTZ(_) => { - todo!() - } - Value::Duration(_) => { - todo!() - } - Value::Struct(_, _) => { - todo!() - } - } -} - -pub fn json_matches_str(string: &str, json: &JSON) -> TypeDBResult { - parse_json(string).map(|rhs| jsons_equal_up_to_reorder(json, &rhs)) -} - -fn parse_json(json: &str) -> TypeDBResult { +pub(crate) fn parse_json(json: &str) -> TypeDBResult { fn serde_json_into_fetch_answer(json: serde_json::Value) -> JSON { match json { serde_json::Value::Null => JSON::Null, @@ -235,37 +118,10 @@ fn jsons_equal_up_to_reorder(lhs: &JSON, rhs: &JSON) -> bool { } pub fn equals_approximate(first: f64, second: f64) -> bool { - const EPS: f64 = 1e-4; + const EPS: f64 = 1e-10; (first - second).abs() < EPS } -pub async fn match_templated_answer( - context: &Context, - step: &Step, - answer: &ConceptRow, -) -> TypeDBResult> { - let query = apply_query_template(step.docstring().unwrap(), answer); - // let parsed = parse_query(&query).map_err(|err| Error::Other(err.to_string()))?; - // context.transaction().query().get(&parsed.to_string())?.try_collect::>().await - todo!() -} - -fn apply_query_template(query_template: &str, answer: &ConceptRow) -> String { - // let re = Regex::new(r"").unwrap(); - // re.replace_all(query_template, |caps: &Captures| get_iid(answer.map.get(&caps[1]).unwrap())).to_string() - todo!() -} - -fn get_iid(concept: &Concept) -> String { - let iid = match concept { - Concept::Entity(Entity { iid, .. }) => iid, - Concept::Relation(Relation { iid, .. }) => iid, - Concept::Attribute(Attribute { iid, .. }) => iid, - _ => unreachable!("Unexpected Concept type: {concept:?}"), - }; - iid.to_string() -} - #[macro_export] macro_rules! assert_with_timeout { ($expr:expr, $message:expr $(, $arg:expr)* $(,)?) => {{ diff --git a/rust/tests/integration/core/BUILD b/rust/tests/integration/core/BUILD index 7651cd737b..0d195e65a7 100644 --- a/rust/tests/integration/core/BUILD +++ b/rust/tests/integration/core/BUILD @@ -20,6 +20,11 @@ package(default_visibility = ["//visibility:public"]) load("@rules_rust//rust:defs.bzl", "rust_test") load("@vaticle_dependencies//tool/checkstyle:rules.bzl", "checkstyle_test") +exports_files( + ["example.rs"], + visibility = ["//rust:__subpackages__"], +) + rust_test( name = "test_example", srcs = glob(["example.rs"]), diff --git a/rust/tests/integration/core/example.rs b/rust/tests/integration/core/example.rs index 4370dc8e38..edb6465966 100644 --- a/rust/tests/integration/core/example.rs +++ b/rust/tests/integration/core/example.rs @@ -88,7 +88,7 @@ fn example() { let answer = transaction.query(define_query).await.unwrap(); // Work with the driver's enums in a classic way or using helper methods - if answer.is_ok() && matches!(answer, QueryAnswer::Ok()) { + if answer.is_ok() && matches!(answer, QueryAnswer::Ok(_)) { println!("OK results do not give any extra interesting information, but they mean that the query is successfully executed!"); } assert!(answer.is_ok()); diff --git a/tool/docs/update.sh b/tool/docs/update.sh index 063fdbd3d3..6743928c99 100755 --- a/tool/docs/update.sh +++ b/tool/docs/update.sh @@ -18,7 +18,7 @@ # TODO: Temporarily update only 3.0 drivers bazel run //rust:docs_adoc -#bazel run //java:docs_adoc +bazel run //java:docs_adoc bazel run //python:docs_adoc #bazel run //nodejs:docs_adoc #bazel run //c:docs_adoc