in src/Epam.GraphQL/Relay/Paginator.cs [114:276]
public PaginatorResult<TSource> Materialize()
#pragma warning restore CA1502
{
// Dragons live here...
var query = _query;
if (!_shouldMaterialize && !_shouldSkip && !_shouldTake && !_shouldTakeBefore && !_shouldTakeLast)
{
return new PaginatorResult<TSource>
{
StartOffset = 0,
Page = _executer.ToEnumerable(_configurationContext, _stepNameFactory, query),
};
}
var liveSkipCount = _skipCount - (_shouldSkip ? 1 : 0);
if (_shouldSkip)
{
// Skip items excluding the last one.
// It is intended to ensure that we did not skip the whole data sequence.
query = query.Skip(liveSkipCount);
}
var liveTakeCount = _takeCount + 1 + (_shouldSkip ? 1 : 0);
if (_shouldTake)
{
// Take items including the next item and maybe including previous one (if 'Skip' was performed).
query = query.Take(liveTakeCount);
}
var sample = _executer.ToList(_configurationContext, _stepNameFactory, query);
var shouldRemoveLast = false;
var skipCanceled = false;
var hasNextPage = false;
var hasPreviousPage = false;
int? offset = 0;
int? totalCount = null;
if (_shouldTake)
{
if (sample.Count > 0)
{
if (sample.Count == liveTakeCount)
{
if (!_shouldTakeBefore || _takeBeforeCount > 0)
{
hasNextPage = true;
}
shouldRemoveLast = true;
}
else
{
// If sample.Count < liveTakeCount then 'Take' operation gave us one item at least.
// It means that we reached the end of data sequence, so there is no the next page for current page.
totalCount = liveSkipCount + sample.Count;
}
}
else if (_shouldSkip)
{
// Query is being executed the second time because 'Take' gave us no items. It is possible by two reasons:
// 1) we reached the end of data sequence by skiping items. This case will be handled below by taking items again.
// 2) the sequence is initially empty. This case should not be handled here.
// Again, we apply the same technique (take one more item from data sequence) to ensure that we have items after the current page.
sample = _executer.ToList(_configurationContext, _stepNameFactory, _query.Take(_takeCount + 1));
skipCanceled = true;
if (sample.Count == _takeCount + 1)
{
hasNextPage = true;
shouldRemoveLast = true;
}
}
}
else
{
// 'Take' operation was not performed, so we've got all the items from the sequence till the end.
totalCount = liveSkipCount + sample.Count;
}
if (sample.Count > 0)
{
// Remove extra items if necessary...
if (shouldRemoveLast)
{
sample.RemoveAt(sample.Count - 1);
}
if (_shouldSkip && !skipCanceled)
{
sample.RemoveAt(0);
offset = _skipCount;
}
if (!skipCanceled && !hasNextPage && sample.Count == 0)
{
offset = null;
}
}
else
{
skipCanceled = true;
if (_shouldSkip && !_shouldTake)
{
// If there is no items after skipping (and we did not perform taking), try to materialize query again.
sample = _executer.ToList(_configurationContext, _stepNameFactory, _query);
}
if (sample.Count == 0)
{
offset = null;
}
totalCount = sample.Count;
}
if (_shouldSkip && !skipCanceled)
{
hasPreviousPage = true;
}
if (_shouldTakeLast)
{
var removeCount = Math.Max(0, sample.Count - _takeLastCount);
if (removeCount > 0)
{
offset += removeCount;
sample.RemoveRange(0, removeCount);
hasPreviousPage = true;
}
if (sample.Count == 0 && !_shouldTakeBefore)
{
hasPreviousPage = false;
offset = null;
}
}
// Reset wrapper
_skipCount = 0;
_takeCount = 0;
_takeLastCount = 0;
_takeBeforeCount = 0;
_shouldTake = false;
_shouldSkip = false;
_shouldTakeLast = false;
_shouldTakeBefore = false;
return new PaginatorResult<TSource>
{
StartOffset = offset,
EndOffset = offset + Math.Max(sample.Count - 1, 0),
HasPreviousPage = hasPreviousPage,
HasNextPage = hasNextPage,
Page = sample,
TotalCount = totalCount,
};
}