Skip to content

Commit 4c9f7f8

Browse files
committed
Merge branch '3.8-dev'
2 parents b86ae26 + 43406f3 commit 4c9f7f8

2 files changed

Lines changed: 179 additions & 0 deletions

File tree

docs/src/dev/future/index.asciidoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ story.
165165
|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 |Y
166166
|link:https://github.com/apache/tinkerpop/blob/master/docs/src/dev/future/proposal-transaction-4[Proposal 4] |TinkerGraph Transaction Support |3.7.0 |Y
167167
|link:https://github.com/apache/tinkerpop/blob/master/docs/src/dev/future/proposal-scoping-5[Proposal 5] |Lazy vs. Eager Evaluation|3.8.0 |N
168+
|link:https://github.com/apache/tinkerpop/blob/master/docs/src/dev/future/proposal-scoping-5[Proposal 6] |asNumber() Step|3.8.0 |N
168169
|=========================================================
169170
170171
= Appendix
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
////
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
(the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
////
17+
18+
image::apache-tinkerpop-logo.png[width=500,link="https://tinkerpop.apache.org"]
19+
20+
*x.y.z - Proposal 6*
21+
22+
== asNumber() Step
23+
24+
=== Motivation
25+
26+
Given the addition of the `asString()` and `asDate()` steps in the 3.7 line, this proposal seeks to bridge another gap in language functionality, which is number casting.
27+
28+
=== Definition
29+
30+
The `asNumber()` step will convert the incoming traverser to the nearest parsable type (e.g. int or double) if no argument is provided, or to the desired numerical type, based on the number token (`N`) provided. Like the `asDate()` step, it will not be scoped (for now, scopes can be added in the future).
31+
32+
The proposed tokens, subject to change based on final implementation, are:
33+
`N.byte`, `N.short`, `N.int`, `N.long`, `N.float`, `N.double`, `N.bigInt`, `N.bigDecimal`
34+
35+
The overloads are:
36+
37+
* `asNumber()`
38+
* `asNumber(N)`
39+
40+
The incoming traverser can be of type:
41+
42+
*Number* - the conversion will become a casting operation:
43+
[source]
44+
----
45+
gremlin> g.inject(5).asNumber()
46+
==> 5 // parses to int
47+
gremlin> g.inject(5.0).asNumber()
48+
==> 5 // parses to double
49+
gremlin> g.inject(5.123f).asNumber()
50+
==> 5.123 // will cast float to double
51+
----
52+
53+
* Double to whole number types will be truncated via floor operation
54+
55+
[source]
56+
----
57+
gremlin> g.inject(5.43).asNumber(N.int)
58+
==> 5
59+
gremlin> g.inject(5.67).asNumber(N.int)
60+
==> 5
61+
----
62+
* Widening of types will be a simple cast
63+
[source]
64+
----
65+
gremlin> g.inject(5).asNumber(N.long)
66+
==> 5
67+
----
68+
* Narrowing of types may result in Overflow Exception
69+
[source]
70+
----
71+
gremlin> g.inject(12).asNumber(N.byte)
72+
==> 12
73+
gremlin> g.inject(128).asNumber(N.byte)
74+
==> Overflow Exception
75+
gremlin> g.inject(300).asNumber(N.byte)
76+
==> Overflow Exception
77+
----
78+
79+
*String* - the conversion will become a parsing operation:
80+
81+
* Parsable strings will be parsed to the default type or type specified. Overflow will be treated the same way as if a number was the input.
82+
** Note we can keep things simple for the initial implementation, and throw Parsing Exception for all non-numerical strings, regardless if they are the recognized tokens in Java or Gremlin language. In other words, all strings below will be considered illegal inputs:
83+
*** Java/Groovy type - “1.0f”, “1.0d”, “1L”
84+
*** Java parsing function limits - “1.0f”, “1.0d”
85+
*** Gremlin Lang - “1B”, “1S”, “1L”, “1N”, “1.0D”, “1.0F”, “1.0M”
86+
[source]
87+
----
88+
gremlin> g.inject("5").asNumber()
89+
==> 5
90+
gremlin> g.inject("5").asNumber(N.int)
91+
==> 5
92+
gremlin> g.inject("1,000").asNumber(N.int)
93+
==> Parsing Exception
94+
gremlin> g.inject("128").asNumber(N.byte)
95+
==> Parsing/Overflow Exception
96+
----
97+
98+
* Semi-parsable strings - do we throw exceptions immediately or try to find our way to the specified token type if possible? [Discussion] point.
99+
[source]
100+
----
101+
// Given "1.0" should be parsing into double
102+
gremlin> g.inject("1.0").asNumber(N.int)
103+
==> Parsing Exception
104+
// asNumber() will parse to double, then user will chain with casting to N.int
105+
gremlin> g.inject("1.0").asNumber().asNumber(N.int)
106+
==> 1
107+
gremlin> g.inject("1.0").subString(0,1).asNumber(N.int)
108+
==> 1
109+
110+
Other Options:
111+
// Make the step smart to recognize it can be parsed then casted:
112+
// 1) parse to double
113+
// 2) cast to token type
114+
gremlin> g.inject("1.0").asNumber(N.int)
115+
==> 1
116+
117+
// Or cast via substring based on types:
118+
// 1) trunct all decimal place of string (make sure string is parsable)
119+
// 2) cast to token type
120+
gremlin> g.inject("1.0").asNumber(N.int)
121+
==> 1
122+
// Note: this option is favorable because of potential precision loss, eg:
123+
// (long) Double.parseDouble("123456789123456789.0")
124+
// ==> 123456789123456784
125+
// However, this may be more complex, i.e. what if we get a very long string with letters mixed in the decimal place?
126+
----
127+
* Non-parsable strings - throw exception
128+
[source]
129+
----
130+
gremlin> g.inject("test").asNumber()
131+
==> Parsing Exception
132+
----
133+
134+
*Array, List, & Set* - throws exceptions, unless unfolded:
135+
136+
* Omit scopes in the first iteration to be consistent with `asDate()`. In this case, user would need to use `unfold()`/`fold()`, or else a Parsing Exception will be thrown.
137+
[source]
138+
----
139+
gremlin> g.inject([1, 2, 3, 4]).asNumber()
140+
==> Parsing Exception
141+
142+
gremlin> g.inject([1, 2, 3, 4]).unfold().asNumber()
143+
==> 1
144+
==> 2
145+
==> 3
146+
==> 4
147+
148+
gremlin> g.inject([1, 2, 3, 4]).unfold().asNumber().fold()
149+
==> [1, 2, 3, 4]
150+
----
151+
152+
* Scopes can potentially be added in future iterations
153+
** `asNumber(Scope)`
154+
** `asNumber(Scope, N)`
155+
*** `Scope.global` - the default scope, will throw an exception since a list cannot be converted to a number
156+
*** `Scope.local` - the individual items inside will be evaluated and converted
157+
158+
[source]
159+
----
160+
gremlin> g.inject([1, 2, 3, 4]).asNumber()
161+
==> Parsing Exception
162+
163+
gremlin> g.inject([1, 2, 3, 4]).asNumber(local)
164+
==> [1, 2, 3, 4]
165+
gremlin> g.inject([1, "2", 3, "4.0"]).asNumber(local)
166+
==> [1.0, 2.0, 3.0, 4.0]
167+
gremlin> g.inject([1, "two", 3, "4.0"]).asNumber(local)
168+
==> Parsing Exception
169+
----
170+
171+
*Non-Parsable Types* - throws exception
172+
[source]
173+
----
174+
gremlin> g.V(1).asNumber(N.int)
175+
==> Parsing Exception ("Type Vertex is not parsable to Type Integer")
176+
----
177+
178+

0 commit comments

Comments
 (0)