Skip to content

Commit 4095b44

Browse files
committed
Clarify semantics of list and consume
o Also simplifies implementation to avoid unnecessary recursion
1 parent 0004c80 commit 4095b44

File tree

2 files changed

+93
-107
lines changed

2 files changed

+93
-107
lines changed

driver/src/main/java/org/neo4j/driver/internal/InternalStatementResult.java

Lines changed: 41 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
import org.neo4j.driver.v1.util.Function;
4343
import org.neo4j.driver.v1.util.Functions;
4444

45-
import static java.lang.String.format;
4645
import static java.util.Collections.emptyList;
4746

4847
public class InternalStatementResult implements StatementResult
@@ -55,7 +54,6 @@ public class InternalStatementResult implements StatementResult
5554
private List<String> keys = null;
5655
private ResultSummary summary = null;
5756

58-
private boolean open = true;
5957
private long position = -1;
6058
private boolean done = false;
6159

@@ -170,26 +168,17 @@ StreamCollector pullAllResponseCollector()
170168
@Override
171169
public List<String> keys()
172170
{
173-
tryFetching();
171+
if ( keys == null )
172+
{
173+
tryFetchNext();
174+
}
174175
return keys;
175176
}
176177

177178
@Override
178179
public boolean hasNext()
179180
{
180-
if (!recordBuffer.isEmpty())
181-
{
182-
return true;
183-
}
184-
else if (done)
185-
{
186-
return false;
187-
}
188-
else
189-
{
190-
tryFetching();
191-
return hasNext();
192-
}
181+
return tryFetchNext();
193182
}
194183

195184
@Override
@@ -201,67 +190,52 @@ public Record next()
201190
// in a way that makes the two equivalent in performance.
202191
// To get the intended benefit, we need to allocate Record in this method,
203192
// and have it copy out its fields from some lower level data structure.
204-
assertOpen();
205-
Record nextRecord = recordBuffer.poll();
206-
if ( nextRecord != null )
193+
if ( tryFetchNext() )
207194
{
208195
position += 1;
209-
return nextRecord;
210-
}
211-
else if ( done )
212-
{
213-
return null;
196+
return recordBuffer.poll();
214197
}
215198
else
216199
{
217-
tryFetching();
218-
return next();
200+
throw new NoSuchRecordException( "No more records" );
219201
}
220202
}
221203

222204
@Override
223205
public Record single()
224206
{
225-
if( !hasNext() )
207+
if ( hasNext() )
226208
{
227-
throw new NoSuchRecordException( "Cannot retrieve a single record, because this result is empty." );
228-
}
209+
Record single = next();
210+
boolean hasMoreThanOne = hasNext();
229211

230-
Record single = next();
231-
boolean hasMany = hasNext();
212+
consume();
232213

233-
consume();
214+
if ( hasMoreThanOne )
215+
{
216+
throw new NoSuchRecordException( "Expected a result with a single record, but this result contains at least one more. " +
217+
"Ensure your query returns only one record, or use `first` instead of `single` if " +
218+
"you do not care about the number of records in the result." );
219+
}
234220

235-
if( hasMany )
221+
return single;
222+
}
223+
else
236224
{
237-
throw new NoSuchRecordException( "Expected a result with a single record, but this result contains at least one more. " +
238-
"Ensure your query returns only one record, or use `first` instead of `single` if " +
239-
"you do not care about the number of records in the result." );
225+
throw new NoSuchRecordException( "Cannot retrieve a single record, because this result is empty." );
240226
}
241-
242-
return single;
243227
}
244228

245229
@Override
246230
public Record peek()
247231
{
248-
assertOpen();
249-
250-
while ( true )
232+
if ( tryFetchNext() )
251233
{
252-
tryFetching();
253-
Record nextRecord = recordBuffer.peek();
254-
if ( nextRecord == null )
255-
{
256-
if ( done )
257-
{
258-
throw new NoSuchRecordException( "Cannot peek past the last record" );
259-
}
260-
}
261-
else
262-
{
263-
return nextRecord;
264-
}
234+
return recordBuffer.peek();
235+
}
236+
else
237+
{
238+
throw new NoSuchRecordException( "Cannot peek past the last record" );
265239
}
266240
}
267241

