Configuring can-set algebra

I’m using a python api server (flask-restless) which uses the JSON api for filtering/sorting/pagination and am trying to get can-connect to work correctly with it.

I’ve read through the docs but its still not making sense in some cases. I’m probably just not understanding how can-set works with server based pagination/sorting etc.

sorting

The server accepts sorting parameters like this:

Ascending: sort=fieldName
Descending: sort=-fieldName (note the -)

I initially thought I’d do something like this:

connection.getList({
    sort: {
        field: 'fieldname',
        type: 'asc'
    }
});

and in my supermap, I could just override the getListData method to send the sorting parameters, but while my api is returning a correct sorted result, I’m not sure how to configure can-set to handle it correctly. When I create my algebra, should I just set:

    const algebra = new set.Algebra({
        sort: function () {
            console.log(arguments);
            return true;
        },
    });

filtering

Filtering is a bit more complex, its not as simple as ?name="value" instead the api accepts filters like:

filter[objects]=[{"val":"value", "op": "=", "name": "fieldname"}]

Same thing, should I just set algebra to:

    'filter[objects]': function() {
        return true;
    }

pagination

pagination works like this:
page[number]=4 --sets the page to 4
page[size]=10 --sets the per page count to 10

So we’d be requesting items 40-49 with that request. Not necessarily ID number 40-49, just the 40th through 49th items.

Just to simplify things, you can have a set that looks like:

{
  sort: "-name",
  filter: {objects: [{"val":"29", "op": "=", "name": "age"}],
  page: {number: 4, size: 10}
}

?

Yes, that is correct. And a lot simpler than my explanation.

So I’ve been trying to make those parameters work. The problem is that a page number and size doesn’t work well with unions.

Say you loaded {number: 1, size: 10} and {number: 2, size: 10} … how do you express the union of these sets? {number: 0.5, size: 20}? My guess is that your server will not support this. So yes, it’s probably best to ignore the page property.

Here’s how you would make sorting work (I might have my 1 and -1's swapped):

var algebra = new can.set.Algebra(
  can.set.props.sort("sort", function(sort, item1, item2){
    if(sort) {
      if(sort[0] === "-") {
        var value1 = item1[sort.substr(1)],
            value2 = item2[sort.substr(2)];
        return value1 > value2 ? 1 : -1;
      } else {
        var value1 = item1[sort],
            value2 = item2[sort];
        return value1 > value2 ? -1 : 1;
      }
    }
  })
)

For filtering, I’ll get back to you shortly.

I am finding can-set rather hard to configure for your use case. I’m going to talk about it at the contributors meeting today.

Thanks Justin. That helps clear it up quite a bit. So basically, for the sort property, we can pass a typical sort function that will return a 0, 1, or -1.

I’m still curious on how filtering would work, you’re right it does seem pretty tricky to configure can-set for this api. But I think figuring it may be helpful for others in the future since the the web server I’m using is an implementation of the JSON API spec and is also used by quite a few api servers.

@justinbmeyer, have you put any more thought to how you would configure the filtering aspect of this?

I have a similar API which allows filter operators using the following syntax

{
  updated: { gt: "2017/02/11 00:00:00" },
  status : { ne: "completed" }
}

The API also allows for array values to be passed for a “where in” operator. For example:

{
  task_id: [1,6,12]
}

Will get tasks with task_id 1, 6 and 12. This is similar to the current enum functionality but without the restriction of values. In fact, I believe if you configure an empty enumerator in can-set it actually works, but how would you handle the fact that task_id is both an id and an “enumerator”?

Why wouldn’t enum work for task_id?

For gt, ne, etc you’d probably want something similar to how boolean works. This does the best job of explaining it: http://canjs.com/doc/can-set.prop.html

For example:

new set.Algebra({
  status: function(aValue, bValue, a, b, prop) {
    return { ... }
  }
})

You’d need to make that return the right thing so the following happens:

set.intersection( { status : { lt: 10} }, { status : { gt: 9 } } ) //-> {status: {gt: 10, lt: 9}}
set.union( { status : { lt: 10} }, { status : { gt: 9 } } ) //-> {}

When status function is called it would need to return something like:

{
  intersection: {gt: 10, lt: 9},
  union: null // I think null
}