You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by co...@apache.org on 2023/10/27 22:52:09 UTC

(tinkerpop) branch updateDevAndFutureDocs created (now 952f1085b0)

This is an automated email from the ASF dual-hosted git repository.

colegreer pushed a change to branch updateDevAndFutureDocs
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git


      at 952f1085b0 Update Dev and Future docs for 4.0.x development

This branch includes the following new commits:

     new 952f1085b0 Update Dev and Future docs for 4.0.x development

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(tinkerpop) 01/01: Update Dev and Future docs for 4.0.x development

Posted by co...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

colegreer pushed a commit to branch updateDevAndFutureDocs
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git

commit 952f1085b0f5e099ebf3a3cea8f5811ddbd22a5a
Author: Cole-Greer <Co...@improving.com>
AuthorDate: Fri Oct 27 15:51:57 2023 -0700

    Update Dev and Future docs for 4.0.x development
---
 docs/src/dev/developer/contributing.asciidoc       |   18 +-
 docs/src/dev/developer/for-committers.asciidoc     |   25 +-
 docs/src/dev/future/index.asciidoc                 |   58 +-
 .../dev/future/proposal-3-remove-closures.asciidoc | 1531 --------------------
 ...2.asciidoc => proposal-arrow-flight-1.asciidoc} |    0
 docs/src/dev/future/proposal-equality-1.asciidoc   |  843 -----------
 .../src/dev/future/proposal-transaction-4.asciidoc |  137 --
 7 files changed, 29 insertions(+), 2583 deletions(-)

diff --git a/docs/src/dev/developer/contributing.asciidoc b/docs/src/dev/developer/contributing.asciidoc
index 2ff436292c..52a3962eec 100644
--- a/docs/src/dev/developer/contributing.asciidoc
+++ b/docs/src/dev/developer/contributing.asciidoc
@@ -240,9 +240,8 @@ On successful review, the *pull request will be merged* to the main repository a
 [[versioning]]
 == Versioning
 
-TinkerPop version numbers follow a format of `EPOCH.MAJOR.MINOR.PATCH`, where:
+As of TinkerPop 4.0.0, version numbers follow a format of `MAJOR.MINOR.PATCH`, where:
 
-* `EPOCH` version is incremented for epochs of thinking around TinkerPop and its relation to the graph ecosystem and is part of a complete release.
 * `MAJOR` version is incremented for incompatible changes and is part of a complete release.
 * `MINOR` version is incremented for backward compatible changes and is part of a complete release.
 * `PATCH` version is incremented for backward compatible bug fixes and typically only applied to client modules for partial release.
@@ -253,18 +252,9 @@ driver/client. A partial release is helpful in that it decouples the release of
 typically server modules. In this way, for example, a small fix can be applied and released around .NET without having
 to release all the other components which may not have experienced any real change.
 
-The number for `EPOCH` rarely changes and when it does, it typically represents a complete re-write of the software
-and thus massive changes can be expected. As a result of the definition for `EPOCH`, there is greater focus on the other
-three numbers when considering where changes will land:
-
-1. A client with a specific `EPOCH.MAJOR.MINOR` will be backward compatible with user application code written for
-`EPOCH.MAJOR.MINOR`.
-1. A client with specific `EPOCH.MAJOR` will be backward compatible with the `EPOCH.MAJOR` version of the server.
-
-A complete release is always just `EPOCH.MAJOR.MINOR` where the `PATCH` is inferred as "0", thus 3.6.x line would
-initially release all components as `3.6.0`. A `PATCH` number must start with "1" within a release because of how
-link:https://github.com/NuGet/Home/issues/7376[Nuget treats "0"]. If it was determined that `gremlin-python` needed a
-special release then it could be released independently of the other modules as `3.6.0.1`.
+1. A client with a specific `MAJOR.MINOR` will be backward compatible with user application code written for
+`MAJOR.MINOR`.
+1. A client with specific `MAJOR` will be backward compatible with the `MAJOR` version of the server.
 
 In short, the dev mailing list is open to discussion on where a change will land. The discussion just needs to have
 good reasoning and use the general principles above as a framework for coming to a consensus.
diff --git a/docs/src/dev/developer/for-committers.asciidoc b/docs/src/dev/developer/for-committers.asciidoc
index 014d25a6e5..87cd5a634a 100644
--- a/docs/src/dev/developer/for-committers.asciidoc
+++ b/docs/src/dev/developer/for-committers.asciidoc
@@ -64,7 +64,7 @@ TinkerPop also has a link:https://the-asf.slack.com/archives/CUBJ577EW[Slack cha
 about the project among contributors, though it must be kept in mind that project discussion and decisions must occur
 on the dev mailing list mentioned above.
 
-Occasionally, online meetings via video conference are held. These meetings are schedule via the dev mailing list
+Occasionally, online meetings via video conference are held. These meetings are scheduled via the dev mailing list
 about a week before they are to occur to find a day and time that is available for those interested in attending.
 On the day of the meeting, the meeting organizer will create a Google Hangout (or similar video conferencing link).
 At that point, all who are interested can attend.  Meeting minutes should be
@@ -141,8 +141,8 @@ TinkerPop has several release branches:
 * `3.4-dev` - 3.4.x (no longer maintained)
 * `3.5-dev` - 3.5.x (non-breaking bug fixes and enhancements)
 * `3.6-dev` - 3.6.x (non-breaking bug fixes and enhancements)
-* `master` - 3.7.x (current development)
-* `4.0-dev` - 4.0.x (future development)
+* `3.7-dev` - 3.7.x (non-breaking bug fixes and enhancements)
+* `master` - 4.x.x (current development)
 
 The branch description above that reads "non-breaking bug fixes and enhancements" simply means that within that release
 line (i.e. patch version) changes should not alter existing behavior, introduce new APIs, change serialization formats,
@@ -151,13 +151,12 @@ can be assured that their upgrades will not introduce potential problems. A good
 client of one version within a release line can interact properly with a server version within that same line. If so,
 it is likely an acceptable change within that branch.
 
-Changes to earlier branches should merge forward toward `master` (e.g. `3.6-dev` should merge to `master`). Please read
-more about this process in the <<pull-requests, Pull Requests>> section. Note that `4.0-dev` is rebased on `master`
-and currently behaves as a fresh repository as all 3.x content was removed.
+Changes to earlier branches should merge forward toward `master` (e.g. `3.7-dev` should merge to `master`). Please read
+more about this process in the <<pull-requests, Pull Requests>> section.
 
-As described in <<versioning,versioning>>, it is possible to do a "partial" release which will utilize a four-digit
-version that starts with a "1" (e.g. `3.6.0.1`). The branching strategy for a partial release requires that a `-dev`
-branch be created with the three digit prefix.
+As described in <<versioning,versioning>>, it is possible to do a "partial" release which will utilize the third "patch" digit
+version that starts with a "1" (e.g. `4.0.1`). The branching strategy for a partial release requires that a `-dev`
+branch be created with the two digit prefix (e.g. 4.0-dev).
 
 Other branches may be created for collaborating on features or for RFC's that other developers may want to inspect.
 It is suggested that the JIRA issue ID be used as the prefix, since that triggers certain automation, and it provides a
@@ -172,8 +171,8 @@ Developers should remove their own branches when they are no longer needed.
 
 Tags are used for milestones, release candidates, and approved <<versioning,partial and complete releases>>. Tags for a
 complete release are simply defined by the three-digit version number. Tags for a partial release, should be prefixed
-by the individual language relevant to that release. For example, if there is an initial partial release for `3.6.0`
-on `gremlin-python` then the tag should be `3.6.0.1-python`.
+by the individual language relevant to that release. For example, if there is an initial partial release for `4.0.0`
+on `gremlin-python` then the tag should be `4.0.1-python`.
 
 Please refrain from creating arbitrary tags, as they produce permanent clutter.
 
@@ -214,7 +213,7 @@ this property set.
 affect users or providers.  This label is important when organizing release notes.
 ** The "deprecation" label which is assigned to an issue that includes changes to deprecate a portion of the API.
 * The "affects/fix version(s)" fields should be appropriately set, where the "fix version" implies the version on
-which that particular issue will completed. This is a field usually only set by committers and should only be set
+which that particular issue will be completed. This is a field usually only set by committers and should only be set
 when the issue is being closed with a completed disposition (e.g. "Done", "Fixed", etc.).
 * The "priority" field can be arbitrarily applied with one exception.  The "trivial" option should be reserved for
 tasks that are "easy" for a potential new contributor to jump into and do not have significant impact to urgently
@@ -698,7 +697,7 @@ require a review.
 
 * You are responsible for a release and need to manipulate files accordingly for the release.
 ** `Gremlin.version()`, CHANGELOG dates, `pom.xml` version bumps, etc.
-* You are doing an minor change and it is obvious that an RTC is not required (would be a pointless burden to the community).
+* You are doing a minor change and it is obvious that an RTC is not required (would be a pointless burden to the community).
 ** The fix is under the link:http://www.apache.org/foundation/glossary.html#CommitThenReview[commit-then-review] (CTR) policy and lazy consensus is sufficient, where a single -1 vote requires you to revert your changes.
 ** Adding a test case, fixing spelling/grammar mistakes in the documentation, fixing LICENSE/NOTICE/etc. files, fixing a minor issue in an already merged branch.
 
diff --git a/docs/src/dev/future/index.asciidoc b/docs/src/dev/future/index.asciidoc
index f849877b4a..c301da383d 100644
--- a/docs/src/dev/future/index.asciidoc
+++ b/docs/src/dev/future/index.asciidoc
@@ -47,25 +47,28 @@ in each release line represent unreleased changes only. Once an official release
 are removed with new items taking their place as they are planned. The release line is removed from the roadmap
 completely when it is no longer maintained.
 
-== 3.7.x - Target 2023H1
+== 4.x - Target 2024
 