@@ -274,46 +248,34 @@ public List<Record> list()
274248
@Override
275249
public <T> List<T> list( Function<Record, T> mapFunction )
276250
{
277-
if ( isEmpty() )
278-
{
279-
assertOpen();
280-
return emptyList();
281-
}
282-
else if ( position == -1 && hasNext() )
251+
if ( hasNext() )
283252
{
284253
List<T> result = new ArrayList<>();
254+
285255
do
286256
{
287257
result.add( mapFunction.apply( next() ) );
288258
}
289259
while ( hasNext() );
290260

291-
consume();
292261
return result;
293262
}
294263
else
295264
{
296-
throw new ClientException(
297-
format( "Can't retain records when cursor is not pointing at the first record (currently at position %d)", position )
298-
);
265+
return emptyList();
299266
}
300267
}
301268

302-
@SuppressWarnings("StatementWithEmptyBody")
303269
@Override
304270
public ResultSummary consume()
305271
{
306-
if(!open)
307-
{
308-
return summary;
309-
}
310-
311272
while ( !done )
312273
{
313274
connection.receiveOne();
275+
recordBuffer.clear();
314276
}
315277
recordBuffer.clear();
316-
open = false;
278+
317279
return summary;
318280
}
319281

@@ -323,26 +285,17 @@ public void remove()
323285
throw new ClientException( "Removing records from a result is not supported." );
324286
}
325287

326-
private void assertOpen()
288+
private boolean tryFetchNext()
327289
{
328-
if ( !open )
329-
{
330-
throw new ClientException( "Result has been closed" );
331-
}
332-
}
333-
334-
private boolean isEmpty()
335-
{
336-
tryFetching();
337-
return position == -1 && recordBuffer.isEmpty() && done;
338-
}
339-
340-
private void tryFetching()
341-
{
342-
while ( recordBuffer.isEmpty() && !done )
290+
while ( recordBuffer.isEmpty() )
343291
{
292+
if ( done )
293+
{
294+
return false;
295+
}
344296
connection.receiveOne();
345297
}
346-
}
347298

299+
return true;
300+
}
348301
}

driver/src/test/java/org/neo4j/driver/internal/InternalStatementResultTest.java

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121

2222
import java.util.ArrayList;
23-
import java.util.Arrays;
2423
import java.util.LinkedList;
2524
import java.util.List;
2625

@@ -33,14 +32,14 @@
3332
import org.neo4j.driver.internal.spi.Connection;
3433
import org.neo4j.driver.internal.value.NullValue;
3534
import org.neo4j.driver.v1.Record;
36-
import org.neo4j.driver.v1.Records;
3735
import org.neo4j.driver.v1.Statement;
3836
import org.neo4j.driver.v1.StatementResult;
3937
import org.neo4j.driver.v1.Value;
40-
import org.neo4j.driver.v1.exceptions.ClientException;
4138
import org.neo4j.driver.v1.exceptions.NoSuchRecordException;
4239
import org.neo4j.driver.v1.util.Pair;
4340

41+
import static java.util.Arrays.asList;
42+
4443
import static org.hamcrest.CoreMatchers.equalTo;
4544
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
4645
import static org.junit.Assert.assertFalse;
@@ -52,6 +51,8 @@
5251
import static org.mockito.Mockito.doAnswer;
5352
import static org.mockito.Mockito.mock;
5453

54+
import static org.neo4j.driver.v1.Records.column;
55+
import static org.neo4j.driver.v1.Values.ofString;
5556
import static org.neo4j.driver.v1.Values.value;
5657

5758
public class InternalStatementResultTest
@@ -67,16 +68,20 @@ public void iterationShouldWorksAsExpected()
6768

