Add docs for new resolver builders

Add docs for new resolver builders

= Resolvers

In `Connect`, you implement the graph by creating `resolvers`. Those resolvers are functions that expose some data on the graph.

A resolver has a few basic elements:

. Inputs – A set of attributes that are required to be in the current parsing context
for the resolver to be able to work. Inputs is optional, no inputs means that the
resolver is always capable of working, independently of the current parsing context.
. Outputs - A query-like notation representing the shape of data the resolver is able
to resolve. This is typically a list of attributes/joins, where joins
typically include a simple subquery.
. A function - A `(fn [env input-data] tree-of-promised-output)` that takes the inputs
and turns them into a tree that satisfies the "output query".

So you might define a resolver like this:

[source,clojure]
----
(pc/defresolver person-resolver
  [{:keys [database] :as env} {:keys [person/id]}]
  {::pc/input #{:person/id}
   ::pc/output [:person/first-name :person/age]}
  (let [person (my-database/get-person database id)]
    {:person/age        (:age person)
     :person/first-name (:first-name person)}))
----

TIP: If you use link:https://cursive-ide.com/[Cursive], you can ask it to resolve the `pc/defresolver` as a `defn` and you will get proper symbol resolution

Where the `database` in the environment would be supplied when running the parser, and the input would have to be
found in the current context.  Remember that graph queries are contextual... you have to have a starting node to work
from, so in the above example we're assuming that during our parse we'll reach a point where the context contains
a `:person/id`.  The `my-database` stuff is just made up for this example, and is intended to show you that your
data source does not need to remotely match the schema of your graph query.

Pathom will scan through the defined resolvers in order to try to satisfy all of the properties in a query.  So, technically
you can split up your queries as much as makes sense into separate resolvers, and as long as the inputs are in the context
Pathom will assemble things back together.

Of course, it doesn't make sense in this case to do so, because each resolver would end up running a new query:

[source,clojure]
----
(pc/defresolver person-age-resolver [{:keys [database] :as env} {:keys [person/id]}]
  {::pc/input #{:person/id}
   ::pc/output [:person/age]}
  (let [person (my-database/get-person database id)]
    {:person/age (:age person)}))

(pc/defresolver person-first-name-resolver [{:keys [database] :as env} {:keys [person/id]}]
  {::pc/input #{:person/id}
   ::pc/output [:person/first-name]}
  (let [person (my-database/get-person database id)]
    {:person/first-name (:first-name person)}))

...
----

The point is that a single-level query like `[:person/id :person/first-name :person/age]` can be satisfied and "folded together"
by Pathom over any number of resolvers.

This fact is the basis of parser (de)composition and extensibility.  It can also come in handy for performance
refinements when there are computed attributes.

== Derived/Computed Attributes

There are times when you'd like to provide an attribute that is computed in some fashion.  You can, of course, simply
compute it within the resolver along with other properties like so:

[source,clojure]
----
(pc/defresolver person-resolver [{:keys [database] :as env} {:keys [person/id]}]
  {::pc/input #{:person/id}
   ::pc/output [:person/first-name :person/last-name :person/full-name :person/age]}
  (let [{:keys [age first-name last-name]} (my-database/get-person database id)]
    {:person/age        age
     :person/first-name first-name
     :person/last-name  last-name
     :person/full-name  (str first-name " " last-name) ; COMPUTED
     ...}))
----

but this means that you'll take the overhead of the computation when *any* query relate to person comes up.  You can
instead spread such attributes out into other resolvers as we discussed previously, which will only be invoked if the
query actually asks for those properties:

[source,clojure]
----
(pc/defresolver person-resolver [{:keys [database] :as env} {:keys [person/id]}]
  {::pc/input #{:person/id}
   ::pc/output [:person/first-name :person/last-name :person/age]}
  (let [{:keys [age first-name last-name]} (my-database/get-person database id)]
    {:person/age        age
     :person/first-name first-name
     :person/last-name  last-name}))

(pc/defresolver person-name-resolver [_ {:person/keys [first-name last-name]}]
  {::pc/input #{:person/first-name :person/last-name}
   ::pc/output [:person/full-name]}
  {:person/full-name (str first-name " " last-name)})
----

This combination of resolvers can still resolve all of the properties in `[:person/full-name :person/age]` (if
`:person/id` is in the context), but a query for just `[:person/age]` won't invoke any of the logic for the
`person-name-resolver`.

== Single Inputs -- Establishing Context [[SingleInputs]]

So far we have seen how to define a resolver that can work *as long as* the inputs are already in the environment.  You're
almost certainly wondering how to do that.

One way is to define <<GlobalResolvers,global resolvers>> and start the query from them, but very often you'd just
like to be able to say "I'd like the first name of person with id 42."

EQL uses "idents" to specify exactly that sort of query:

[source,clojure]
----
[{[:person/id 42] [:person/first-name]}]
----

The above is a join on an ident, and the expected result is a map with the ident as a key:

[source,clojure]
----
{[:person/id 42] {:person/first-name "Joe"}}
----

The query *itself* has everything you need to establish the *context* for running the `person-resolver`,
and in fact that is how Pathom single-input resolvers work.

If you use an ident in a query then Pathom is smart enough to know that it can use that ident to establish the context
for finding resolvers.  In other words, in the query above the ident `[:person/id 42]` is turned
into the *parsing context* `{:person/id 42}`, which satisfies the *input* of any resolver that needs
`:person/id` to run.

== Resolver Without Input -- Global Resolver [[GlobalResolvers]]

A resolver that requires no input can output its results at any point in the graph, thus it is really a global resolver.
Pay a particular attention to the *"at any point in the graph"* - it's not just at the root.  Thus, a resolver without inputs
can "inject" its outputs into any level of the query graph result.

We're going to start building a parser that can satisfy queries about a music store. So, we'll start with a global resolver
that can resolve the "latest product".  The code below shows the entire code needed, boilerplate and all:

[source,clojure]
----
include::example$com/wsscode/pathom/book/connect/getting_started.cljs[]
----

Our first resolver exposes the attribute `::latest-product`, and since it doesn't require any input it is a global resolver.
Also, note that our output description includes the full output details (including nested attributes), this is mostly
useful for auto-completion on UI's and automatic testing. If you return extra data it will still end up in the output
context.

Try some of these queries on the demo below:

[source,clojure]
----
[::latest-product]
[{::latest-product [:product/title]}]

; ::latest-product can be requested anywhere
[{::latest-product
  [* ::latest-product]}]
----

++++

[::latest-product]

++++

== Resolvers with single input

Next, let's say we want to have a new attribute which is the brand of the product. Of course, we could just throw the
data there in our other resolver, but the real power of Connect comes out when we start splitting the responsibilities
among resolvers, so let's define a resolver for brand that requires an input of `:product/id`:

[source,clojure]
----
include::example$com/wsscode/pathom/book/connect/getting_started2.cljs[]
----

++++

[{::latest-product [:product/title :product/brand]}]

++++

The input is a `set` containing the keys required on the current entity in the parsing context for the resolver to be
able to work. This is where `Connect` starts to shine because any time your query asks for a bit of data
it will try to figure it out how to satisfy that request based on the attributes that the current contextual entity *already has*.

More importantly: `Connect` will *explore the dependency graph* in order to resolve things if it needs to!
To illustrate this, let's pretend we have some external ID for the brand and that we can derive this ID from the
brand string - pretty much just another mapping:

[source,clojure]
----
;; a silly pretend lookup
(def brand->id {"Taylor" 44151})