-The development of the 3.7.x release line is currently under way with a target release date for the initial release of
-the line of 23H1.
+The development of the 4.x release line is currently under way with a target release date for the initial release of
+the line of 2024.
 
 * Add support for traversals as parameters for `V()`, `is()`, and `has()` (includes `Traversal` arguments to `P`)
 * Geospatial support for TinkerPop (link:++https://lists.apache.org/list?dev@tinkerpop.apache.org:2021-7:DISCUSS%20geo-spatial++[DISCUSS Thread])
-* Add mid-traversal `E()` support (link:https://issues.apache.org/jira/browse/TINKERPOP-2798[TINKERPOP-2798])
-* Allow properties on elements (as opposed to just references) for remote traversals
 * Add subgraph/tree structure in all GLVs
-* List functions (`concat()`/etc.)
 * Define semantics for query federation across Gremlin servers (depends on `call()` step)
 * Gremlin debug support
-* Date/Time manipulation functions (`dateAdd()`, `dateDiff()`, etc.)
-* Add string manipulation functions (`split()`, `substring()` etc.) (link:https://issues.apache.org/jira/browse/TINKERPOP-2672[TINKERPOP-2672])
 * Case-insensitive search (link:https://issues.apache.org/jira/browse/TINKERPOP-2673[TINKERPOP-2673])
 * Type conversion with `cast()` step
 * Mutation steps for `clone()` of an `Element` and for `moveE()` for edges.
-* Add a language element to merge `Map` objects more easily.
+* *Transactions* - Redesign the transaction model so that it is better suited for all graphs.
+** Ensure that there is no difference between remote and embedded transaction usage and that the API is less tangled
+than it is today.
+* *Groovy* - Reconsider all dependencies on Groovy throughout TinkerPop
+** Remove Groovy support from Gremlin Server which should be possible now that `gremlin-language` and `call()` are
+available.
+** Investigate options for using JShell as a replacement for `groovysh` in Gremlin Console.
+** Investigate options for removing `ScriptEngine` support in general, which would include support from
+`gremlin-language`.
 
 = Proposals
 
@@ -95,40 +98,5 @@ story.
 [width="100%",cols="3,10,2,^1",options="header"]
 |=========================================================
 |Proposal |Description |Targets |Resolved
-|link:https://github.com/apache/tinkerpop/blob/master/docs/src/dev/future/proposal-equality-1.asciidoc[Proposal 1] |Equality, Equivalence, Comparability and Orderability Semantics - Documents existing Gremlin semantics along with clarifications for ambiguous behaviors and recommendations for consistency. |3.6.0 |N
-|link:https://github.com/apache/tinkerpop/blob/master/docs/src/dev/future/proposal-arrow-flight-2[Proposal 2] |Gremlin Arrow Flight. |4.0.0 |N
-|link:https://github.com/apache/tinkerpop/blob/master/docs/src/dev/future/proposal-3-remove-closures[Proposal 3] |Removing the Need for Closures/Lambda in Gremlin |3.7.0 |N
+|link:https://github.com/apache/tinkerpop/blob/master/docs/src/dev/future/proposal-arrow-flight-1[Proposal 1] |Gremlin Arrow Flight. |Future |N
 |=========================================================
-
-= Appendix
-
-== TinkerPop4
-
-This space is currently a bit of a scratchpad for ideas and changes that might not fit well into TinkerPop3 and
-therefore might be best left to TinkerPop4.
-
-* *Transactions* - Redesign the transaction model so that it is better suited for all graphs.
-** Ensure that TinkerPop has a native implementation for transactions in TinkerGraph so that all tests can run from it.
-** Ensure that there is no difference between remote and embedded transaction usage and that the API is less tangled
-than it is today.
-* *Groovy* - Reconsider all dependencies on Groovy throughout TinkerPop
-** Remove Groovy support from Gremlin Server which should be possible now that `gremlin-language` and `call()` are
-available.
-** Investigate options for using JShell as a replacement for `groovysh` in Gremlin Console.
-** Investigate options for removing `ScriptEngine` support in general, which would include support from
-`gremlin-language`.
-
-=== 4.x Branching Methodology
-
-Development of 4.x occurs on the `4.0-dev` branch. This branch was created as an orphan branch and therefore has no
-history tied to any other branch in the repo including master. As such, there is no need to merge/rebase `4.0-dev`. When
-it comes time to promote `4.0-dev` to `master` the procedure for doing so will be to:
-
-1. Create a `3.x-master` branch from `master`
-1. Delete all content from `master` in one commit
-1. Rebase `4.0-dev` on `master`
-1. Merge `4.0-dev` to `master` and push
-
-From this point 3.x development will occur on `3.x-master` and 4.x development occurs on `master` (with the same version
-branching as we have now, e.g `3.3-dev`, `4.1-dev`, etc.) The `3.x-master` branch changes will likely still merge to
-`master`, but will all merge as no-op changes.
diff --git a/docs/src/dev/future/proposal-3-remove-closures.asciidoc b/docs/src/dev/future/proposal-3-remove-closures.asciidoc
deleted file mode 100644
index 87cace007c..0000000000
--- a/docs/src/dev/future/proposal-3-remove-closures.asciidoc
+++ /dev/null
@@ -1,1531 +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.
-////
-== Proposal 3 - Removing the Need for Closures/Lambda in Gremlin
-
-=== Status
-
-This proposal has been accepted through a post to the TinkerPop Dev List and is ready to begin implementaion.
-
-=== Motivation
-
-There are a number of useful operations that Gremlin users often wish to
-perform that are not provided today in the form of traversal steps or
-predicates (P/TextP). For historical reasons these functions were
-omitted and users were able to accomplish these tasks by specifying
-anonymous code blocks or “closures” to perform these tasks. For example,
-below is an example of how you can achieve a case-insensitive search for
-any cities that contain “Miami”.
-
-....
-g.V().hasLabel('city').
-    has('name',filter{it.get().toLowerCase().contains('Miami'.toLowerCase())})
-....
-
-While this is just one example of how closures are used, they are a
-powerful fallback mechanism in Gremlin to handle use cases where there
-is no functionality within the Gremlin language to meet the
-requirements. However, for a variety of reasons such as security and
-performance, many/most remote providers of TinkerPop do not allow users
-to execute closures as part of a query. This leaves users with a
-problem, as the mechanism provided to solve these sorts of use cases is
-not allowed. Examples of some commonly requested functionality that
-cannot be accomplished without the use of closures would be:
-
-[cols=",,",options="header",]
-|===
-|String Functions |List Functions |Date Functions
-|asString |reverse |dateAdd
-|concat |remove |dateDiff
-|length |indexOf |asDate
-|split |product |
-|substring |all |
-|rTrim |any |
-|lTrim |none |
-|trim |concat |
-|replace |length |
-|reverse |intersect |
-|toUpper |difference |
-|toLower |union |
-|===
-
-=== Considerations
-
-* Adding full support for traversals as parameters to predicates would
-simplify the syntax of these examples. However, this is a known issue
-with Gremlin and none of the proposed options below are blocked by it,
-nor do they exacerbated this issue any further. As such, the
-ramifications of that change are not covered by this proposal.
-
-=== Proposed Options and Recommendation
-
-==== Option 1 (Recommended)
-
-Create a new Gremlin step for each of the desired functions a user is
-looking to perform. Each step encapsulates a set of functionality that
-customers are looking to achieve. While certain steps may be reused
-across input types (e.g. `reverse()` for both list and string inputs)
-the behaviors of each step is well-defined for a given input type.
-
-*Example*: Find me all `city` nodes with a `name` starting with `miami`,
-ignoring case?
-`g.V().hasLabel('city').where(values('name').toLower(), eq('miami'))`
-
-===== Pros:
-
-* Most similar to current Gremlin patterns for adding steps
-* Feel the most like Gremlin when writing the
-
-===== Cons:
-
-* Would result in a potentially large number of steps being added to the
-language, hindering discoverability
-* Adds complexity to creating and maintaining all the current GLVs due
-to the number of new steps.
-
-==== Option 2
-
-Create a single Gremlin step that take the operation as a parameter and
-uses that parameter to mutate the behavior to achieve the desired
-functionality.
-
-*Example*: : Find me all `city` nodes with a `name` starting with
-`miami`, ignoring case?
-`g.V().hasLabel('city').where(F.apply(Func.toLower, values('name')), eq('miami'))`
-
-===== Pros:
-
-* Single step simplifies adding new operations across GLVs
-
-===== Cons:
-
-* Introduces a novel concept, a function, to the Gremlin language
-* No set signature for `F.apply` as it will differ per operation
-
-==== Option 3
-
-Create a new type of predicate in Gremlin that specifies the operation
-and returns the correct output. This would be a new paradigm within
-Gremlin, as it extends predicates to return non-boolean results.
-
-*Example*:: Find me all `city` nodes with a `name` starting with
-`miami`, ignoring case?
-`g.V().hasLabel('city').where(SP.toLower(values('name')).is('miami'))`
-
-===== Pros:
-
-* Single predicate simplifies adding new operations as they are now a
-token and not a new step that needs to be propagated across all the GLVs
-
-===== Cons:
-
-* New paradigm in Gremlin that further blurs the line between predicates
-and steps
-* Signature differs for each predicate operation
-* Signatures of common steps needs to change to include options like
-`where(Predicate)`
-
-=== Proposed Syntax
-
-<<string-function-syntax>>
-
-<<list-function-syntax>>
-
-<<date-function-syntax>>
-
-=== Examples
-
-==== String Examples
-
-===== String Example 1 (SE1)
-
-I want to find the offices, by name, where the name does not have a "-"
-as the third character of the string
-(https://stackoverflow.com/questions/56115935/gremlin-is-there-a-way-to-find-the-character-based-on-the-index-of-a-string[here])
-
-`g.V().hasLabel('office').where(__.values('name').substr(2, 1)).is(neq('-'))) `
-
-===== String Example 2 (SE2)
-
-I would like to trim out the "Mbit/s" from the string
-(https://stackoverflow.com/questions/45365726/im-unable-to-substring-values-that-i-get-by-running-a-gremlin-query-ive-been[here])
-
-`g.V('Service').has('serviceId','ETHA12819844').out('AssociatedToService').`
-`value("bandwidth").replace("Mbit/s", "")`
-
-===== String Example 3 (SE3)
-
-I am trying to add a new vertex which should be labeled like an existing
-vertex but with some prefix attached
-(https://stackoverflow.com/questions/61106927/concatenate-gremlin-graphtraversal-result-with-string[here])
-
-....
-`g.V(3).as('a').addV(constant("").concat("prefix_", select('a').label())`
-....
-
-===== String Example 4 (SE4)
-
-Find all products that start with the same case-insensitive prefix. +
-e.g. Given the following products:
-
-[cols=",",options="header",]
-|===
-|id |product_name
-|1 |PROD-123
-|2 |PROD-234
-|3 |TEST-1234
-|4 |GAMMA-1234
-|5 |PR-123
-|===
-
-We should return:
-
-[cols=",",options="header",]
-|===
-|id |product_name
-|1 |PROD-123
-|2 |PROD-234
-|===
-
-....
-g.V().hasLabel('Product').has('product_name').as('product1').
-  V().hasLabel('Product').has('product_name'`).`
-  where(__.is(select('product1').toLower())`.values('product_name').substring(0, 5)).
-  select('product1')
-....
-
-===== String Example 5 (SE5)
-
-Perform case-insensitive search
-
-....
-g.V().hasLabel('Product').where(values('product_name').toLower(), eq('foo'))
-....
-
-===== String Example 6 (SE6)
-
-Applying functions to returning values, in this case return the `age`
-and a lower cased version of `name`
-
-`g.V().hasLabel('person').valueMap('age', 'name').by().by(toLower())`
-
-===== String Example 7 (SE7)
-
-Concatenating values on the return, in this case return a concatenated
-name
-
-`g.V().hasLabel('person').project('age', 'name').` `by('age').`
-`by(values('first_name').concat(" ").concat(values('last_name'))`
-
-==== List Examples
-
-===== List Example 1 (LE1)
-
-Given a list of people, return the list of `age`s if everyone’s `age` >
-18
-
-`g.V().hasLabel('person').values('age').fold().where(all(gt(18)))`
-
-===== List Example 2 (LE2)
-
-Given a set of vertices, return the list of vertices if anyone’s `age` >
-18
-
-`g.V([1,2,3,4]).fold().where(any(values('age').is(gt(18))))`
-
-===== List Example 3 (LE3)
-
-Given a list, find the index of the first occurrence of `Dave`
-
-`g.V().hasLabel('person').fold().indexOf(has('name', 'Dave'))` `==> 12`
-
-`g.inject(['Dave', 'Kelvin', 'Stephen']).indexOf(constant('Dave'))`
-`==> 0`
-
-===== List Example 4 (LE4)
-
-Given a list of people, remove any person with a name of `Dave`
-
-`g.V().hasLabel('person').fold().remove(has('name', 'Dave'))`
-`==> [‘Kelvin’, ‘Stephen’]`
-
-`g.inject(['Dave', 'Kelvin', 'Stephen']).remove(constant('Dave'))`
-`==> [‘Kelvin’, ‘Stephen’]`
-
-`g.inject(['Dave', 'Kelvin', 'Stephen']).remove(constant(['Dave', 'Stephen'))`
-`==> ['Kelvin']`
-
-==== Date Examples
-
-===== Date Example 1 (DE1)
-
-Given a transaction, find me all other transactions within 7 days prior
-
-`g.V('transaction1').values('date').dateAdd(DT.Days, -7).as('purchase_date').V().hasLabel('transaction').where(gt('purchase_date')).by('date').by()`
-
-===== Date Example 2 (DE2)
-
-Given two transactions, find me the difference in the dates
-
-`g.V('transaction1').values('date').dateDiff(DT.Days, V('transaction2').values('date').asDate())`
-
-===== Date Example 3 (DE3)
-
-Given a static value, return me the value as a date
-
-`g.inject('1900-01-01').asDate()`
-
-===== Date Example 4 (DE4)
-
-Find the difference between a transaction and the first of the year
-
-`g.V('transaction1').values('date').dateDiff(DT.Days, inject(datetime('2023-01-01'))`
-
-
-
-== String Manipulation functions in TinkerPop [[string-function-syntax]]
-
-One of the common gaps that user's find when using Gremlin is that there
-is a lack of string manipulation capabilities within the language
-itself. This requires that users use closures to handle many common
-string manipulation options that users want to do on data in the graph.
-This is a problem for many users as many of the providers prevent the
-use of arbitrary closures due to the security risks so for these users
-there is no way to manipulate strings directly.
-
-=== Proposal
-
-The proposal here is to add a set of steps to handle common string
-manipulation requests from users, the details for each are discussed
-below:
-
-* <<asString, asString()>>
-* <<concat, concat()>>
-* <<length, length()>>
-* <<split, split()>>
-* <<substring, substring()>>
-* <<rTrim, rTrim()>>
-* <<lTrim, lTrim()>>
-* <<trim, trim()>>
-* <<replace, replace()>>
-* <<reverse, reverse()>>
-* <<toUpper, toUpper()>>
-* <<toLower, toLower()>>
-
-=== Gremlin Language Variant Function Names
-
-[cols=",,,,,",options="header",]
-|===
-|Groovy |Java |Python |JavaScript |.NET |Go
-|asString() |asString() |as_string() |asString() |AsString() |AsString()
-
-|concat() |concat() |concat() |concat() |Concat() |Concat()
-
-|length() |length() |length() |length() |Length() |Length()
-
-|split() |split() |split() |split() |Split() |Split()
-
-|substring() |substring() |substring() |substring() |Substring()
-|Substring()
-
-|rTrim() |rTrim() |rtrim() |rTrim() |RTrim() |RTrim()
-
-|lTrim() |lTrim() |ltrim() |lTrim() |LTrim() |LTrim()
-
-|trim() |trim() |trim() |trim() |Trim() |Trim()
-
-|replace() |replace() |replace() |replace() |Replace() |Replace()
-
-|reverse() |reverse() |reverse() |reverse() |Reverse() |Reverse()
-
-|toUpper() |toUpper() |to_upper() |toUpper() |ToUpper() |ToUpper()
-
-|toLower() |toLower() |to_lower() |toLower() |ToLower() |ToLower()
-|===
-
-'''''
-
-== Function Definitions
-
-=== `asString()` [[asString]]
-
-Returns the value of the incoming traverser as a string
-
-==== Signature(s)
-
-`asString()`
-
-`asString(Scope)`
-
-==== Parameters
-
-* Scope - Scope Enum
-
-==== Allowed incoming traverser types
-
-Any data type allowed by TinkerPop
-
-==== Expected Output
-
-A String value representing the string value of the traverser being
-passed in as shown below:
-
-[cols=",,",options="header",]
-|===
-|Incoming Datatype |Example Query |Example Output
-|Integer |`g.inject(29).asString()` |29
-
-|Float |`g.inject(29.0).asString()` |29.0
-
-|String |`g.inject('foo').asString()` |foo
-
-|UUID |`g.inject(UUID.randomUUID()).asString()`
-|47557eed-04e7-4aa4-89eb-9689d26fe94a
-
-|Map
-|`g.inject([["id": 1], ["id": 2, "something":"anything"]]).asString()`
-|[[id:1], [id:2, something:anything]]
-
-|Date |`g.inject(datetime()).asString()` |Sun Nov 04 00:00:00 UTC 2018
-
-|List |`g.inject([1,2,3]).asString()` |[1, 2, 3]
-
-|List (Local Scope) |`g.inject([1,2,3]).asString(local)` |["1", "2",
-"3"]
-
-|Vertex |`g.V(1).asString()` |v[1]
-
-|Edge |`g.E(7).asString()` |e[7][1-knows->2]
-
-|Property |`g.V(1).properties('age').asString()` |vp[age->29]
-
-|null |`g.V().group().by('foo').select(keys).asString()` |null
-|===
-
-'''''
-
-=== `concat()` [[concat]]
-
-Concatenates one or more strings together
-
-==== Signature(s)
-
-`concat(String...)`
-
-`concat(Traversal)`
-
-==== Parameters
-
-* `String...` - One or more String values to concatenate to the input
-string
-* `Traversal` - A traversal value to concatenate
-
-==== Allowed incoming traverser types
-
-String data types. If a non-string
-traverser, or the list containing non-string values, is passed in then
-an `IllegalArgumentException` will be thrown
-
-==== Expected Output
-
-A String value representing the concatenation of all the incoming traverser and input values
-
-....
-g.inject('this').concat('is', 'a', 'test')
-==>thisisatest
-g.V(1).values('first_name').concat(' ').concat(V(1).values('last_name')
-==>John Doe
-g.inject('this', 'is', 'a', 'test').concat(' inserted')
-==>this inserted
-==>is inserted
-==>a inserted
-==>test inserted
-g.inject('John').concat(' ').concat(V(1).values('last_name'))
-==>John Doe
-....
-
-*Note* `concat()` may also be extended to handle concatenating list
-values together but that is out of scope for this change.
-
-'''''
-
-=== `length()` [[length]]
-
-Returns the length of the input string
-
-==== Signature(s)
-
-`length()`
-
-`length(Scope)`
-
-==== Parameters
-
-* Scope - Scope Enum
-
-==== Allowed incoming traverser types
-
-String data types or array, if local scope is used. If a non-string
-traverser, or the list containing non-string values, is passed in then
-an `IllegalArgumentException` will be thrown
-
-==== Expected Output
-
-A Long value representing the number of items in an array or the number
-of characters in a string
-
-....
-g.inject('this').length()
-==>4
-g.inject('this').length(local)
-==>4
-....
-
-*Note*:While this is similar to `count(local)` they are not the same.
-`count(local)` treats the input by calculating the count of the items
-stored within the traversal. `length()` treats the input as an array and
-provides the length of that array.
-
-[cols=",,,",options="header",]
-|===
-|Input Datatype |Example traversal |count(local) |length()
-|Integer |`g.inject(29)` |1 |IllegalArgumentException
-
-|Float |`g.inject(29.0)` |1 |IllegalArgumentException
-
-|String |`g.inject('foo')` |1 |3
-
-|UUID |`g.inject(UUID.randomUUID())` |1 |IllegalArgumentException
-
-|Map |`g.inject(["id": 2, "something":"anything"]])` |1
-|IllegalArgumentException
-
-|Date |`g.inject(datetime())` |1 |IllegalArgumentException
-
-|List |`g.inject([1,2,3])` |3 |3
-
-|Vertex |`g.V(1)` |1 |IllegalArgumentException
-
-|Edge |`g.E(7)` |1 |IllegalArgumentException
-
-|Property |`g.V(1).properties('age')` |1 |IllegalArgumentException
-
-|null |`g.V().group().by('foo').select(keys)` |0
-|IllegalArgumentException
-|===
-
-'''''
-
-=== `split()` [[split]]
-
-Returns a list of strings created by splitting the input string around
-the matches of the given delimiter.
-
-==== Signature(s)
-
-`split(String)`
-
-`split(Scope, String)`
-
-==== Parameters
-
-* String - The delimiter character(s) to split the input string* *
-
-==== Allowed inputs
-
-String data types or array, if local scope is used. If a non-string
-traverser, or the list containing non-string values, is passed in then
-an `IllegalArgumentException` will be thrown
-
-==== Expected Output
-
-An array of strings split around the delimiter character(s)
-
-....
-g.inject('this').split('h')
-==>[t, is]
-g.inject('one,two').split(',')
-==>[one, two]
-g.inject('axxb').split('x')
-==>[a, b]
-g.inject('axybxc').split('xy')
-==>[a, bxc]
-g.inject(['this', 'that']).split('h')
-==>[[t, is], [t, at]]
-....
-
-'''''
-
-=== `substring()` [[substring]]
-
-returns a substring of the original string with the length specified,
-uses a 0-based start
-
-==== Signature(s)
-
-`substring(Long, Long)`
-
-`substring(Long)`
-
-`substring(Scope, Long, Long)`
-
-`substring(Scope, Long)`
-
-==== Parameters
-
-* Long - The start index, 0 based. If the value is negative then the
-start location will be the end of the string and it will go the
-specified number of characters from the end of the string.
-* Long - The number of characters to return. Optional - if not provided
-then all remaining characters will be returned
-* Scope - Scope Enum
-
-==== Allowed incoming traverser types
-
-String data types or array, if local scope is used. If a non-string
-traverser, or the list containing non-string values, is passed in then
-an `IllegalArgumentException` will be thrown
-
-==== Expected Output
-
-A String value containing the number of characters specified beginning
-at the start location. If the start location plus the length specified
-is greater than or equal to the input length, the result will contain
-the entire string.
-
-....
-g.inject('this').substring(0, 1)
-==>t
-g.inject('this').substring(2)
-==>is
-g.inject('this').substring(2, 5)
-==>is
-g.inject('this').substring(-1)
-==>s
-g.inject(['this', 'is', 'a', 'test']).substring(local, 2)
-==>[is, '' ,'' , 'st']
-....
-
-'''''
-
-=== `rTrim()` [[rTrim]]
-
-Returns a string with trailing whitespace removed
-
-*Note*: Whitespace characters are defined as space/tab/line feed/line
-tabulation/form feed/carriage return.
-
-==== Signature(s)
-
-`rTrim()`
-
-`rTrim(Scope)`
-
-==== Parameters
-
-* Scope - Scope Enum
-
-==== Allowed incoming traverser types
-
-String data types or array, if local scope is used. If a non-string
-traverser, or the list containing non-string values, is passed in then
-an `IllegalArgumentException` will be thrown
-
-==== Expected Output
-
-A string value with trailing whitespace removed
-
-....
-g.inject('this ').rTrim()
-==>this
-g.inject(['this ', 'that ']).rTrim(local)
-==>[this, that]
-....
-
-'''''
-
-=== `lTrim()` [[lTrim]]
-
-Returns a string with leading whitespace removed
-
-*Note*: Whitespace characters are defined as space/tab/line feed/line
-tabulation/form feed/carriage return.
-
-==== Signature(s)
-
-`lTrim()`
-
-`lTrim(Scope)`
-
-==== Parameters
-
-* Scope - Scope Enum
-
-==== Allowed incoming traverser types
-
-String data types or array, if local scope is used. If a non-string
-traverser, or the list containing non-string values, is passed in then
-an `IllegalArgumentException` will be thrown
-
-==== Expected Output
-
-A string value with leading whitespace removed
-
-....
-g.inject(' this').lTrim()
-==>this
-g.inject([' this', ' that']).lTrim(local)
-==>[this, that]
-....
-
-'''''
-
-=== `trim()` [[trim]]
-
-Returns a string with leading and trailing whitespace removed
-
-*Note*: Whitespace characters are defined as space/tab/line feed/line
-tabulation/form feed/carriage return.
-
-==== Signature(s)
-
-`trim()`
-
-`trim(Scope)`
-
-==== Parameters
-
-* Scope - Scope Enum
-
-==== Allowed incoming traverser types
-
-String data types or array, if local scope is used. If a non-string
-traverser, or the list containing non-string values, is passed in then
-an `IllegalArgumentException` will be thrown
-
-==== Expected Output
-
-A string value with leading and trailing whitespace removed
-
-....
-g.inject(' this ').trim()
-==>this
-g.inject([' this ', ' that ']).trim()
-==>[this, that]
-....
-
-'''''
-
-=== `replace()` [[replace]]
-
-Returns a string with the specified characters in the original string
-replaced with the new characters
-
-==== Signature(s)
-
-`replace(String, String)`
-
-`replace(Scope, String, String)`
-
-==== Parameters
-
-* String - The character(s) to be replaced
-* String - The character(s) to replace with
-* Scope - Scope Enum
-
-==== Allowed incoming traverser types
-
-String data types or array, if local scope is used. If a non-string
-traverser, or the list containing non-string values, is passed in then
-an `IllegalArgumentException` will be thrown
-
-==== Expected Output
-
-A string
-
-....
-g.inject('this').replace('t', 'x)
-==>xhis
-g.inject('this').replace('x', 't')
-==>this
-g.inject('this').replace('is', 'was')
-==>thwas
-g.inject(['this', 'that']).replace('th', 'was')
-==>[wasis, wasat]
-....
-
-'''''
-
-=== `reverse()` [[reverse]]
-
-Reverses the current string
-
-==== Signature(s)
-
-`reverse()`
-
-`reverse(Scope)`
-
-==== Parameters
-
-* Scope - Scope Enum
-
-==== Allowed incoming traverser types
-
-String data types or array, if local scope is used. If a non-string
-traverser, or the list containing non-string values, is passed in then
-an `IllegalArgumentException` will be thrown
-
-==== Expected Output
-
-A String value representing the reversed version of the incoming string
-
-....
-g.inject('this').reverse()
-==>siht
-g.inject(['this', 'that']).reverse(local)
-==>[siht, taht]
-....
-
-*Note* `reverse()` may also be extended to handle concatenating list
-values together but that is out of scope for this change.
-
-'''''
-
-=== `toUpper()` [[toUpper]]
-
-Returns an upper case string representation.
-
-*Note*: All case conversions will be done via the mappings specified for
-Unicode (https://www.unicode.org/reports/tr44/#Casemapping[found here])
-
-==== Signature(s)
-
-`toUpper()`
-
-`toUpper(Scope)`
-
-==== Parameters
-
-* Scope - Scope Enum
-
-==== Allowed incoming traverser types
-
-String data types or array, if local scope is used. If a non-string
-traverser, or the list containing non-string values, is passed in then
-an `IllegalArgumentException` will be thrown
-
-==== Expected Output
-
-A string
-
-....
-g.inject('this').toUpper()
-==>THIS
-g.inject(['this', 'that']).toUpper()
-==>[THIS, THAT]
-....
-
-'''''
-
-=== `toLower()` [[toLower]]
-
-Returns an lower case string representation
-
-*Note*: All case conversions will be done via the mappings specified for
-Unicode (https://www.unicode.org/reports/tr44/#Casemapping[found here])
-
-==== Signature(s)
-
-`toLower()`
-
-`toLower(Scope)`
-
-==== Parameters
-
-* Scope - Scope Enum
-
-==== Allowed incoming traverser types
-
-String data types or array, if local scope is used. If a non-string
-traverser, or the list containing non-string values, is passed in then
-an `IllegalArgumentException` will be thrown
-
-==== Expected Output
-
-A string
-
-....
-g.inject('THIS').toLower()
-==>this
-g.inject(['THIS', 'THAT']).toLower()
-==>[this, that]
-....
-
-
-== List Manipulation functions in TinkerPop [[list-function-syntax]]
-
-One of the common gaps that user's find when using Gremlin is that there
-is a lack of list manipulation capabilities within the language itself.
-This requires that users use closures to handle many common manipulation
-options that users want to do on data in the graph. This is a problem
-for many users as many of the providers prevent the use of arbitrary
-closures due to the security risks so for these users there is no way to
-manipulate strings directly.
-
-=== Proposal
-
-The proposal here is to add a set of steps to handle common list
-manipulation requests from users, the details for each are discussed
-below:
-
-* <<length_list, length()>>
-* <<reverse_list, reverse()>>
-* <<remove_list, remove()>>
-* <<indexOf_list, indexOf()>>
-* <<product_list, product()>>
-* <<all_list, all()>>
-* <<any_list, any()>>
-* <<none_list, none()>>
-* <<concat_list, concat()>>
-* <<intersect_list, intersect()>>
-* <<union_list, union()>>
-* <<difference_list, difference()>>
-* <<disjunct_list, disjunct()>>
-* <<conjoin_list, conjoin()>>
-
-=== Gremlin Language Variant Function Names
-
-[cols=",,,,,",options="header",]
-|===
-|Groovy |Java |Python |JavaScript |.NET |Go
-|length() |length() |length() |length() |Length() |Length()
-
-|reverse() |reverse() |reverse() |reverse() |Reverse() |Reverse()
-
-|remove() |remove() |remove() |remove() |Remove() |Remove()
-
-|indexOf() |indexOf() |index_of() |indexOf() |IndexOf() |IndexOf()
-
-|product() |product() |product() |product() |Product() |Product()
-
-|all() |all() |all() |all() |All() |All()
-
-|any() |any() |any() |any() |Any() |Any()
-
-|none() |none() |none() |none() |None() |None()
-
-|concat() |concat() |concat() |concat() |Concat() |Concat()
-
-|intersect() |intersect() |intersect() |intersect() |Intersect()
-|Intersect()
-
-|union() |union() |union() |union() |Union() |Union()
-
-|difference() |difference() |difference() |difference() |Difference()
-|Difference()
-
-|disjunct() |disjunct() |disjunct() |disjunct() |Disjunct()
-|Disjunct()
-
-|conjoin() |conjoin() |conjoin() |conjoin() |Conjoin() |Conjoin()
-|===
-
-'''''
-
-== Function Definitions
-
-=== `length()` [[length_list]]
-
-Returns the length of a list in the incoming traverser
-
-==== Signature(s)
-
-`length()`
-
-==== Parameters
-
-None
-
-==== Allowed incoming traverser types
-
-Array data types. If non-array data types are passed in then an
-`IllegalArgumentException` will be thrown
-
-==== Expected Output
-
-A Long value representing the number of items in an array or the number
-of characters in a string
-
-....
-g.inject([1, 2]).length()
-==>2
-....
-
-=== `reverse()` [[reverse_list]]
-
-Returns the value of the incoming list in reverse order
-
-==== Signature(s)
-
-`reverse()`
-
-==== Parameters
-
-None
-
-==== Allowed incoming traverser types
-
-Array data types. If non-array data types are passed in then an
-`IllegalArgumentException` will be thrown
-
-==== Expected Output
-
-An array in reverse order.
-
-....
-g.inject([1,2]).reverse()
-==>[2, 1]
-....
-
-=== `remove()` [[remove_list]]
-
-Removes the first element from the incoming list where the value equals
-the specified value
-
-==== Signature(s)
-
-`remove(value)`
-
-`remove(Traversal)`
-
-==== Parameters
-
-* value - The value to remove
-
-==== Allowed incoming traverser types
-
-Array data types. If non-array data types are passed in then an
-`IllegalArgumentException` will be thrown
-
-==== Expected Output
-
-An array value representing the new list
-
-....
-g.inject([1,2]).remove(1)
-==>[2]
-....
-
-=== `indexOf()` [[indexOf_list]]
- 
-Returns the first occurrence of the `value` in the incoming array
-
-==== Signature(s)
-
-`indexOf(value)`
-
-`indexOf(Traversal)`
-
-==== Parameters
-
-* value - The value to locate
-
-==== Allowed incoming traverser types
-
-Array data types. If non-array data types are passed in then an
-`IllegalArgumentException` will be thrown
-
-==== Expected Output
-
-A long representing the index of the first occurrence of the value
-(zero-based). If the values does not exist then `null` is returned
-
-....
-g.inject([1,2]).indexOf(1)
-==>0
-....
-
-=== `product()` [[product_list]]
-
-Returns the cartesian product of two lists
-
-==== Signature(s)
-
-`product(value)`
-
-`product(Traversal)`
-
-==== Parameters
-
-* value - An array
-
-==== Allowed incoming traverser types
-
-Array data types. If non-array data types are passed in then an
-`IllegalArgumentException` will be thrown
-
-==== Expected Output
-
-A set of values where each value contains the cartesian product of two
-lists
-
-....
-g.inject([1,2]).product([3,4])
-==>[[1,3], [1,4], [2,3], [2,4]]
-....
-
-=== `any()` [[any_list]]
-
-Allows the traverser to continue if any items in the array pass the
-supplied predicate.
-
-==== Signature(s)
-
-`any(Predicate)`
-
-==== Parameters
-
-* Predicate - The predicate to use to test the values in the array
-
-==== Allowed incoming traverser types
-
-Array data types. All other types will be filtered out.
-
-==== Expected Output
-
-The arrays which have any item pass the predicate.
-
-....
-g.inject([1,2]).any(P.eq(1))
-==>[1,2]
-....
-
-=== `all()` [[all_list]]
-
-Allows the traverser to continue if all items in the array pass the
-supplied predicate.
-
-==== Signature(s)
-
-`all(Predicate)`
-
-==== Parameters
-
-* Predicate - The predicate to use to test the values in the array
-
-==== Allowed incoming traverser types
-
-Array data types. All other types will be filtered out.
-
-==== Expected Output
-
-The arrays which have all items pass the predicate.
-
-....
-g.inject([1,2]).all(P.gt(0))
-==>[1,2]
-....
-
-=== `none()` [[none_list]]
-
-Returns true if no items in the array `value` exist in the input
-
-==== Signature(s)
-
-`none(value)`
-
-`none(Traversal)`
-
-==== Parameters
-
-* value - An array of the items to check in the incoming list
-
-==== Allowed incoming traverser types
-
-Array data types. If non-array data types are passed in then an
-`IllegalArgumentException` will be thrown
-
-==== Expected Output
-
-True if no values from one list are in the other, False otherwise
-
-....
-g.inject([1,2]).none([1])
-==>false
-g.inject([1,2]).none([1, 3])
-==>false
-g.inject([1,2]).none([3])
-==>true
-....
-
-=== `concat()` [[concat_list]]
-
-Returns the concatenation of the incoming array and the traversal or
-array value passed as a parameter. This will return all values,
-including duplicates.
-
-==== Signature(s)
-
-`concat(value)`
-
-`concat(Traversal)`
-
-==== Parameters
-
-* value - An array of the items to check in the incoming list
-
-==== Allowed incoming traverser types
-
-Array data types. If non-array data types are passed in then an
-`IllegalArgumentException` will be thrown
-
-==== Expected Output
-
-An array containing the values of the concatenation of the two lists
-
-....
-g.inject([1,2]).concat([3])
-==>[1, 2, 3]
-g.inject([1,2]).concat([1, 4])
-==>[1, 2, 1, 4]
-g.V().has('age', 29).values('age').dedup().fold().concat(V().has('age', 30).values('age').dedup().fold())
-==>[29, 30]
-....
-
-=== `union()` [[union_list]]
-
-Returns the union of the incoming array and the traversal or array value
-passed as a parameter. This will return an array of unique values
-
-==== Signature(s)
-
-`union(value)`
-
-`union(Traversal)`
-
-==== Parameters
-
-* value - An array of the items to check in the incoming list
-
-==== Allowed incoming traverser types
-
-Array data types. If non-array data types are passed in then an
-`IllegalArgumentException` will be thrown
-
-==== Expected Output
-
-An array containing the unique values of the union of the two lists
-
-....
-g.inject([1,2]).union([1])
-==>[1, 2]
-g.inject([1,2]).union([1, 4])
-==>[1, 2, 4]
-g.V().has('age', 29).values('age').dedup().fold().union(V().has('age', 30).values('age').dedup().fold())
-==>[29, 30]
-....
-
-=== `intersect()` [[intersect_list]]
-
-Returns the intersection of the incoming array and the traversal or
-array value passed as a parameter. This will return an array of unique
-values
-
-==== Signature(s)
-
-`intersect(value)`
-
-`intersect(Traversal)`
-
-==== Parameters
-
-* value - An array of the items to check in the incoming list
-
-==== Allowed incoming traverser types
-
-Array data types. If non-array data types are passed in then an
-`IllegalArgumentException` will be thrown
-
-==== Expected Output
-
-An array containing the unique values of the intersection of the two
-lists
-
-....
-g.inject([1,2]).intersect([1])
-==>[1]
-g.inject([1,2]).intersect([1, 2, 3])
-==>[1, 2]
-g.V().has('age', 29).values('age').dedup().fold().intersect(V().has('age', 30).values('age').dedup().fold())
-==>[]
-....
-
-=== `difference()` [[difference_list]]
-
-Returns the difference of the incoming array and the traversal or array
-value passed as a parameter. This will return an array of unique values
-
-==== Signature(s)
-
-`difference(value)`
-
-`difference(Traversal)`
-
-==== Parameters
-
-* value - An array of the items to check in the incoming list
-
-==== Allowed incoming traverser types
-
-Array data types. If non-array data types are passed in then an
-`IllegalArgumentException` will be thrown
-
-==== Expected Output
-
-An array containing the different values of the intersection of the two
-lists
-
-....
-g.inject([1,2]).difference([1])
-==>[2]
-g.inject([1,2]).difference([1, 2, 3])
-==>[3]
-g.V().has('age', 29).values('age').dedup().fold().difference(V().has('age', 30).values('age').dedup().fold())
-==>[29, 30]
-....
-
-=== `disjunct()` [[disjunct_list]]
-
-Returns the disjunct set of the incoming array and the traversal or array
-value passed as a parameter. This will return an array of unique values
-
-==== Signature(s)
-
-`disjunct(value)`
-
-`disjunct(Traversal)`
-
-==== Parameters
-
-* value - An array of the items to check in the incoming list
-
-==== Allowed incoming traverser types
-
-Array data types. If non-array data types are passed in then an
-`IllegalArgumentException` will be thrown
-
-==== Expected Output
-
-An array containing the different values of the intersection of the two
-lists
-
-....
-g.inject([1,2]).disjunct([1])
-==>[2]
-g.inject([1,2,4]).disjunct([1, 2, 3])
-==>[3, 4]
-....
-
-=== `conjoin()` [[conjoin_list]]
-
-Returns the join of the incoming array and delimiter passed as a
-parameter. This will return a String of the values joined together with
-the delimiter.
-
-==== Signature(s)
-
-`conjoin(delimiter)`
-
-==== Parameters
-
-* delimiter - A string delimiter used to join the values
-
-==== Allowed incoming traverser types
-
-String data types. If non-array data types are passed in then an
-`IllegalArgumentException` will be thrown
-
-==== Expected Output
-
-A string of the values joined together with the delimiter
-
-....
-g.inject([1,2]).conjoin("5")
-==>152
-g.inject([1,2,3]).conjoin(";")
-==>1;2;3
-....
-
-== Date Manipulation functions in TinkerPop [[date-function-syntax]]
-
-One of the common gaps that user's find when using Gremlin is that there
-is a lack of date manipulation capabilities within the language itself.
-This requires that users use closures to handle many common manipulation
-options that users want to do on data in the graph. This is a problem
-for many users as many of the providers prevent the use of arbitrary
-closures due to the security risks so for these users there is no way to
-manipulate strings directly.
-
-=== Proposal
-
-The proposal here is to add a set of steps to handle common datetime
-manipulation requests from users, the details for each are discussed
-below:
-
-* <<asDate, asDate()>>
-* <<dateAdd, dateAdd()>>
-* <<dateDiff, dateDiff()>>
-
-=== Gremlin Language Variant Function Names
-
-[cols=",,,,,",options="header",]
-|===
-|Groovy |Java |Python |JavaScript |.NET |Go
-|asDate() |asDate() |as_date() |asDate() |AsDate() |AsDate()
-|dateAdd() |dateAdd() |date_add() |dateAdd() |DateAdd() |DateAdd()
-|dateDiff() |dateDiff() |date_diff() |dateDiff() |DateDiff() |DateDiff()
-|===
-
-== Function Definitions
-
-=== `asDate()` [[asDate]]
-
-Returns the value of the incoming traverser as an ISO-8601 date
-
-==== Signature(s)
-
-`asDate()`
-
-`asDate(Scope)`
-
-==== Parameters
-
-* Scope - Scope Enum
-
-==== Allowed incoming traverser types
-
-Any data type that can be parsed into an ISO-8601 date. If an
-unsupported types is passed in then an `IllegalArgumentException` will
-be thrown
-
-==== Expected Output
-
-A Date value representing the ISO-8601 value of the traverser being
-passed in as shown below:
-
-[cols=",,",options="header",]
-|===
-|Incoming Datatype |Example Query |Example Output
-|Integer |`g.inject(0).asDate()` |1900-01-01T00:00:00Z
-
-|Float |`g.inject(29.0).asDate()` |1900-01-01T00:00:00Z
-
-|String |`g.inject('1/1/1900').asDate()` |1900-01-01T00:00:00Z
-
-|UUID |`g.inject(UUID.randomUUID()).asDate()`
-|`IllegalArgumentException`
-
-|Map
-|`g.inject([["id": 1], ["id": 2, "something":"anything"]]).asDate()`
-|`IllegalArgumentException`]
-
-|Datetime |`g.inject(datetime()).asDate()` |Sun Nov 04 00:00:00 UTC 2018
-
-|List |`g.inject([1,2,3]).asDate()` |`IllegalArgumentException`
-
-|List (Local Scope) |`g.inject([1,2,3]).asDate(local)`
-|`IllegalArgumentException`
-
-|Vertex |`g.V(1).asDate()` |`IllegalArgumentException`
-
-|Edge |`g.E(7).asDate()` |`IllegalArgumentException`
-
-|Property |`g.V(1).properties('age').asDate()`
-|`IllegalArgumentException`
-
-|null |`g.V().group().by('foo').select(keys).asDate()`
-|`IllegalArgumentException`
-|===
-
-=== `dateAdd()` [[dateAdd]]
-
-Returns the value with the addition of the `value` number of units as
-specified by the `DateToken`
-
-==== Signature(s)
-
-`dateAdd(DateToken, value)`
-
-`dateAdd(Scope, DateToken, value))`
-
-==== Parameters
-
-* DateToken - DateToken Enum
-* value - The number of units, specified by the Datetime Token, to add to
-the incoming values
-
-==== Allowed incoming traverser types
-
-Datetime data types. If non-array data types are passed in then an
-`IllegalArgumentException` will be thrown
-
-==== Expected Output
-
-A Datetime with the value added.
-
-....
-g.inject(datetime()).dateAdd(DT.days, 7)
-==> 2018-03-22
-g.inject(datetime()).dateAdd(DT.days, -7)
-==> 2018-03-8
-g.inject([datetime(), datetime()]).dateAdd(local, DT.days, 7)
-==> [2018-03-22, 2018-03-22]
-....
-
-=== `dateDiff()` [[dateDiff]]
-
-Returns the difference between two Datetimes in epoch time
-
-==== Signature(s)
-
-`dateDiff(value)`
-
-`dateDiff(Traversal)`
-
-`dateDiff(Scope, value))`
-
-==== Parameters
-
-* value - The Datetime to find the difference from
-
-==== Allowed incoming traverser types
-
-Datetime data types. If non-array data types are passed in then an
-`IllegalArgumentException` will be thrown
-
-==== Expected Output
-
-The epoch time difference between the two values
-
-....
-g.inject(datetime()).dateDiff(datetime().dateAdd(DT.days, 7))
-==> 604800
-g.inject(datetime()).dateDiff(datetime().dateAdd(DT.days, 7))
-==> -604800
-g.inject([datetime(), datetime()]).dateDiff(local, DT.days, 7)
-==> [604800, 604800]
-....
diff --git a/docs/src/dev/future/proposal-arrow-flight-2.asciidoc b/docs/src/dev/future/proposal-arrow-flight-1.asciidoc
similarity index 100%
rename from docs/src/dev/future/proposal-arrow-flight-2.asciidoc
rename to docs/src/dev/future/proposal-arrow-flight-1.asciidoc
diff --git a/docs/src/dev/future/proposal-equality-1.asciidoc b/docs/src/dev/future/proposal-equality-1.asciidoc
deleted file mode 100644
index ec4925a74f..0000000000
--- a/docs/src/dev/future/proposal-equality-1.asciidoc
+++ /dev/null
@@ -1,843 +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.
-////
-image::apache-tinkerpop-logo.png[width=500,link="https://tinkerpop.apache.org"]
-
-*x.y.z - Proposal 1*
-
-= Equality, Equivalence, Comparability and Orderability Semantics
-
-== Status
-
-Existing Gremlin behavior documented here is being migrated to Provider Documentation. Tests for enforcement of this
-existing behavior and suggestions for modifications are being scoped for 3.6.0. Open issues described in this document
-are being set aside for further discussion.
-
-== Motivation
-
-How values compare to each other is crucial to the behavior of a query language. While comparison semantics may sound 
-like a trivial question at first, when looking under the surface many interesting questions arise, including aspects 
-around equality and comparability in the context of type casting (e.g., over numerics of different types), slightly 
-different variants of equality being used in different context (e.g. predicates vs. deduplication), questions around 
-comparability and ordering across different logical types, as well as around the identity of elements (such as vertex 
-and edge properties). 
-
-TinkerPop / Gremlin is written in and (partially) relies upon Java / JVM, and there is no clear semantics defined and 
-published for the different types of equality and comparison operations as of today. Rather, what equals what and how 
-values compare is often implicitly defined by the semantics of the underlying Java data structures that are being used, 
-and hence may vary from context to context. We believe that a concise definition of these concepts is critical for 
-both TinkerPop users — who need to be able to reason about the outcome of their queries — as well as custom 
-implementations of the TinkerPop API, who would benefit from a concise definition to follow. Therefore, TinkerPop 
-should provide a complete and cohesive semantics for equality / comparison such that all Graph Providers can easily 
-ensure that their query processing approach aligns with the TinkerPop implementation. Helping users and 
-implementers alike, this will help increase the adoption of Gremlin as a query language. 
-
-This documentation is a proposal that shall serve as a basis for a community discussion on how TinkerPop should handle 
-equality / comparison in different contexts. Motivated by different examples of the status today, we formalize 
-different notions of equality and comparability and describe the contexts in which they apply. While the semantics 
-that we propose is largely aligned with the semantics that is implemented in TinkerPop today, this proposal aims to 
-fill in some existing gaps (such as providing a complete, cross-datatype ordering instead of throwing exceptions) and 
-proposes modifications for a few edge cases, as to make the overall semantics more predictable, coherent, and 
-documentable.    
-
-=== Examples
-
-Below are a couple of example scenarios where defining semantics can help clarify and mitigate inconsistent / undefined 
-behavior in TinkerPop today:
-
-==== Underspecified or Undocumented Behavior
-
-Consider an equality check such as 
-
-[source]
-----
-gremlin> g.V().has(id, 19)
-----
-
-Without a precise definition, both users and Graph providers don't know whether this query matches only nodes with an 
-ID that is exactly equal to the integer value 19 or, for instance, all numerical values that cast to an Integer value 
-19. To see that, right now, they need to dig into the TinkerPop code base. While, in the above example, type casting 
-rules apply, in other cases such as
-
-[source]
-----
-gremlin> g.V().property(numericValue).dedup()
-----
-
-the two values above would always be treated as different entities.
-
-==== The behavior that is inherently driven by Java:
-
-Another example is equality over composite type.
-
-[source]
-----
-gremlin> g.V().aggregate("a").out().aggregate("b").cap("a").where(eq("b"))
-----
-
-This query compares two `BulkSet` objects produced by cap-Step. But the comparison is Java dependent and we don’t have 
-a clear definition of how the comparison works for this kind of types. Same as `Map`, e.g.
-
-[source]
-----
-gremlin> g.V().group().unfold().as("a").V().group().unfold().as("b").where(eq("a", "b"))
-----
-
-and even we have comparison over `Map.Entry` which is Java dependent type.
-
-[source]
-----
-gremlin> g.V().group().unfold().order() 
-class java.util.HashMap$Node cannot be cast to class java.lang.Comparable (java.util.HashMap$Node and java.lang.Comparable are in module java.base of loader 'bootstrap')
-----
-
-==== Incompleteness Rendres Unexpected Results 
-
-A query which tries to determine the order across multiple types fails today. 
-
-[source]
-----
-// Properties have values of Integer and String.
-gremlin> g.V().values("some property").order()
-
-class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')
-
-// This query aims to order a heterogeneous result set
-gremlin> g.V().union(out(), outE()).order()
-class org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerVertex cannot be cast to class java.lang.Comparable (org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerVertex is in unnamed module of loader 'app'; java.lang.Comparable is in module java.base of loader 'bootstrap')
-----
-
-It would be more helpful for users to define the complete order across types and returns a result instead of throwing 
-an exception.
-
-==== Inconsistencies Results
-
-Handling for `NaN`, `NULL`, `+0.0`, `-0.0`, `+INF`, `-INF` is tricky, and TinkerPop does not cover all cases 
-consistently at this moment.
-
-[source]
-----
-gremlin> g.V("1").properties("key")
-==>vp[key→0]
-
-// NaN == 0 holds true for this equality check.  
-gremlin> g.V("1").has("key", Double.NaN)
-==>v[1]
-
-gremlin> g.V("1").properties()
-==>vp[key→Infinity]
-
-// 0.0 is interpreted as BigDecimal in Groovy and it tries to promote Infinity to BigDecimal as well,
-// then the type casting fails. This is observed when using Java11.
-gremlin> g.V("1").has("key", gt(0.0))
-Character I is neither a decimal digit number, decimal point, nor "e" notation exponential mark.
-----
-
-In the next section, we provide a conceptual proposal to define concepts around how values compare and are ordered, 
-which aims to provide an answer to these and other questions. We seek the feedback from the community to discuss and 
-reach a consensus around the proposal and are open to all other ideas around how these concepts should be defined in 
-TinkerPop / Gremlin.
-
-== Conceptualization of Equality and Comparison
-
-In the above section we used the notions of "equality" and "comparison" in a generalized way. Inspired by the 
-formalization in https://s3.amazonaws.com/artifacts.opencypher.org/openCypher9.pdf[the openCypher specification], we 
-now refine these two notions into four, where we distinguish between equality vs. equivalence and comparability vs. 
-orderability, which constitute two flavors of these concepts tailored to their usage in different concepts.  We 
-summarize and contrast these concepts in the following subsections; more technical details and discussion of edge 
-cases can be found in the technical appendix.
-
-=== Proposed semantics
-
-==== Equality vs. Equivalence
-
-Equality defines when two values are considered equal in the context of database lookups and predicates, while 
-equivalence defines value collation semantics in the context of, for instance, deduplication. For instance, 
-equivalence over two values `a := Double.NaN` and `b:= Double.NaN` is true, but equality would (in our proposal) be 
-defined as false; the rational here (which is commonly found in query and programming languages) is that comparing two
-"unknown" numbers — which is a frequent use case for NaN, cannot certainly be identified as equal in comparison, but it
-typically makes sense to group them together in, for instance, aggregations. 
-
-Both equality and equivalence can be understood as complete, i.e. the result of equality and equivalence checks is 
-always either `true` or `false` (in particular, it never returns nulltype` or throws an exception). The details on 
-equality and equivalence are sketched in the following two subsections, respectively.
-
-===== Equality 
-
-* Used by equality and membership predicates (such as https://github.com/apache/tinkerpop/blob/734f4a8745e797f794c4860962912b04313f312a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java#L130[P.eq], https://github.com/apache/tinkerpop/blob/734f4a8745e797f794c4860962912b04313f312a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/P.java#L139[P.neq], and the list membership https://github.com/apache/tinkerpop/blob/72be3549a5e4f99115e9d4 [...]
-
-* If graph providers need join semantics in query execution, equality should be used to join data over join keys. +
-Example:
-
-[code]
-----
-// equality over 2 ids
-gremlin> g.V().has(id, "some id")
-// equality over vertices
-gremlin> g.V().as("v").out().out().where(eq("v"))
-----
-
-* Equality adheres to type promotion semantics for numerical values, i.e. equality holds for values of different
-numerical type if they cast into the exactly same same value of the lowest common super type.
-* Other than the type promotion between Numbers, 2 values of different type are always regarded as not equal.
-* Equality checks always return `true` or `false`. They never result in `nulltype` output, undefined behavior, nor do
-they ever throw an error.
-
-===== Equivalence
-
-* Equivalence defines how TinkerPop deals with 2 values to be grouped or de-duplicated. Specifically it is necessary for the dedup and group steps in Gremlin. +
-Example:
-
-[code]
-----
-// deduplication needs equivalence over 2 property values
-gremlin> g.V().dedup().by("name")
-// grouping by equivalence over 2 property values
-gremlin> g.V().group().by("age") 
-----
-
-* Equivalence ignores type promotion semantics, i.e. two values of different types (e.g. 2^^int vs. 2.0^^float) are
-always considered to be non-equivalent. (There is an open question whether equivalence takes type promotion into account).
-* For Number, 
-** Because type promotion is not effective, if the types are different then two numbers are never equivalent
-** NaN is not equal to NaN, but equivalent to each other
-* Other than the edge case around NaN (and, as of today, Numbers), equivalence in TinkerPop is identical to equality.
-* Like equality, equivalence checks always return `true` or `false`. They never result in `nulltype` output, undefined behavior, nor do they ever throw an error.
-
-==== Comparability vs. Orderability
-
-Comparability and orderability can be understood as the "dual" concepts of equality and equivalence for range
-comparisons (rather than exact comparison). For the 2 values of the same type (except for NaN), comparability is
-stronger than orderability in the sense that everything that every order between two values that holds `true` w.r.t.
-comparability also holds `true` w.r.t. orderability, but not vice versa. Comparability is what is being used in range
-predicates. It is restricted to comparison within the same type or, for numerics, class of types; comparability is
-complete within a given type, but returns `nulltype` if the two types are considered incomparable (e.g., an integer
-cannot be compared to a string). Orderability fills these gaps, by providing a stable sort order over mixed type
-results; it is consistent with comparability within a type, and complete both within and across types, i.e. it will
-never return `nulltype` or throw an exception.
-
-More details on comparability and orderability are sketched in the following two subsections, respectively.
-
-===== Comparability
-
-* Used by the comparison operators (https://github.com/apache/tinkerpop/blob/050f66a956ae36ceede55613097cc86e19b8a737/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Compare.java#L88[P.gt], https://github.com/apache/tinkerpop/blob/050f66a956ae36ceede55613097cc86e19b8a737/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/Compare.java#L138[P.lt], https://github.com/apache/tinkerpop/blob/050f66a956ae36ceede55613097cc86e19b8a737/gremlin-core/ [...]
-Example:
-
-[code]
-----
-// comparison over 2 property values
-gremlin> g.E().has("weight", gt(1))  
-----
-
-* For numbers,
-** it should be aligned to equality conceptually as far as type promotion is concerned. e.g. `1.0 < 2 < 3L`
-* Comparison should not result in undefined behavior, but can return `nulltype` if and only if we are comparing
-incomparable data types. How this `nulltype` result is handled is Graph provider dependent.
-* Otherwise Comparison does return `true` or `false`
-
-===== Orderability
-
-* Used to determine the order. In TinkerPop, the order step follows the notion of orderability.
-* Orderability must not result in `nulltype` / undefined behavior.
-* Orderability must not throw an error. In other words, even if 2 values are incomparable we should still be able to
-determine the order of those two. This inevitably leads to the requirement to define the order across different data
-types. For the detailed order across types, see appendix.
-* Orderability determines if 2 values are ordered at the same position or one value is positioned earlier than another.
-* The concept of equivalence is used to determine if the 2 values are at the same position
-* When the position is identical, which value comes first (in other words, whether it should perform stable sort)
-depends on graph providers' implementation.
-* For values of the same type, comparability can be used to determine which comes first except for `NaN` in Number.
-For a different type, we have a dedicated order as described in the section below.
-
-===== Mapping table for TinkerPop operators
-
-Shown as below is a table for which notion proposed above each TinkerPop construct used.
-
-[%header]
-|================
-|Construct|Concept                
-|P.eq     |Equality               
-|P.neq    |Equality               
-|P.within |Equality               
-|P.without|Equality               
-|P.lt     |Comparability          
-|P.gt     |Comparability          
-|P.lte    |Equality, Comparability
-|P.gte    |Equality, Comparability
-|P.inside |Comparability          
-|P.outside|Comparability          
-|P.between|Equality, Comparability
-|================
-
-== What would change ?
-
-=== Semantics
-
-In terms of Semantics, right now TinkerPop does not have formal semantics to define these characteristics introduced
-in this proposal. Therefore these semantics should be published in the official TinkerPop documentation.
-
-=== Behavioral changes
-
-==== Equality
-
-* NaN +
-JDK11 seems to produce a different error from JDK8 when it comes to `BigDecimal` comparisons that hit NaN and such. For
-JDK8 they seem to produce `NumberFormatException` but for JDK11 you get stuff like:
-
-[code]
-----
-gremlin> g.V().has("key", Float.NaN)
-Character N is neither a decimal digit number, decimal point, nor "e" notation exponential mark.
-----
-When `Double` / `Float` is stored, it always throws. With the proposed change, it wouldn't throw but because `NaN` is
-not equal to any numbers this returns empty result.
-
-* BigDecimal +
-Equality around `BigDecimal` and special values which cannot be parsed as `Integer` such as `NaN`, `INF` should not
-produce exceptions and should filter.
-
-[code]
-----
-gremlin> g.addV().property('key',Float.NaN)
-==>v[0]
-gremlin> g.addV().property('key',1.0f)
-==>v[2]
-gremlin> g.V().has('key',Float.NaN)
-==>v[0]
-gremlin> g.V().has('key',1.0f)
-==>v[2]
-gremlin> g.V().values("key").is(eq(1.0f)) // 3.5.x
-==>1.0
-gremlin> g.V().has('key',1.0) // 3.5.x - likely due to Groovy going to BigDecimal for "1.0"
-java.lang.NumberFormatException
-Type ':help' or ':h' for help.
-Display stack trace? [yN]n
-gremlin> g.V().values("key").is(eq(new BigDecimal(1.0f))) // 3.5.x
-java.lang.NumberFormatException
-Type ':help' or ':h' for help.
-Display stack trace? [yN]
-gremlin> g.V().has('key',1.0) // proposed
-==>v[2]
-gremlin> g.V().values("key").is(eq(1.0)) // proposed
-==>1.0
-----
-
-==== Comparability
-
-* NaN +
-Comparing on `NaN` should return no results.
-
-[code]
-----
-gremlin> g.addV().property('key',-5)
-==>v[0]
-gremlin> g.addV().property('key',0)
-==>v[2]
-gremlin> g.addV().property('key',5)
-==>v[4]
-gremlin> g.addV().property('key',Double.NaN)
-==>v[6]
-gremlin> g.V().values("key").is(lte(Double.NaN)) // 3.5.x
-==>-5
-==>0
-==>NaN
-gremlin> g.V().values("key").is(gte(Double.NaN)) // 3.5.x
-==>0
-==>5
-==>NaN
-gremlin> g.V().values("key").is(lt(Double.NaN)) // 3.5.x
-==>-5
-gremlin> g.V().values("key").is(gt(Double.NaN)) // 3.5.x
-==>5
-gremlin> g.V().values("key").is(lte(Double.NaN)) // proposed
-==>NaN
-gremlin> g.V().values("key").is(gte(Double.NaN)) // proposed
-==>NaN
-gremlin> g.V().values("key").is(lte(Double.NaN)) // proposed
-gremlin> g.V().values("key").is(gte(Double.NaN)) // proposed
-----
-
-* Comparability throws exception today but based on the proposal, it returns `nulltype` when comparing incompatible types.
-  ** When `Vertex` / `Edge` / `VertexProperty`  is compared, today it throws but it should return `nulltype`.
-  ** When `nulltype` is compared, today it throws an exception but it should return `nulltype`. 
-
-==== Equivalence
-
-TinkerPop today uses a hash value for original values for grouping and the behavior is unchanged.
-
-==== Orderability
-
-* Currently, TinkerPop follows comparability for orderability, thus non-comparable and mixed-type values will fail in
-ordering. The proposed change is to be able to order any types.
-
-[code]
-----
-gremlin> g.V().order(). // 3.5.x
-org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerVertex cannot be cast to java.lang.Comparable
-Type ':help' or ':h' for help.
-Display stack trace? [yN]
-gremlin> g.V(1).values('name').union(identity(),V(2)).order() // 3.5.x
-org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerVertex cannot be cast to java.lang.Comparable
-Type ':help' or ':h' for help.
-Display stack trace? [yN]n
-gremlin> g.V().order()  // proposed
-==>v[1]
-==>v[2]
-==>v[3]
-==>v[4]
-==>v[5]
-==>v[6]
-gremlin> g.V(1).values('name').union(identity(),V(2)).order() // proposed
-==>v[2]
-==>marko
-gremlin> g.addV().property("key", 100)
-==>v[0]
-gremlin> g.addV().property("key", "100000")
-==>v[2]
-gremlin> g.V().values('key').order() // 3.5.x
-java.lang.Integer cannot be cast to java.lang.String
-Type ':help' or ':h' for help.
-Display stack trace? [yN]
-gremlin> g.V().values('key').order() // proposed
-==>100
-==>100000
-----
-
-== Open Questions
-
-* Should we take type-promotion into account in terms of equivalence ? +
-[code]
-----
-// In this case below,
-gremlin> g.V().property()
-==>[key:1.0]
-==>[key:1]
-
-// which is more natural, whether we don't de-duplicate them
-gremlin> g.V().property().dedup()
-==>[key:1.0]
-==>[key:1]
-
-// or de-dup them
-gremlin> g.V().property().dedup()
-==>[key:1.0]
-----
-        
-If de-duping, there is another question which value we should filter out. We need to define priority over types in `Number`.
-Also note that TinkerPop is Java based and we have `Double.NaN` and `Float.NaN`, `±Double.INF` and `±Float.INF`. Not
-adhering type casting means, for example, Double.NaN and Float.NaN is not de-duplicated / grouped according to the semantics.
-        
-* `Map.Entry` is Java dependent type. Instead of defining semantics for `Map.Entry`, do we introduce a concept of like
-key-value tuple for it to generalize?
-* Today we have `Date` type but don’t we need timezone aware `DateTime` type as well?
-* Some graph providers may not support `BigDecimal`. Do we leave how TinkerPop deals with `BigDecimal` to Graph Providers?
-* Which should be more reasonable, `nulltype` eq `nulltype` is true or false?
-  ** If it is true, it may be respected in JOIN operation
-* There are a number of situations where the Gremlin grammar won’t support some of the examples - to what extent do
-these sorts of constructs need to exist in the grammar? Not having them would impact the ability to supply tests that
-enforce the behaviors that we’ve outlined.
-
-== Technical Appendix
-
-=== Types
-
-First we need to define which data types the TinkerPop query execution runtime needs to handle. It is JVM based so as a
-primitive type, we are using the following types:
-
-* Boolean: true or false
-* Integer
-  ** int8
-  ** int16 (short)
-  ** int32
-  ** int64 (long)
-  ** uint8 (byte)
-  ** uint16
-  ** uint32
-  ** uint64
-  ** BigInt
-* Float
-  ** float32
-  ** float64 (double)
-  *** In Double and Float, we have a concept of INFINITY / https://en.wikipedia.org/wiki/Signed_zero[signed-zero], and NaN.
-  ** BigFloat
-* String / Char
-* UUID
-* Date
-* `nulltype`
-  ** It denotes the "undefined" value.
-
-Graph providers may not support all of these types depending on the architecture and implementation. Therefore
-TinkerPop must provide a way for Graph providers to override the behavior while it has its own default behavior. Also
-when some types are not supported Graph providers needs to map unsupported types into supported types internally. This
-mapping can be done in either information-preserving manner or non-preserving manner. Graph providers must tell which
-mapping they support through `Graph.Features` as well as which types they support.
-
-* Which atomic types are supported
-  ** Boolean, Integer, Float, String, UUID and Date
-  ** TinkerPop by default supports all of them
-* Which integer types are supported
-  ** int8, int16, int32, int64, uint8, uint16, uint32, uint64, BigInt
-  ** TinkerPop by default supports uint8 (byte), int16 (short), int32 (int), int64 (long) and BigInt
-* Which float types are supported
-  ** float32, float64 and BigFloat
-  ** TinkerPop by defualt supports all as float, double, and BigDecimal in Java
-
-In addition to these, there are composite types as follows:
-
-* Vertex
-* Edge
-* VertexProperty
-* Property
-    ** Edge property
-    ** Vertex meta property
-* PropertyKey
-* PropertyValue
-* Label
-* ID
-* Path
-* List
-* Map
-* Set / BulkSet
-* Map.Entry (obtained from unfolding a Map)
-
-=== Type Casting
-
-We do type casting a.k.a type promotion for Numbers. Numbers are  Byte, Short, Integer, Long, Float, Double, BigInteger,
- and BigDecimal. Here is the rule how types are promoted:
-
-* If at least one is BigDecimal then compare as BigDecimal
-* If at least one is BigInteger then compare as BigInteger
-* If at least one is Double then compare as Double
-* If one of them is a Float, then convert both to floating type of highest common bit denomination
-  ** If another value is Long or Double, we need 64bit so convert both to Double 
-  ** Otherwise convert both to Float
-* If at least one is Long then compare as Long
-* If at least one is Integer then compare as Integer
-* If at least one is Short then compare as Short
-* If at least one is Byte then compare as Byte
-
-BigDecimal and BigInteger may not be supported depending on the language and Storage, therefore the behavior of type
-casting for these 2 types can vary depending on a Graph provider.
-
-=== Equality
-
-==== Primitive types
-
-===== Number
-
-Number consists of Byte, Short, Integer, Long, Float, Double, BigInteger, and BigDecimal.
-
-* If either one of LHS or RHS is Number and another isn't, eq returns `false`.
-* If both LHS and RHS are Number, it follows the type casting described above and then check the equality.
-* Example for edge cases:
-    ** -0.0 eq 0.0  = `true`
-    ** +0.0 eq 0.0 = `true`
-    **  -0.0 eq +0.0 = `true`
-    ** NaN eq NaN  = `false`
-    ** +INF eq +INF = `true`
-    **  -INF eq -INF = `true`
-    **  -INF eq +INF = `false`
-* TinkerPop is JVM based so there can be ±INF^^float and ±INF^^double, NaN^^float and NaN^^double. They also adhere the type promotion.
-
-===== Boolean
-
-* If either one of LHS or RHS is Boolean and another isn't, return `false`
-* True != False, True == True, False == False
-
-===== String
-
-* If either one of LHS or RHS is String and another isn't, return `false`
-* We assume the common graphical order over unicode strings.
-* LHS and RHS needs to be lexicographically equal for LHS eq RHS == `true` for String.
-
-===== UUID
-* UUID is evaluated based on its String representation.
-* However, for example, UUID("b46d37e9-755c-477e-9ab6-44aabea51d50") and String "b46d37e9-755c-477e-9ab6-44aabea51d50" are not equal to each other.
-
-===== Date
-
-* If either one of LHS or RHS is Date and another isn't, return `false`
-* LHS eq RHS == `true` when both LHS and RHS value are numerically identical in Unix Epoch time.
-
-===== `nulltype`
-
-* If either one of LHS or RHS is `nulltype` and another isn't, return `false`
-* If both LHS and RHS are `nulltype`, return `true` 
-
-==== Composite types
-
-For all of them, if LHS and RHS is not of the same data type, equality returns `false`. The following semantics applied when both LHS and RHS has the data type.
-
-===== Vertex / Edge / VertexProperty
-
-They are considered as Element family in TinkerPop and if 2 elements have the same type and have the same ID, they are considered as equal.
-
-===== Property
-
-If key and value are same, 2 properties are equal.
-
-===== PropertyKey
-
-key is String type so Equality for String type applies.
-
-===== PropertyValue
-
-Any type, so Equality for a corresponding type applies.
-
-===== ID
-
-Any type, so Equality for a corresponding type applies.
-
-===== Label
-
-label is String type so Equality for String type applies.
-
-===== Path
-
-2 Paths are equal when their path elements are equal (using equality of List), and the corresponding path labels are also equal. 
-
-===== List
-
-* Two lists are equal if they contain the same (equal to each other) elements in the same order.
-
-===== Map
-
-* Two maps are equal when a Set of key-value pairs from those 2 maps are equal to each other. A key-value pair is equal to another pair if and only if both its key and value are equal to each other.
-
-===== Set
-
-* Two sets are equal if they contain the same (equal to each other) elements.
-
-=== Equivalence
-
-Equivalence is identical to Equality, except for the cases listed below.
-
-==== Primitive types
-
-===== Number
-
-* Unlike Equality, we *don't do* type casting for Equivalence.
-    ** If the type is different, they are not equivalent.
-        *** +INF^^double is not equivalent to +INF^^float
-        *** NaN^^double is not equivalent to NaN^^float
-    ** 123 and 123.0 are equal but not equivalent to each other
-* -0.0, 0.0, and +0.0 are not equivalent to each other
-    ** -0.0 is equivalent to -0.0
-    ** 0.0 is equivalent to 0.0
-    ** +0.0 is equivalent to +0.0
-* -INF and +INF are not equivalent to each other
-    ** -INF is equivalent to -INF
-    ** +INF is equivalent to +INF
-    ** They are equialavlent to each other irrespective to its underlying type, so in Java, for example, Double.POSITIVE_INFINITY is equivalent to Float.POSITIVE_INFINITY.
-* NaN is not equivalent to any other numbers
-    ** NaN *is equivalent to* NaN irrespective to its underlying type, so in Java, for example, Double.NaN is equivalent to Float.NaN.
-
-===== `nulltype`
-
-* `nulltype` is not equivalent to any other values
-* `nulltype` is equivalent to `nulltype`
-
-=== Comparability
-
-==== Primitive types
-
-===== Number
-
-* If either one of LHS or RHS is Numbers and another isn’t, throw an Exception. This comes first before the handling for each type.
-* If both LHS and RHS are Numbers, try the type casting, and then compare 2 values.
-* For -0.0, 0.0, +0.0, lt and gt returns `false` and lte, gte returns `true` because they are "equal" in this semantics.
-* -INF < +INF
-* Any comparison between NaN and any numbers (including NaN) should return `false` +
-https://docs.oracle.com/javase/specs/jls/se8/html/jls-4.html#jls-4.2.3
-* IF `nulltype` and NaN is compared it should return `nulltype` as their "type" is different and they are not comparable.
-
-===== Boolean
-
-* If either one of LHS or RHS is Boolean and another isn’t, throws an Exception
-* False < True
-
-===== String
-
-* If either one of LHS or RHS is String and another isn’t, returns `nulltype`.
-* We assume the common lexicographical order over unicode strings
-* LHS and RHS are compared lexicographically
-* UUID is evaluated based on its String representation.
-
-===== UUID
-
-* UUID is evaluated based on its String representation.
-* However, for example, UUID("b46d37e9-755c-477e-9ab6-44aabea51d50") and String "b46d37e9-755c-477e-9ab6-44aabea51d50" cannot be compared with each other, hence comparing them returns `nulltype`.
-
-===== Date
-
-* If either one of LHS or RHS is Date and another isn’t, throw an Exception
-* Compare LHS and RHS based on chronological order, i.e. numerical order in timestamp.
-
-===== `nulltype`
-
-* `nulltype` is not comparable, if the LHS or RHS is `nulltype` then the comparison result is `nulltype`.
-
-==== Composite types
-
-For all of them, if LHS and RHS is not of the same data type, equality returns `false`. The following semantics applied when both LHS and RHS has the data type.
-
-===== Vertex / Edge / VertexProperty
-
-They are not comparable, return `nulltype`.
-
-===== Property
-
-It it not comparable, return `nulltype`.
-
-===== PropertyKey
-
-Comparability of String applies.
-
-===== PropertyValue
-
-Property values are of any primitive types defined, so Comparability for a corresponding type applies.
-
-===== ID
-
-IDs are of any primitive types defined, so Comparability for a corresponding type applies.
-
-===== Label
-
-Comparability of String applies.
-
-===== Path
-
-It it not comparable, throw an Exception.
-
-===== List
-
-It it not comparable, throw an Exception.
-
-===== Map
-
-It it not comparable, throw an Exception.
-
-===== Map.Entry
-
-It it not comparable, throw an Exception.
-
-===== Set
-
-It it not comparable, throw an Exception.
-
-=== Orderability
-
-To sort across any types of values, we define the order between each type as follows:
-(In this order, ID, label, property key and property value are considered as a part of primitive types)
-
-* `nulltype`
-* Boolean
-* Number
-* Date
-* String
-* Vertex
-* Edge
-* VertexProperty
-* Property
-* Path
-* List
-* Map
-
-==== Primitive types
-
-===== Number
-
-* Same applies as Comparability. Exceptions are as below:
-    ** NaN is ordered at a larger index among all Numbers. i.e. after +INF.
-* We do type promotion for orderability as we do for comparability.
-
-===== Boolean
-
-* False < True
-
-===== String
-
-* String value is ordered lexicographically
-
-===== UUID
-
-* UUID is ordered lexicographically based on its String representation
-
-===== Date
-
-* Date value is ordered chronologically
-
-===== `nulltype`
-
-* `nulltype` is before all value types
-
-==== Composite types
-
-===== Vertex / Edge / VertexProperty
-
-They are ordered by their ID. The ID is chosen internally by the implementation, so ordering is implementation specific, but is guaranteed to be stable.
-
-===== Property
-
-They are ordered by property key. If the key is equal, then property value is used as the 2nd key.
-
-===== PropertyKey
-
-Comparability of String applies.
-
-===== PropertyValue
-
-Property values are of any primitive types defined, so orderability for a corresponding type applies.
-
-===== ID
-
-IDs are of any primitive types defined, so orderability for a corresponding type applies.
-
-===== Label
-
-Comparability of String applies.
-
-===== Path
-
-* Orderability of the 1st element in the Path applies. Empty Path should come first.
-* If the 1st element is tie, then check the next element, and so on.
-* If one Path exhausts the element fast then it comes earlier in the order.
-
-===== List
-
-* Orderability of the 1st element in the List applies.
-* Empty List should come first.
-* If the 1st element is tie, then check the next element, and so on.
-* If one List exhausts the element fast then it comes earlier in the order.
-
-===== Map
-
-* For 2 maps, get the 1st entry (a key-value pair) from both, the orderability between them decides the order of the maps.
-* If the 1st entry is tie, then we pick the 2nd one and repeat the process until we determine the order.
-    ** So the orderability of Map depends on in which order they return an entry. It is implementation dependent and undefined in this semantics.
-* If one Map exhausts an entry earlier than another, then it comes earlier in the order.
-
-===== Map.Entry
-
-* First check the orderability of their key.
-* If the key ties, then check the orderability of their value.
-
-===== Set
-
-* For 2 sets, get the 1st item from both, the orderbaility between them decides the order of the sets.
-* If the 1st item is tie, we pick the 2nd one and so on until we determine the order.
-    ** So the orderability of Set depends on in which order they return an item. It is implementation dependent and undefined in this semantics.
-* If one Set exhausts an item earlier than another, then it comes earlier in the order.
diff --git a/docs/src/dev/future/proposal-transaction-4.asciidoc b/docs/src/dev/future/proposal-transaction-4.asciidoc
deleted file mode 100644
index 5a63e10f2a..0000000000
--- a/docs/src/dev/future/proposal-transaction-4.asciidoc
+++ /dev/null
@@ -1,137 +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.
-////
-image::apache-tinkerpop-logo.png[width=500,link="https://tinkerpop.apache.org"]
-
-*x.y.z - Proposal 4*
-
-== TinkerGraph Transaction Support
-
-=== Introduction
-Now, if you need to use transactions in TinkerPop, the only solution is to use the Neo4j plugin. Unfortunately, this plugin has not been updated for a long time and is only compatible with Neo4j version 3.4, which reached end of life in March 2020. Gremlin cannot be used as a replacement for other graphs that support transactions.
-
-=== Motivation
-* Users will be able to use transactions without the outdated Neo4j plugin.
-* Test framework simplification.
-* A more robust Gremlin Server setup out-of-the-box.
-* A better learning experience for new users.
-* A less complicated transaction story for new users.
-* A better test graph that is more readily swappable with other graphs.
-
-=== Assumptions
-
-* Need to reuse existing code as much as possible.
-* Users should be able to work both with and without transactions.
-* Transaction implementation should not have additional external dependencies.
-* Existing drivers should work without changes.
-
-=== Specifications
-==== Design Overview
-The main changes relate to TinkerGraph, need to make a similar solution but with transaction support. Also TinkerGraph by default will have 2 configurations on Gremlin Server and users themselves will choose what they need, TinkerGraph or TinkerTransactionGraph.
-Locks will be write-only to reduce the impact of transactions on performance. In this case the isolation level will be `read committed`, the same as now with the Neo4j plugin. 
-Only ThreadLocalTransaction is planned, therefore embedded graph transactions may not be fully supported.
-
-==== Overview of changes to code base
-===== ElementContainer
-Storage for current Element value and ThreadLocal updated value with `isDeleted` flag. Similar for `Vertices` and `Edges`.
-[code]
-----
-class ElementContainer {
-    Vertex current;
-    ThreadLocal<Vertex> transactionUpdatedValue;
-    ThreadLocal<Boolean> isDeleted;
-}
-----
-
-===== TinkerTransactionGraph
-Vertices and Edges will store with version number and dirty transaction values
-`Map<Object, ElementContainer> vertices` where `Object` is Vertex Id.
-Shared code with TinkerGraph will be moved to AbstractTinkerGraph.
-
-===== TinkerTransactionElement
-Add `currentVersion` to all TinkerElements. This is the number of the transaction that last updated it.
-
-===== TinkerThreadLocalTransaction
-Extension of AbstractThreadLocalTransaction. Add a unique transaction number. Can use `AtomicLong.incrementAndGet`
-
-===== Graph Feature files
-Add TinkerTransactionGraphFeatures, TinkerTransactionGraphVertexFeatures, TinkerTransactionGraphEdgeFeatures, TinkerTransactionGraphGraphFeatures, TinkerTransactionGraphVertexPropertyFeatures.
-
-==== CRUD operations without transaction
-1. Wrap into transaction
-2. Commit
-
-==== CRUD operations with transaction
-===== Create
-Add new ElementContainer to `vertices` and `edges` in TinkerTransactionGraph.
-
-==== Read
-Read values from `vertices` ElementContainer, `transactionUpdatedValue` if present, otherwise `current`.
-if `isDeleted` flag is set, then the element is deleted in transaction and should be skipped.
-
-===== Update
-Update corresponding ElementContainer  in `vertices`.
-
-===== Delete
-Set `isDeleted` flag in ElementContainer  in `vertices`.
-
-==== Transaction flows
-===== Commit flow
-To reduce lock time double-checked locking used.
-1. Make list of all affected elements sorted by transaction#. The reason is to reduce the lock time.
-2. If any has a newer version, then fail.
-3. Try to lock all Vertices/Edges changed in transaction. For vertex/edge delete operation also need to lock adjacent edges/vertices. Lock is for write operations only. If some Vertex/Edge is already locked then fail.
-4. Check versions again, fail if some element is updated. Code: `Vertices.get(id).current()).currentVersion() !=Vertices.get(id).transactionUpdatedValue().currentVersion()`
-5. For all Elements replace current version with value updated in transaction (or remove Element on Delete operation). Cleanup `transactionUpdatedValue`.
-6. Change version of all updated Elements.
-7. Unlock.
-8. Update indexes if needed.
-
-===== Rollback
-Cleanup `transactionUpdatedValue` in `vertices`
-
-===== Error
-On any error, including transaction conflict:
-1. Rollback
-2. Throw exception
-
-===== Timeout
-Add transactionWatchdog which will rollback locked/expired transactions. Similar to `Session.touch`.
-1. If the transaction was open longer than some preconfigured (default 10-20 minutes?) time.
-2. If the transaction start commit, but not finish (default 1-2 seconds?).
-
-===== Examples of conflict resolution
-* tx1 adds a property to v1, tx2 deletes v1 (same for edges)
-transaction that executed first will set the lock on v1, second transaction will fail on one of steps 2-4.
-* tx1 adds an edge from v1 to v2, tx2 deletes v1 or v2
-If tx1 try to commit first: tx1 add edge, tx2 delete v1 AND all edges including newly added.
-If tx2 try to commit first: tx2 deletes v1 or v2, tx1 will fail on step 1.
-* tx1 changes a Cardinality.single property to x, tx2 changes the same Cardinality.single property to y
-Last transaction will fail on step 2 or 4.
-* tx1 adds a new vertex v1, tx2 adds the same vertex (same for edges)
-Last transaction will fail on step 2 or 4 or 5.
-* tx1 removes vertex v1, tx2 removes the same vertex (same for edges)
-Both transactions will be successful. 
-* tx1 adds vertex v1, tx2 removes vertex v1 (same for edges)
-If v1 exists before and tx1 commit first: tx1 fail on step 5
-If v1 exists before and tx2 commit first: tx1 fail on step 2 or 4
-If v1 not exists before and tx1 commit first: tx1 ok; tx2 depends on commit time, ok when tx1 finished and fail on step 2 or 4 otherwise.
-If v1 not exists before and tx2 commit first: tx2 ok, tx1 ok
-
-
-==== Additional changes
-Split TinkerFactory into AbstractTinkerFactory and TinkerFactory, add TinkerTransactionGraphFactory.
-Transfer responsibility of graph update operations from static TinkerHelper to TinkerGraph/TinkerTransactionGraph.