bolt / docs

Documentation for Bolt.
45 stars 194 forks source link

.previous() and .next() return unexpected results when sorting by a field with non-unique values #597

Closed jerryjappinen closed 7 years ago

jerryjappinen commented 7 years ago

In contenttypes.yml I have defined the following content type, where each record has a uniqueish title and a year value:

# Archived side projects
archives:
    listing_sort: "-year"
    sort: "-year"
    fields:
        title:
            type: text
        year:
            type: integer

Note that I'm not using a timestamp, just a year number, and many records of this type will have the same value. I wish to sort my records by year in my list view, and provide consistent next/prev navigation on the record page.

However, .next() will not get the next record in the list sorted by year. It will instead get the first record with a higher year value than the current record. Same thing with previous in the other direction of course. These are often the same thing when sorting by timestamps or unique titles, but not with recurring values like a year.

As an example, if I have the following list of records (the titles don't matter):

index   year    title

[0]     2017    Dying Light
[1]     2016    The Darkness II
[2]     2016    Tomb Raider
[3]     2015    Dragon Age Inquisition
[4]     2015    Crimsonland
[5]     2015    Geometry Wars 3
[6]     2015    Grim Fandango
[7]     2015    Fallout New Vegas
[8]     2014    Metal Gear Solid 2
[9]     2014    Call of Duty Advanced Warfare

Sorting works fine. But .previous() and .next() will look for the first record where the year value is higher/lower:

{% setcontent archives = 'archives' %}
{% set crimsonland = archives[4] %}

{# Expecting 'Dragon Age Inquisition', but gives me 'The Darkness II' #}
{% set prev = crimsonland.previous() %}

{# Expecting 'Geometry Wars 3', but gives me 'Metal Gear Solid 2' #}
{% set next = crimsonland.next() %}

Things will completely break down if I sort with -year,title: both functions will give me 'Call of Duty Advanced Warfare'.

As it is the prev/next functions work for many common use cases where the field values are mostly unique, but not with recurring values like I have here.

Other notes

The sorting behavior is consistent regardless of where I do the sorting: I get the same results if I use -year in setcontent and pass it as a parameter to record.previous()/record.next(). I took a look into src/Legacy/Content.php, and it seems this is indeed the intended behavior:

public function previous($field = 'datepublish', $where = [])
{
    list($field, $asc) = $this->app['storage']->getSortOrder($field);

    $operator = $asc ? '<' : '>';
    $order = $asc ? ' DESC' : ' ASC';

    $params = [
        $field         => $operator . $this->values[$field],
        'limit'        => 1,
        'order'        => $field . $order,
        'returnsingle' => true,
        'hydrate'      => true,
    ];

    $pager = [];
    $previous = $this->app['storage']->getContent($this->contenttype['singular_slug'], $params, $pager, $where);

    return $previous;
}

While I'm not 100 % familiar with how the parameters are used, it looks to me like this code will look for the first element that has a higher/lower value in the specified field than the original record (in my case year). The implementation isn't honestly looking for the next element relative to the current one in the list that the user is dealing with, in effect skipping over many records.

jerryjappinen commented 7 years ago

Moved to https://github.com/bolt/bolt/issues/6129, I don't know how this ended up in the docs repo.