(pc/defresolver brand-id-from-name [_ {:keys [product/brand]}]
  {::pc/input #{:product/brand}
   ::pc/output [:product/brand-id]}
  {:product/brand-id (get brand->id brand)})

(comment
  (parser {} [{::latest-product [:product/title :product/brand-id]}])
  ; => #::{:latest-product #:product{:title "Acoustic Guitar", :brand-id 44151}}
)
----

Note that our query never said anything about the `:product/brand`. `Connect` automatically walked the path
`:product/id -> :product/brand -> :product/brand-id` to obtain the information desired by the query!

When a required attribute is not present in the current entity, `Connect` will look for resolvers that can fetch it,
analyze their inputs, and recursively walk backwards towards the "known data" in the context.
When a required attribute is not present in the current entity, `Connect` will calculate the possible paths
from the data you have to the data you request, then it can use some heuristic to decide which path to take
and walk this path to reach the data, if there is no possible path connect reader will return `::p/continue` to
let another reader try to handle that key. You can read more about how this works in the Index page.

Also remember that single-input resolvers can handle ident-based queries.
Thus, the following ident-join queries already work without having to define anything else:

[source,clojure]
----
(parser {} [{[:product/id 1] [:product/brand]}])
; => {[:product/id 1] #:product{:brand "Taylor"}}

(parser {} [{[:product/brand "Taylor"] [:product/brand-id]}])
; => {[:product/brand "Taylor"] #:product{:brand-id 44151}}
----

== Multiple inputs

The input to a resolver is a set, and as such you can require more than one thing as input to your resolvers.  When doing
so, of course, your resolver function will receive all of the inputs requested; however, this also means that the parsing
context needs to contain them, or there must exist other resolvers that can use what's in the context to fill them in.

As you have seen before, the only way to provide ad-hoc information to connect is using the ident query, but in the ident
itself you can only provide one attribute at a time.

Since version `2.2.0-beta11` the ident readers from connect (`ident-reader` and `open-ident-reader`) support adding extra
context to the query using parameters. Let's say you want to load some customer data but you want to reduce the number
of resolvers called by providing some base information that you already have, you can issue a query like this:

[source,clojure]
----
[{([:customer/id 123] {:pathom/context {:customer/first-name "Foo" :customer/last-name "Bar"}})
  [:customer/full-name]}]
----

== Resolver builders

There are some common resolver patterns that emerge from usage, Pathom connect provides
helpers for such cases, the next sections describes the Connect core available helpers.

=== Alias resolvers

Sometimes you might want to make an alias, that is, create a resolver which just converts one
name to another. For example:

[source,clojure]
----
(pc/defresolver alias-youtube-video [env {:user/keys [youtube-video-url]}]
  {::pc/input  #{:user/youtube-video-url}
   ::pc/output [:youtube.video/id]}
  {:youtube.video/id youtube-video-url})
----

The previous resolver will convert `:user/youtube-video-url` to `:youtube.video/id`. To make
that easy, Pathom provides some helpers:

[source,clojure]
----
; this returns a resolver that works just like the previous resolver
(def alias-youtube-video (pc/alias-resolver :user/youtube-video-url :youtube.video/id))
----

If you want to create an alias that goes in both directions, use `pc/alias-resolver2`.

=== Constant resolvers

This is a resolver that always returns a constant value for a given key. A common use
case is use this to set some default.

[source,clojure]
----
; using helper
(def answer-to-everything
  (pc/constantly-resolver :douglas.adams/answer-to-everything 42))

; is equivalente to:
(pc/defresolver answer-to-everything [_ _]
  {::pc/output [:douglas.adams/answer-to-everything]}
  {:douglas.adams/answer-to-everything 42})
----

=== Single attribute resolvers

For cases of a single transition from one attribute to another, you can use the
`single-attr-resolver` helper:

[source,clojure]
----
; convertion fn
(defn fahrenheit->celcius [x]
  (-> x (- 32) (* 5/9)))

; resolver with helper
(def f->c-resolver
  (pc/single-attr-resolver :unit/fahrenheit :unit/celcius fahrenheit->celcius))

; is equivalent to:
(pc/defresolver answer-to-everything [_ {:keys [unit/fahrenheit]}]
  {::pc/input  #{:unit/fahrenheit}
   ::pc/output [:unit/celcius]}
  {:unit/celcius (fahrenheit->celcius fahrenheit)})
----

Sometimes you also need to get some data from the environment, for those cases use
`single-attr-resolver2`, the difference is that this one will send `env` and the value
input as arguments to the provided function:

[source,clojure]
----
(defn single-with-env [env value]
  (* value (::multiplier env))

(def env-demo-resolver
  (pc/single-attr-resolver ::some-value ::other-value single-with-env))

; is equivalent to
(pc/defresolver answer-to-everything [env {::keys [some-value]}]
  {::pc/input  #{::some-value}
   ::pc/output [::other-value]}
  {::other-value (single-with-env env some-value)})
----

== Parameters

Parameters enable another dimension of information to be added to the request. Params have
different semantics from inputs: inputs are more a `dependency` thing while params are
more like options. In practice, the main difference is that inputs are something Pathom
will try to look up and make available, while parameters must always be provided at
query time, there have no auto resolution.
Common cases to use parameters are: pagination, sorting, filtering...

Let's write a resolver that outputs a sequence of instruments which can optionally be sorted
via a sorting criteria specified via a parameter.

[source,clojure]
----
(pc/defresolver instruments-list [env _]
  {::pc/output [{::instruments [:instrument/id :instrument/brand
                                :instrument/type :instrument/price]}]}
  (let [{:keys [sort]} (-> env :ast :params)] ; <1>
    {::instruments (cond->> instruments
                     (keyword? sort) (sort-by sort))}))
----

<1> Pulls the parameters from the environment

Then we can run queries like:

[source,clojure]
----
[(::instruments {:sort :instrument/brand})]
[(::instruments {:sort :instrument/price})]
[(::instruments {:sort :instrument/type})]

; params with join

[{(::instruments {:sort :instrument/price})
  [:instrument/id
   :instrument/brand]}]
----

Try it out:

++++

[(::instruments {:sort :instrument/price})]

++++

== N+1 Queries and Batch resolvers

When you have a to-many relation that is being resolved by a parser, you will typically end up with a single query that
finds the "IDs", and then `N` more queries to fill in the details of each item in the sequence.  This is known as the
`N+1` problem, and can be a source of significant performance problems.

Instead of running a resolver once for each item on the list, the idea to solve this problem is to send all the inputs
as a sequence, so the resolver can do some optimal implementation to handle multiple items. When this happens,
we call it a batch resolver. For example, let's take a look at the following demo:

[source,clojure]
----
include::example$com/wsscode/pathom/book/connect/batch.cljs[]
----

Try the demo:

++++

[{:items [:number-added]}]

++++

NOTE: This demo is using Pathom async parsers.  The resolvers in async parsers can return channels that (eventually)
resolve to the result, which is why you see `go` blocks in the code.  See <<AsyncParsing, Async Parsing>> for more details.
We use them in this demo so we can  "sleep" in a Javascript environment to mimic overhead in processing.
In the rest of the book we recommend using the parallel parser, the reason  to use the async parser in this demo is
that it more easily demonstrates the `N+1` issue.

You can note by the tracer that it took one second for each entry, a clear cascade,
because it had to call the `:number-added` resolver once for each item.

We can improving that by turning this into a batch resolver, like this:

[source,clojure]
----
include::example$com/wsscode/pathom/book/connect/batch2.cljs[]
----

Try the demo:

++++

[{:items [:number-added]}]

++++

Note that this time the sleep of one second only happened once, this is because when Pathom is processing a list and the
resolver supports batching, the resolver will get all the inputs in a single call, so your batch resolver can get all
the items in a single iteration. The results will be cached back for each entry, this will make the other items hit the
cache instead of calling the resolver again.

=== Batch transforms [[resolver-batch-transform]]

Starting on version `2.2.0` Pathom add some helpers to facilitate the creation of batch resolvers using Pathom
<<connect-transform,transform>> facilities.

In the previous example we manually detected if input was a sequence, this API is made this way so the resolver
keeps compatibility with the regular resolver API, but often it is easier if you get a consistent input (always a sequence
for example). We can enforce this using a transform:

[source,clojure]
----
(pc/defresolver slow-resolver [_ input]
  {::pc/input     #{:number}
   ::pc/output    [:number-added]
   ; use the transform, note we removed ::pc/batch? true, that's because the transform
   ; will add this for us
   ::pc/transform pc/transform-batch-resolver}
  (go
    (async/<! (async/timeout 1000))
    ; no need to detect sequence, it is always a sequence now
    (mapv (fn [v] {:number-added (inc (:number v))}) input)))
----

Try the demo:

++++

[{:items [:number-added]}]

++++

Another helper that Pathom provides is to transform a serial resolver that would run
one by one, into a batch that runs at concurrency `n`.

[source,clojure]
----
(pc/defresolver slow-resolver [_ {:keys [number]}]
  {::pc/input     #{:number}
   ::pc/output    [:number-added]
   ; set auto-batch with concurrency of 10
   ::pc/transform (pc/transform-auto-batch 10)}
  (go
    (async/<! (async/timeout 1000))
    ; dealing with the single case, as in the first example we did on batch
    {:number-added (inc number)}))
----

Try the demo:

++++

[{:items [:number-added]}]

++++

Note this time we did called resolver fn multiple times but in parallel, the way this may
impact the performance will vary case by case, I suggest giving some thought on the best
strategy for each case individually.

=== Aligning results

Often times when you do a batch request to some service/api the results won't come in
the same order of the request, also the count might not match in case some of the items
on request were invalid. To facilitate the coding of these cases Pathom provides
a helper to correctly sort the results back, for more info check the docs about
link:https://cljdoc.org/d/com.wsscode/pathom/CURRENT/api/com.wsscode.pathom.connect#batch-restore-sort[batch-restore-sort on cljdoc].

= Resolvers

In `Connect`, you implement the graph by creating `resolvers`. Those resolvers are functions that expose some data on the graph.

A resolver has a few basic elements:

. Inputs – A set of attributes that are required to be in the current parsing context
for the resolver to be able to work. Inputs is optional, no inputs means that the
resolver is always capable of working, independently of the current parsing context.
. Outputs - A query-like notation representing the shape of data the resolver is able
to resolve. This is typically a list of attributes/joins, where joins
typically include a simple subquery.
. A function - A `(fn [env input-data] tree-of-promised-output)` that takes the inputs
and turns them into a tree that satisfies the "output query".

So you might define a resolver like this:

[source,clojure]
----
(pc/defresolver person-resolver
  [{:keys [database] :as env} {:keys [person/id]}]
  {::pc/input #{:person/id}
   ::pc/output [:person/first-name :person/age]}
  (let [person (my-database/get-person database id)]
    {:person/age        (:age person)
     :person/first-name (:first-name person)}))
----

TIP: If you use link:https://cursive-ide.com/[Cursive], you can ask it to resolve the `pc/defresolver` as a `defn` and you will get proper symbol resolution

Where the `database` in the environment would be supplied when running the parser, and the input would have to be
found in the current context.  Remember that graph queries are contextual... you have to have a starting node to work
from, so in the above example we're assuming that during our parse we'll reach a point where the context contains
a `:person/id`.  The `my-database` stuff is just made up for this example, and is intended to show you that your
data source does not need to remotely match the schema of your graph query.

Pathom will scan through the defined resolvers in order to try to satisfy all of the properties in a query.  So, technically
you can split up your queries as much as makes sense into separate resolvers, and as long as the inputs are in the context
Pathom will assemble things back together.

Of course, it doesn't make sense in this case to do so, because each resolver would end up running a new query:

[source,clojure]
----
(pc/defresolver person-age-resolver [{:keys [database] :as env} {:keys [person/id]}]
  {::pc/input #{:person/id}
   ::pc/output [:person/age]}
  (let [person (my-database/get-person database id)]
    {:person/age (:age person)}))

(pc/defresolver person-first-name-resolver [{:keys [database] :as env} {:keys [person/id]}]
  {::pc/input #{:person/id}
   ::pc/output [:person/first-name]}
  (let [person (my-database/get-person database id)]
    {:person/first-name (:first-name person)}))

...
----

The point is that a single-level query like `[:person/id :person/first-name :person/age]` can be satisfied and "folded together"
by Pathom over any number of resolvers.

This fact is the basis of parser (de)composition and extensibility.  It can also come in handy for performance
refinements when there are computed attributes.

== Derived/Computed Attributes

There are times when you'd like to provide an attribute that is computed in some fashion.  You can, of course, simply
compute it within the resolver along with other properties like so:

[source,clojure]
----
(pc/defresolver person-resolver [{:keys [database] :as env} {:keys [person/id]}]
  {::pc/input #{:person/id}
   ::pc/output [:person/first-name :person/last-name :person/full-name :person/age]}
  (let [{:keys [age first-name last-name]} (my-database/get-person database id)]
    {:person/age        age
     :person/first-name first-name
     :person/last-name  last-name
     :person/full-name  (str first-name " " last-name) ; COMPUTED
     ...}))
----

but this means that you'll take the overhead of the computation when *any* query relate to person comes up.  You can
instead spread such attributes out into other resolvers as we discussed previously, which will only be invoked if the
query actually asks for those properties:

[source,clojure]
----
(pc/defresolver person-resolver [{:keys [database] :as env} {:keys [person/id]}]
  {::pc/input #{:person/id}
   ::pc/output [:person/first-name :person/last-name :person/age]}
  (let [{:keys [age first-name last-name]} (my-database/get-person database id)]
    {:person/age        age
     :person/first-name first-name
     :person/last-name  last-name}))

(pc/defresolver person-name-resolver [_ {:person/keys [first-name last-name]}]
  {::pc/input #{:person/first-name :person/last-name}
   ::pc/output [:person/full-name]}
  {:person/full-name (str first-name " " last-name)})
----

This combination of resolvers can still resolve all of the properties in `[:person/full-name :person/age]` (if
`:person/id` is in the context), but a query for just `[:person/age]` won't invoke any of the logic for the
`person-name-resolver`.

== Single Inputs -- Establishing Context [[SingleInputs]]

So far we have seen how to define a resolver that can work *as long as* the inputs are already in the environment.  You're
almost certainly wondering how to do that.

One way is to define <<GlobalResolvers,global resolvers>> and start the query from them, but very often you'd just
like to be able to say "I'd like the first name of person with id 42."

EQL uses "idents" to specify exactly that sort of query:

[source,clojure]
----
[{[:person/id 42] [:person/first-name]}]
----

The above is a join on an ident, and the expected result is a map with the ident as a key:

[source,clojure]
----
{[:person/id 42] {:person/first-name "Joe"}}
----

The query *itself* has everything you need to establish the *context* for running the `person-resolver`,
and in fact that is how Pathom single-input resolvers work.

If you use an ident in a query then Pathom is smart enough to know that it can use that ident to establish the context
for finding resolvers.  In other words, in the query above the ident `[:person/id 42]` is turned
into the *parsing context* `{:person/id 42}`, which satisfies the *input* of any resolver that needs
`:person/id` to run.

== Resolver Without Input -- Global Resolver [[GlobalResolvers]]

A resolver that requires no input can output its results at any point in the graph, thus it is really a global resolver.
Pay a particular attention to the *"at any point in the graph"* - it's not just at the root.  Thus, a resolver without inputs
can "inject" its outputs into any level of the query graph result.

We're going to start building a parser that can satisfy queries about a music store. So, we'll start with a global resolver
that can resolve the "latest product".  The code below shows the entire code needed, boilerplate and all:

[source,clojure]
----
include::example$com/wsscode/pathom/book/connect/getting_started.cljs[]
----

Our first resolver exposes the attribute `::latest-product`, and since it doesn't require any input it is a global resolver.
Also, note that our output description includes the full output details (including nested attributes), this is mostly
useful for auto-completion on UI's and automatic testing. If you return extra data it will still end up in the output
context.

Try some of these queries on the demo below:

[source,clojure]
----
[::latest-product]
[{::latest-product [:product/title]}]

; ::latest-product can be requested anywhere
[{::latest-product
  [* ::latest-product]}]
----

++++

[::latest-product]

++++

== Resolvers with single input

Next, let's say we want to have a new attribute which is the brand of the product. Of course, we could just throw the
data there in our other resolver, but the real power of Connect comes out when we start splitting the responsibilities
among resolvers, so let's define a resolver for brand that requires an input of `:product/id`:

[source,clojure]
----
include::example$com/wsscode/pathom/book/connect/getting_started2.cljs[]
----

++++

[{::latest-product [:product/title :product/brand]}]

++++

The input is a `set` containing the keys required on the current entity in the parsing context for the resolver to be
able to work. This is where `Connect` starts to shine because any time your query asks for a bit of data
it will try to figure it out how to satisfy that request based on the attributes that the current contextual entity *already has*.

More importantly: `Connect` will *explore the dependency graph* in order to resolve things if it needs to!
To illustrate this, let's pretend we have some external ID for the brand and that we can derive this ID from the
brand string - pretty much just another mapping:

[source,clojure]
----
;; a silly pretend lookup
(def brand->id {"Taylor" 44151})

(pc/defresolver brand-id-from-name [_ {:keys [product/brand]}]
  {::pc/input #{:product/brand}
   ::pc/output [:product/brand-id]}
  {:product/brand-id (get brand->id brand)})

(comment
  (parser {} [{::latest-product [:product/title :product/brand-id]}])
  ; => #::{:latest-product #:product{:title "Acoustic Guitar", :brand-id 44151}}
)
----

Note that our query never said anything about the `:product/brand`. `Connect` automatically walked the path
`:product/id -> :product/brand -> :product/brand-id` to obtain the information desired by the query!

When a required attribute is not present in the current entity, `Connect` will look for resolvers that can fetch it,
analyze their inputs, and recursively walk backwards towards the "known data" in the context.
When a required attribute is not present in the current entity, `Connect` will calculate the possible paths
from the data you have to the data you request, then it can use some heuristic to decide which path to take
and walk this path to reach the data, if there is no possible path connect reader will return `::p/continue` to
let another reader try to handle that key. You can read more about how this works in the Index page.

Also remember that single-input resolvers can handle ident-based queries.
Thus, the following ident-join queries already work without having to define anything else:

[source,clojure]
----
(parser {} [{[:product/id 1] [:product/brand]}])
; => {[:product/id 1] #:product{:brand "Taylor"}}

(parser {} [{[:product/brand "Taylor"] [:product/brand-id]}])
; => {[:product/brand "Taylor"] #:product{:brand-id 44151}}
----

== Multiple inputs

The input to a resolver is a set, and as such you can require more than one thing as input to your resolvers.  When doing
so, of course, your resolver function will receive all of the inputs requested; however, this also means that the parsing
context needs to contain them, or there must exist other resolvers that can use what's in the context to fill them in.

As you have seen before, the only way to provide ad-hoc information to connect is using the ident query, but in the ident
itself you can only provide one attribute at a time.

Since version `2.2.0-beta11` the ident readers from connect (`ident-reader` and `open-ident-reader`) support adding extra
context to the query using parameters. Let's say you want to load some customer data but you want to reduce the number
of resolvers called by providing some base information that you already have, you can issue a query like this:

[source,clojure]
----
[{([:customer/id 123] {:pathom/context {:customer/first-name "Foo" :customer/last-name "Bar"}})
  [:customer/full-name]}]
----

== Alias resolvers

Sometimes you might want to make an alias, that is, create a resolver which just converts one
name to another. For example:

[source,clojure]
----
(pc/defresolver alias-youtube-video [env {:user/keys [youtube-video-url]}]
  {::pc/input  #{:user/youtube-video-url}
   ::pc/output [:youtube.video/id]}
  {:youtube.video/id youtube-video-url})
----

The previous resolver will convert `:user/youtube-video-url` to `:youtube.video/id`. To make
that easy, Pathom provides some helpers:

[source,clojure]
----
; this returns a resolver that works just like the previous resolver
(def alias-youtube-video (pc/alias-resolver :user/youtube-video-url :youtube.video/id))
----

If you want to create an alias that goes in both directions, use `pc/alias-resolver2`.

== Parameters

Parameters enable another dimension of information to be added to the request. Params have
different semantics from inputs: inputs are more a `dependency` thing while params are
more like options. In practice, the main difference is that inputs are something Pathom
will try to look up and make available, while parameters must always be provided at
query time, there have no auto resolution.
Common cases to use parameters are: pagination, sorting, filtering...

Let's write a resolver that outputs a sequence of instruments which can optionally be sorted
via a sorting criteria specified via a parameter.

[source,clojure]
----
(pc/defresolver instruments-list [env _]
  {::pc/output [{::instruments [:instrument/id :instrument/brand
                                :instrument/type :instrument/price]}]}
  (let [{:keys [sort]} (-> env :ast :params)] ; <1>
    {::instruments (cond->> instruments
                     (keyword? sort) (sort-by sort))}))
----

<1> Pulls the parameters from the environment

Then we can run queries like:

[source,clojure]
----
[(::instruments {:sort :instrument/brand})]
[(::instruments {:sort :instrument/price})]
[(::instruments {:sort :instrument/type})]

; params with join

[{(::instruments {:sort :instrument/price})
  [:instrument/id
   :instrument/brand]}]
----

Try it out:

++++

[(::instruments {:sort :instrument/price})]

++++

== N+1 Queries and Batch resolvers

When you have a to-many relation that is being resolved by a parser, you will typically end up with a single query that
finds the "IDs", and then `N` more queries to fill in the details of each item in the sequence.  This is known as the
`N+1` problem, and can be a source of significant performance problems.

Instead of running a resolver once for each item on the list, the idea to solve this problem is to send all the inputs
as a sequence, so the resolver can do some optimal implementation to handle multiple items. When this happens,
we call it a batch resolver. For example, let's take a look at the following demo:

[source,clojure]
----
include::example$com/wsscode/pathom/book/connect/batch.cljs[]
----

Try the demo:

++++

[{:items [:number-added]}]

++++

NOTE: This demo is using Pathom async parsers.  The resolvers in async parsers can return channels that (eventually)
resolve to the result, which is why you see `go` blocks in the code.  See <<AsyncParsing, Async Parsing>> for more details.
We use them in this demo so we can  "sleep" in a Javascript environment to mimic overhead in processing.
In the rest of the book we recommend using the parallel parser, the reason  to use the async parser in this demo is
that it more easily demonstrates the `N+1` issue.

You can note by the tracer that it took one second for each entry, a clear cascade,
because it had to call the `:number-added` resolver once for each item.

We can improving that by turning this into a batch resolver, like this:

[source,clojure]
----
include::example$com/wsscode/pathom/book/connect/batch2.cljs[]
----

Try the demo:

++++

[{:items [:number-added]}]

++++

Note that this time the sleep of one second only happened once, this is because when Pathom is processing a list and the
resolver supports batching, the resolver will get all the inputs in a single call, so your batch resolver can get all
the items in a single iteration. The results will be cached back for each entry, this will make the other items hit the
cache instead of calling the resolver again.

=== Batch transforms [[resolver-batch-transform]]

Starting on version `2.2.0` Pathom add some helpers to facilitate the creation of batch resolvers using Pathom
<<connect-transform,transform>> facilities.

In the previous example we manually detected if input was a sequence, this API is made this way so the resolver
keeps compatibility with the regular resolver API, but often it is easier if you get a consistent input (always a sequence
for example). We can enforce this using a transform:

[source,clojure]
----
(pc/defresolver slow-resolver [_ input]
  {::pc/input     #{:number}
   ::pc/output    [:number-added]
   ; use the transform, note we removed ::pc/batch? true, that's because the transform
   ; will add this for us
   ::pc/transform pc/transform-batch-resolver}
  (go
    (async/<! (async/timeout 1000))
    ; no need to detect sequence, it is always a sequence now
    (mapv (fn [v] {:number-added (inc (:number v))}) input)))
----

Try the demo:

++++

[{:items [:number-added]}]

++++

Another helper that Pathom provides is to transform a serial resolver that would run
one by one, into a batch that runs at concurrency `n`.

[source,clojure]
----
(pc/defresolver slow-resolver [_ {:keys [number]}]
  {::pc/input     #{:number}
   ::pc/output    [:number-added]
   ; set auto-batch with concurrency of 10
   ::pc/transform (pc/transform-auto-batch 10)}
  (go
    (async/<! (async/timeout 1000))
    ; dealing with the single case, as in the first example we did on batch
    {:number-added (inc number)}))
----

Try the demo:

++++

[{:items [:number-added]}]

++++

Note this time we did called resolver fn multiple times but in parallel, the way this may
impact the performance will vary case by case, I suggest giving some thought on the best
strategy for each case individually.

=== Aligning results

Often times when you do a batch request to some service/api the results won't come in
the same order of the request, also the count might not match in case some of the items
on request were invalid. To facilitate the coding of these cases Pathom provides
a helper to correctly sort the results back, for more info check the docs about
link:https://cljdoc.org/d/com.wsscode/pathom/CURRENT/api/com.wsscode.pathom.connect#batch-restore-sort[batch-restore-sort on cljdoc].


Update upgrade docs to include date and link to changelog.

////
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.
////

TinkerPop 3.1.0
===============

image::https://raw.githubusercontent.com/apache/incubator-tinkerpop/master/docs/static/images/gremlin-gangster.png[width=225]

*A 187 On The Undercover Gremlinz*

TinkerPop 3.1.0
---------------

*Release Date: November 16, 2015*

Please see the link:https://github.com/apache/incubator-tinkerpop/blob/3.1.0-incubating/CHANGELOG.asciidoc#tinkerpop-310-release-date-november-16-2015] for a complete list of all the modifications that are part of this release.

Additional upgrade information can be found here:

* <<_tinkerpop_3_0_2,TinkerPop 3.0.2>>
* <<_tinkerpop_3_0_1,TinkerPop 3.0.1>>

Upgrading for Users
~~~~~~~~~~~~~~~~~~~

Shading Jackson
^^^^^^^^^^^^^^^

The Jackson library is now shaded to `gremlin-shaded`, which will allow Jackson to version independently without
breaking compatibility with dependent libraries or with those who depend on TinkerPop.  The downside is that if a
library depends on TinkerPop and uses the Jackson classes, those classes will no longer exist with the standard
Jackson package naming.  They will have to shifted as follows:

* `org.objenesis` becomes `org.apache.tinkerpop.shaded.objenesis`
* `com.esotericsoftware.minlog` becomes `org.apache.tinkerpop.shaded.minlog`
* `com.fasterxml.jackson` becomes `org.apache.tinkerpop.shaded.jackson`

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-835[TINKERPOP3-835]

PartitionStrategy and VertexProperty
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

`PartitionStrategy` now supports partitioning within `VertexProperty`.  The `Graph` needs to be able to support
meta-properties for this feature to work.

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-333[TINKERPOP3-333]

Gremlin Server and Epoll
^^^^^^^^^^^^^^^^^^^^^^^^

Gremlin Server provides a configuration option to turn on support for Netty
link:http://netty.io/wiki/native-transports.html[native transport] on Linux, which has been shown to help improve
performance.

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-901[TINKERPOP3-901]

Rebindings Deprecated
^^^^^^^^^^^^^^^^^^^^^

The notion of "rebindings" has been deprecated in favor of the term "aliases".  Alias is a better and more intuitive
term than rebindings which should make it easier for newcomers to understand what they are for.

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-913[TINKERPOP3-913],
link:http://tinkerpop.incubator.apache.org/docs/3.1.0-incubating/#_aliases[Reference Documentation - Aliases]

Configurable Driver Channelizer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The Gremlin Driver now allows the `Channerlizer` to be supplied as a configuration, which means that custom
implementations may be supplied.

See: https://issues.apache.org/jira/browse/TINKERPOP3-680[TINKERPOP3-680]

GraphSON and Strict Option
^^^^^^^^^^^^^^^^^^^^^^^^^^

The `GraphMLReader` now has a `strict` option on the `Builder` so that if a data type for a value is invalid in some
way, GraphMLReader will simply skip that problem value. In that way, it is a bit more forgiving than before especially
with empty data.

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-756[TINKERPOP3-756]

Transaction.close() Default Behavior
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The default behavior of `Transaction.close()` is to rollback the transaction.  This is in contrast to previous versions
where the default behavior was commit.  Using rollback as the default should be thought of as a like a safer approach
to closing where a user must now explicitly call `commit()` to persist their mutations.

See link:https://issues.apache.org/jira/browse/TINKERPOP3-805[TINKERPOP3-805] for more information.

ThreadLocal Transaction Settings
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The `Transaction.onReadWrite()` and `Transaction.onClose()` settings now need to be set for each thread (if another
behavior than the default is desired). For gremlin-server users that may be changing these settings via scripts.
If the settings are changed for a sessionless request they will now only apply to that one request. If the settings are
changed for an in-session request they will now only apply to all future requests made in the scope of that session.

See link:https://issues.apache.org/jira/browse/TINKERPOP3-885[TINKERPOP3-885]

Hadoop-Gremlin
^^^^^^^^^^^^^^

* Hadoop1 is no longer supported. Hadoop2 is now the only supported Hadoop version in TinkerPop.
* Spark and Giraph have been split out of Hadoop-Gremlin into their own respective packages (Spark-Gremlin and Giraph-Gremlin).
* The directory where application jars are stored in HDFS is now `hadoop-gremlin-x.y.z-libs`.
** This versioning is important so that cross-version TinkerPop use does not cause jar conflicts.

See link:https://issues.apache.org/jira/browse/TINKERPOP3-616

Spark-Gremlin
^^^^^^^^^^^^^

* Providers that wish to reuse a graphRDD can leverage the new `PersistedInputRDD` and `PersistedOutputRDD`.
** This allows the graphRDD to avoid serialization into HDFS for reuse. Be sure to enabled persisted `SparkContext` (see documentation).

See link:https://issues.apache.org/jira/browse/TINKERPOP3-868,
link:https://issues.apache.org/jira/browse/TINKERPOP3-925

TinkerGraph Serialization
^^^^^^^^^^^^^^^^^^^^^^^^^

TinkerGraph is serializable over Gryo, which means that it can shipped over the wire from Gremlin Server.  This
feature can be useful when working with remote subgraphs.

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-728[TINKERPOP3-728]

Deprecation in TinkerGraph
^^^^^^^^^^^^^^^^^^^^^^^^^^

The `public static String` configurations have been renamed. The old `public static` variables have been deprecated.
If the deprecated variables were being used, then convert to the replacements as soon as possible.

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-926[TINKERPOP3-926]

Deprecation in Gremlin-Groovy
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The closure wrappers classes `GFunction`, `GSupplier`, `GConsumer` have been deprecated. In Groovy, a closure can be
specified using `as Function` and thus, these wrappers are not needed. Also, the `GremlinExecutor.promoteBindings()`
method which was previously deprecated has been removed.

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-879[TINKERPOP3-879],
link:https://issues.apache.org/jira/browse/TINKERPOP3-897[TINKERPOP3-897]

Gephi Traversal Visualization
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The process for visualizing a traversal has been simplified.  There is no longer a need to "name" steps that will
represent visualization points for Gephi.  It is possible to just "configure" a `visualTraversal` in the console:

[source,text]
gremlin> :remote config visualTraversal graph vg

which creates a special `TraversalSource` from `graph` called `vg`.  The traversals created from `vg` can be used
to `:submit` to Gephi.

See: link:http://tinkerpop.incubator.apache.org/docs/3.1.0-SNAPSHOT/#gephi-plugin[Reference Documentation - Gephi]

Alterations to GraphTraversal
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

There were a number of changes to `GraphTraversal`.  Many of the changes came by way of deprecation, but some semantics
have changed as well:

* `ConjunctionStrategy` has been renamed to `ConnectiveStrategy` (no other behaviors changed).
* `ConjunctionP` has been renamed to `ConnectiveP` (no other behaviors changed).
* `DedupBijectionStrategy` has been renamed (and made more effective) as `FilterRankingStrategy`.
* The `GraphTraversal` mutation API has change significantly with all previous methods being supported but deprecated.
** The general pattern used now is `addE('knows').from(select('a')).to(select('b')).property('weight',1.0)`.
* The `GraphTraversal` sack API has changed with all previous methods being supported but deprecated.
** The old `sack(mult,'weight')` is now `sack(mult).by('weight')`.
* `GroupStep` has been redesigned such that there is now only a key- and value-traversal. No more reduce-traversal.
** The previous `group()`-methods have been renamed to `groupV3d0()`. To immediately upgrade, rename all your `group()`-calls to `groupV3d0()`.
** To migrate to the new `group()`-methods, what was `group().by('age').by(outE()).by(sum(local))` is now `group().by('age').by(outE().sum())`.
* There was a bug in `fold()`, where if a bulked traverser was provided, the traverser was only represented once.
** This bug fix might cause a breaking change to a user query if the non-bulk behavior was being counted on. If so, used `dedup()` prior to `fold()`.
* Both `GraphTraversal().mapKeys()` and `GraphTraversal.mapValues()` has been deprecated.
** Use `select(keys)` and `select(columns)`. However, note that `select()` will not unroll the keys/values. Thus, `mapKeys()` => `select(keys).unfold()`.
* The data type of `Operator` enums will now always be the highest common data type of the two given numbers, rather than the data type of the first number, as it's been before.

Aliasing Remotes in the Console
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The `:remote` command in Gremlin Console has a new `alias` configuration option.  This `alias` option allows
specification of a set of key/value alias/binding pairs to apply to the remote.  In this way, it becomes possible
to refer to a variable on the server as something other than what it is referred to for purpose of the submitted
script.  For example once a `:remote` is created, this command:

[source,text]
:remote alias x g

would allow "g" on the server to be referred to as "x".

[source,text]
:> x.E().label().groupCount()

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-914[TINKERPOP3-914]

Upgrading for Providers
~~~~~~~~~~~~~~~~~~~~~~~

IMPORTANT: It is recommended that providers also review all the upgrade instructions specified for users. Many of the
changes there may prove important for the provider's implementation.

All providers should be aware that Jackson is now shaded to `gremlin-shaded` and could represent breaking change if
there was usage of the dependency by way of TinkerPop, a direct dependency to Jackson may be required on the
provider's side.

Graph System Providers
^^^^^^^^^^^^^^^^^^^^^^

GraphStep Alterations
+++++++++++++++++++++

* `GraphStep` is no longer in `sideEffect`-package, but now in `map`-package as traversals support mid-traversal `V()`.
* Traversals now support mid-traversal `V()`-steps. Graph system providers should ensure that a mid-traversal `V()` can leverage any suitable index.

See link:https://issues.apache.org/jira/browse/TINKERPOP3-762

Decomposition of AbstractTransaction
++++++++++++++++++++++++++++++++++++

The `AbstractTransaction` class has been abstracted into two different classes supporting two different modes of
operation: `AbstractThreadLocalTransaction` and `AbstractThreadedTransaction`, where the former should be used when
supporting `ThreadLocal` transactions and the latter for threaded transactions.  Of course, providers may still
choose to build their own implementation on `AbstractTransaction` itself or simply implement the `Transaction`
interface.

The `AbstractTransaction` gains the following methods to potentially implement (though default implementations
are supplied in `AbstractThreadLocalTransaction` and `AbstractThreadedTransaction`):

* `doReadWrite` that should execute the read-write consumer.
* `doClose` that should execute the close consumer.

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-765[TINKERPOP3-765],
link:https://issues.apache.org/jira/browse/TINKERPOP3-885[TINKERPOP3-885]

Transaction.close() Default Behavior
++++++++++++++++++++++++++++++++++++

The default behavior for `Transaction.close()` is to rollback the transaction and is enforced by tests, which
previously asserted the opposite (i.e. commit on close).  These tests have been renamed to suite the new semantics:

* `shouldCommitOnCloseByDefault` became `shouldCommitOnCloseWhenConfigured`
* `shouldRollbackOnCloseWhenConfigured` became `shouldRollbackOnCloseByDefault`

If these tests were referenced in an `OptOut`, then their names should be updated.

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-805[TINKERPOP3-805]

Graph Traversal Updates
+++++++++++++++++++++++

There were numerous changes to the `GraphTraversal` API. Nearly all changes are backwards compatible with respective
"deprecated" annotations. Please review the respective updates specified in the "Graph System Users" section.

* `GraphStep` is no longer in `sideEffect` package. Now in `map` package.
* Make sure mid-traversal `GraphStep` calls are folding `HasContainers` in for index-lookups.
* Think about copying `TinkerGraphStepStrategyTest` for your implementation so you know folding is happening correctly.

Element Removal
+++++++++++++++

`Element.Exceptions.elementAlreadyRemoved` has been deprecated and test enforcement for consistency have been removed.
 Providers are free to deal with deleted elements as they see fit.

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-297[TINKERPOP3-297]

VendorOptimizationStrategy Rename
+++++++++++++++++++++++++++++++++

The `VendorOptimizationStrategy` has been renamed to `ProviderOptimizationStrategy`.  This renaming is consistent
with revised terminology for what were formerly referred to as "vendors".

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-876[TINKERPOP3-876]

GraphComputer Updates
+++++++++++++++++++++

`GraphComputer.configure(String key, Object value)` is now a method (with default implementation).
This allows the user to specify engine-specific parameters to the underlying OLAP system. These parameters are not intended
to be cross engine supported. Moreover, if there are not parameters that can be altered (beyond the standard `GraphComputer`
methods), then the provider's `GraphComputer` implementation should simply return and do nothing.

Driver Providers
^^^^^^^^^^^^^^^^

Aliases Parameter
+++++++++++++++++

The "rebindings" argument to the "standard" `OpProcessor` has been renamed to "aliases". While "rebindings" is still
supported it is recommended that the upgrade to "aliases" be made as soon as possible as support will be removed in
the future.  Gremlin Server will not accept both parameters at the same time - a request must contain either one
parameter or the other if either is supplied.

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-913[TINKERPOP3-913]

ThreadLocal Transaction Settings
++++++++++++++++++++++++++++++++

If a driver configures the `Transaction.onReadWrite()` or `Transaction.onClose()` settings, note that these settings no
longer apply to all future requests. If the settings are changed for a sessionless request they will only apply to
that one request. If the settings are changed from an in-session request they will only apply to all future requests
made in the scope of that session.

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-885[TINKERPOP3-885]

////
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.
////

TinkerPop 3.1.0
===============

image::https://raw.githubusercontent.com/apache/incubator-tinkerpop/master/docs/static/images/gremlin-gangster.png[width=225]

*A 187 On The Undercover Gremlinz*

TinkerPop 3.1.0
---------------

*Release Date: NOT OFFICIALLY RELEASED YET*

Please see the link:https://github.com/apache/incubator-tinkerpop/blob/3.1.0-incubating/CHANGELOG.asciidoc#XXXXXXXXXXXXXXXXXXXXXXXXXXXX[changelog-(NOT FINAL)] for a complete list of all the modifications that are part of this release.

Additional upgrade information can be found here:

* <<_tinkerpop_3_0_2,TinkerPop 3.0.2>>
* <<_tinkerpop_3_0_1,TinkerPop 3.0.1>>

Upgrading for Users
~~~~~~~~~~~~~~~~~~~

Shading Jackson
^^^^^^^^^^^^^^^

The Jackson library is now shaded to `gremlin-shaded`, which will allow Jackson to version independently without
breaking compatibility with dependent libraries or with those who depend on TinkerPop.  The downside is that if a
library depends on TinkerPop and uses the Jackson classes, those classes will no longer exist with the standard
Jackson package naming.  They will have to shifted as follows:

* `org.objenesis` becomes `org.apache.tinkerpop.shaded.objenesis`
* `com.esotericsoftware.minlog` becomes `org.apache.tinkerpop.shaded.minlog`
* `com.fasterxml.jackson` becomes `org.apache.tinkerpop.shaded.jackson`

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-835[TINKERPOP3-835]

PartitionStrategy and VertexProperty
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

`PartitionStrategy` now supports partitioning within `VertexProperty`.  The `Graph` needs to be able to support
meta-properties for this feature to work.

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-333[TINKERPOP3-333]

Gremlin Server and Epoll
^^^^^^^^^^^^^^^^^^^^^^^^

Gremlin Server provides a configuration option to turn on support for Netty
link:http://netty.io/wiki/native-transports.html[native transport] on Linux, which has been shown to help improve
performance.

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-901[TINKERPOP3-901]

Rebindings Deprecated
^^^^^^^^^^^^^^^^^^^^^

The notion of "rebindings" has been deprecated in favor of the term "aliases".  Alias is a better and more intuitive
term than rebindings which should make it easier for newcomers to understand what they are for.

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-913[TINKERPOP3-913],
link:http://tinkerpop.incubator.apache.org/docs/3.1.0-incubating/#_aliases[Reference Documentation - Aliases]

Configurable Driver Channelizer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The Gremlin Driver now allows the `Channerlizer` to be supplied as a configuration, which means that custom
implementations may be supplied.

See: https://issues.apache.org/jira/browse/TINKERPOP3-680[TINKERPOP3-680]

GraphSON and Strict Option
^^^^^^^^^^^^^^^^^^^^^^^^^^

The `GraphMLReader` now has a `strict` option on the `Builder` so that if a data type for a value is invalid in some
way, GraphMLReader will simply skip that problem value. In that way, it is a bit more forgiving than before especially
with empty data.

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-756[TINKERPOP3-756]

Transaction.close() Default Behavior
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The default behavior of `Transaction.close()` is to rollback the transaction.  This is in contrast to previous versions
where the default behavior was commit.  Using rollback as the default should be thought of as a like a safer approach
to closing where a user must now explicitly call `commit()` to persist their mutations.

See link:https://issues.apache.org/jira/browse/TINKERPOP3-805[TINKERPOP3-805] for more information.

ThreadLocal Transaction Settings
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The `Transaction.onReadWrite()` and `Transaction.onClose()` settings now need to be set for each thread (if another
behavior than the default is desired). For gremlin-server users that may be changing these settings via scripts.
If the settings are changed for a sessionless request they will now only apply to that one request. If the settings are
changed for an in-session request they will now only apply to all future requests made in the scope of that session.

See link:https://issues.apache.org/jira/browse/TINKERPOP3-885[TINKERPOP3-885]

Hadoop-Gremlin
^^^^^^^^^^^^^^

* Hadoop1 is no longer supported. Hadoop2 is now the only supported Hadoop version in TinkerPop.
* Spark and Giraph have been split out of Hadoop-Gremlin into their own respective packages (Spark-Gremlin and Giraph-Gremlin).
* The directory where application jars are stored in HDFS is now `hadoop-gremlin-x.y.z-libs`.
** This versioning is important so that cross-version TinkerPop use does not cause jar conflicts.

See link:https://issues.apache.org/jira/browse/TINKERPOP3-616

Spark-Gremlin
^^^^^^^^^^^^^

* Providers that wish to reuse a graphRDD can leverage the new `PersistedInputRDD` and `PersistedOutputRDD`.
** This allows the graphRDD to avoid serialization into HDFS for reuse. Be sure to enabled persisted `SparkContext` (see documentation).

See link:https://issues.apache.org/jira/browse/TINKERPOP3-868,
link:https://issues.apache.org/jira/browse/TINKERPOP3-925

TinkerGraph Serialization
^^^^^^^^^^^^^^^^^^^^^^^^^

TinkerGraph is serializable over Gryo, which means that it can shipped over the wire from Gremlin Server.  This
feature can be useful when working with remote subgraphs.

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-728[TINKERPOP3-728]

Deprecation in TinkerGraph
^^^^^^^^^^^^^^^^^^^^^^^^^^

The `public static String` configurations have been renamed. The old `public static` variables have been deprecated.
If the deprecated variables were being used, then convert to the replacements as soon as possible.

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-926[TINKERPOP3-926]

Deprecation in Gremlin-Groovy
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The closure wrappers classes `GFunction`, `GSupplier`, `GConsumer` have been deprecated. In Groovy, a closure can be
specified using `as Function` and thus, these wrappers are not needed. Also, the `GremlinExecutor.promoteBindings()`
method which was previously deprecated has been removed.

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-879[TINKERPOP3-879],
link:https://issues.apache.org/jira/browse/TINKERPOP3-897[TINKERPOP3-897]

Gephi Traversal Visualization
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The process for visualizing a traversal has been simplified.  There is no longer a need to "name" steps that will
represent visualization points for Gephi.  It is possible to just "configure" a `visualTraversal` in the console:

[source,text]
gremlin> :remote config visualTraversal graph vg

which creates a special `TraversalSource` from `graph` called `vg`.  The traversals created from `vg` can be used
to `:submit` to Gephi.

See: link:http://tinkerpop.incubator.apache.org/docs/3.1.0-SNAPSHOT/#gephi-plugin[Reference Documentation - Gephi]

Alterations to GraphTraversal
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

There were a number of changes to `GraphTraversal`.  Many of the changes came by way of deprecation, but some semantics
have changed as well:

* `ConjunctionStrategy` has been renamed to `ConnectiveStrategy` (no other behaviors changed).
* `ConjunctionP` has been renamed to `ConnectiveP` (no other behaviors changed).
* `DedupBijectionStrategy` has been renamed (and made more effective) as `FilterRankingStrategy`.
* The `GraphTraversal` mutation API has change significantly with all previous methods being supported but deprecated.
** The general pattern used now is `addE('knows').from(select('a')).to(select('b')).property('weight',1.0)`.
* The `GraphTraversal` sack API has changed with all previous methods being supported but deprecated.
** The old `sack(mult,'weight')` is now `sack(mult).by('weight')`.
* `GroupStep` has been redesigned such that there is now only a key- and value-traversal. No more reduce-traversal.
** The previous `group()`-methods have been renamed to `groupV3d0()`. To immediately upgrade, rename all your `group()`-calls to `groupV3d0()`.
** To migrate to the new `group()`-methods, what was `group().by('age').by(outE()).by(sum(local))` is now `group().by('age').by(outE().sum())`.
* There was a bug in `fold()`, where if a bulked traverser was provided, the traverser was only represented once.
** This bug fix might cause a breaking change to a user query if the non-bulk behavior was being counted on. If so, used `dedup()` prior to `fold()`.
* Both `GraphTraversal().mapKeys()` and `GraphTraversal.mapValues()` has been deprecated.
** Use `select(keys)` and `select(columns)`. However, note that `select()` will not unroll the keys/values. Thus, `mapKeys()` => `select(keys).unfold()`.
* The data type of `Operator` enums will now always be the highest common data type of the two given numbers, rather than the data type of the first number, as it's been before.

Aliasing Remotes in the Console
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The `:remote` command in Gremlin Console has a new `alias` configuration option.  This `alias` option allows
specification of a set of key/value alias/binding pairs to apply to the remote.  In this way, it becomes possible
to refer to a variable on the server as something other than what it is referred to for purpose of the submitted
script.  For example once a `:remote` is created, this command:

[source,text]
:remote alias x g

would allow "g" on the server to be referred to as "x".

[source,text]
:> x.E().label().groupCount()

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-914[TINKERPOP3-914]

Upgrading for Providers
~~~~~~~~~~~~~~~~~~~~~~~

IMPORTANT: It is recommended that providers also review all the upgrade instructions specified for users. Many of the
changes there may prove important for the provider's implementation.

All providers should be aware that Jackson is now shaded to `gremlin-shaded` and could represent breaking change if
there was usage of the dependency by way of TinkerPop, a direct dependency to Jackson may be required on the
provider's side.

Graph System Providers
^^^^^^^^^^^^^^^^^^^^^^

GraphStep Alterations
+++++++++++++++++++++

* `GraphStep` is no longer in `sideEffect`-package, but now in `map`-package as traversals support mid-traversal `V()`.
* Traversals now support mid-traversal `V()`-steps. Graph system providers should ensure that a mid-traversal `V()` can leverage any suitable index.

See link:https://issues.apache.org/jira/browse/TINKERPOP3-762

Decomposition of AbstractTransaction
++++++++++++++++++++++++++++++++++++

The `AbstractTransaction` class has been abstracted into two different classes supporting two different modes of
operation: `AbstractThreadLocalTransaction` and `AbstractThreadedTransaction`, where the former should be used when
supporting `ThreadLocal` transactions and the latter for threaded transactions.  Of course, providers may still
choose to build their own implementation on `AbstractTransaction` itself or simply implement the `Transaction`
interface.

The `AbstractTransaction` gains the following methods to potentially implement (though default implementations
are supplied in `AbstractThreadLocalTransaction` and `AbstractThreadedTransaction`):

* `doReadWrite` that should execute the read-write consumer.
* `doClose` that should execute the close consumer.

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-765[TINKERPOP3-765],
link:https://issues.apache.org/jira/browse/TINKERPOP3-885[TINKERPOP3-885]

Transaction.close() Default Behavior
++++++++++++++++++++++++++++++++++++

The default behavior for `Transaction.close()` is to rollback the transaction and is enforced by tests, which
previously asserted the opposite (i.e. commit on close).  These tests have been renamed to suite the new semantics:

* `shouldCommitOnCloseByDefault` became `shouldCommitOnCloseWhenConfigured`
* `shouldRollbackOnCloseWhenConfigured` became `shouldRollbackOnCloseByDefault`

If these tests were referenced in an `OptOut`, then their names should be updated.

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-805[TINKERPOP3-805]

Graph Traversal Updates
+++++++++++++++++++++++

There were numerous changes to the `GraphTraversal` API. Nearly all changes are backwards compatible with respective
"deprecated" annotations. Please review the respective updates specified in the "Graph System Users" section.

* `GraphStep` is no longer in `sideEffect` package. Now in `map` package.
* Make sure mid-traversal `GraphStep` calls are folding `HasContainers` in for index-lookups.
* Think about copying `TinkerGraphStepStrategyTest` for your implementation so you know folding is happening correctly.

Element Removal
+++++++++++++++

`Element.Exceptions.elementAlreadyRemoved` has been deprecated and test enforcement for consistency have been removed.
 Providers are free to deal with deleted elements as they see fit.

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-297[TINKERPOP3-297]

VendorOptimizationStrategy Rename
+++++++++++++++++++++++++++++++++

The `VendorOptimizationStrategy` has been renamed to `ProviderOptimizationStrategy`.  This renaming is consistent
with revised terminology for what were formerly referred to as "vendors".

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-876[TINKERPOP3-876]

GraphComputer Updates
+++++++++++++++++++++

`GraphComputer.configure(String key, Object value)` is now a method (with default implementation).
This allows the user to specify engine-specific parameters to the underlying OLAP system. These parameters are not intended
to be cross engine supported. Moreover, if there are not parameters that can be altered (beyond the standard `GraphComputer`
methods), then the provider's `GraphComputer` implementation should simply return and do nothing.

Driver Providers
^^^^^^^^^^^^^^^^

Aliases Parameter
+++++++++++++++++

The "rebindings" argument to the "standard" `OpProcessor` has been renamed to "aliases". While "rebindings" is still
supported it is recommended that the upgrade to "aliases" be made as soon as possible as support will be removed in
the future.  Gremlin Server will not accept both parameters at the same time - a request must contain either one
parameter or the other if either is supplied.

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-913[TINKERPOP3-913]

ThreadLocal Transaction Settings
++++++++++++++++++++++++++++++++

If a driver configures the `Transaction.onReadWrite()` or `Transaction.onClose()` settings, note that these settings no
longer apply to all future requests. If the settings are changed for a sessionless request they will only apply to
that one request. If the settings are changed from an in-session request they will only apply to all future requests
made in the scope of that session.

See: link:https://issues.apache.org/jira/browse/TINKERPOP3-885[TINKERPOP3-885]