6869
// WHEN
6970
assertTrue( result.hasNext() );
70-
assertThat( values( result.next() ), equalTo(Arrays.asList(value("v1-1"), value( "v2-1" ))));
71+
assertThat( values( result.next() ), equalTo( asList(value("v1-1"), value( "v2-1" ))));
7172

7273
assertTrue( result.hasNext() );
73-
assertThat( values( result.next() ), equalTo(Arrays.asList(value("v1-2"), value( "v2-2" ))));
74+
assertThat( values( result.next() ), equalTo( asList(value("v1-2"), value( "v2-2" ))));
7475

7576
assertTrue( result.hasNext() ); //1 -> 2
7677

7778
// THEN
78-
assertThat( values( result.next() ), equalTo(Arrays.asList(value("v1-3"), value( "v2-3" ))));
79+
assertThat( values( result.next() ), equalTo( asList(value("v1-3"), value( "v2-3" ))));
7980
assertFalse( result.hasNext() );
81+
82+
expectedException.expect( NoSuchRecordException.class );
83+
84+
// WHEN
8085
assertNull( result.next() );
8186
}
8287

@@ -206,6 +211,44 @@ public void singleShouldThrowOnConsumedResult()
206211
result.single();
207212
}
208213

214+
@Test
215+
public void shouldConsumeTwice()
216+
{
217+
// GIVEN
218+
StatementResult result = createResult( 2 );
219+
result.consume();
220+
221+
// WHEN
222+
result.consume();
223+
224+
// THEN
225+
assertFalse( result.hasNext() );
226+
}
227+
228+
@Test
229+
public void shouldList()
230+
{
231+
// GIVEN
232+
StatementResult result = createResult( 2 );
233+
List<String> records = result.list( column( "k1", ofString() ) );
234+
235+
// THEN
236+
assertThat( records, equalTo( asList( "v1-1", "v1-2" ) ) );
237+
}
238+
239+
@Test
240+
public void shouldListTwice()
241+
{
242+
// GIVEN
243+
StatementResult result = createResult( 2 );
244+
List<Record> firstList = result.list();
245+
assertThat( firstList.size(), equalTo( 2 ) );
246+
247+
// THEN
248+
List<Record> secondList = result.list();
249+
assertThat( secondList.size(), equalTo( 0 ) );
250+
}
251+
209252
@Test
210253
public void singleShouldNotThrowOnPartiallyConsumedResult()
211254
{
@@ -255,7 +298,7 @@ public void retainAndMapByKeyShouldWorkAsExpected()
255298
StatementResult result = createResult( 3 );
256299

257300
// WHEN
258-
List<Value> records = result.list( Records.column( "k1" ) );
301+
List<Value> records = result.list( column( "k1" ) );
259302

260303
// THEN
261304
assertFalse(result.hasNext());
@@ -269,23 +312,13 @@ public void retainAndMapByIndexShouldWorkAsExpected()
269312
StatementResult result = createResult( 3 );
270313

271314
// WHEN
272-
List<Value> records = result.list( Records.column( 0 ) );
315+
List<Value> records = result.list( column( 0 ) );
273316

274317
// THEN
275318
assertFalse(result.hasNext());
276319
assertThat(records, hasSize( 3 ) );
277320
}
278321

279-
@Test
280-
public void retainFailsIfItCannotRetainEntireResult()
281-
{
282-
StatementResult result = createResult( 17 );
283-
result.next();
284-
285-
expectedException.expect( ClientException.class );
286-
result.list();
287-
}
288-
289322
@Test
290323
public void accessingOutOfBoundsShouldBeNull()
291324
{
@@ -312,7 +345,7 @@ public void accessingKeysWithoutCallingNextShouldNotFail()
312345
// not calling next or single
313346

314347
// THEN
315-
assertThat( result.keys(), equalTo( Arrays.asList( "k1", "k2" ) ) );
348+
assertThat( result.keys(), equalTo( asList( "k1", "k2" ) ) );
316349
}
317350

318351
@Test

0 commit comments

Comments
 (0)