简体   繁体   中英

How to implement a RESTful API for order changes on large collection entries?

I have an endpoint that may contain a large number of resources. They are returned in a paginated list. Each resource has a unique id , a rank field and some other data. Semantically the resources are ordered with respect to their rank . Users should be able to change that ordering. I am looking for a RESTful interface to change the rank field in many resources in a large collection.

Reordering one resource may result in a change of the rank fields of many resources. For example consider moving the least significant resource to the most significant position. Many resources may need to be "shifted down in their rank".

The collection being paginated makes the problem a little tougher. There has been a similar question before about a small collection

The rank field is an integer type. I could change its type if it results in a reasonable interface.


For example:

GET /my-resources?limit=3&marker=234 returns :

{
  "pagination": {
    "prevMarker": 123,
    "nextMarker": 345
  },
  "data": [
    {
      "id": 12,
      "rank": 2,
      "otherData": {}
    },
    {
      "id": 35,
      "rank": 0,
      "otherData": {}
    },
    {
      "id": 67,
      "rank": 1,
      "otherData": {}
    }
  ]
}

Considered approaches.

1) A PATCH request for the list.

We could modify the rank fields with the standard json-patch request. For example the following:

[
  {
    "op": "replace",
    "path": "/data/0/rank",
    "value": 0
  },
  {
    "op": "replace",
    "path": "/data/1/rank",
    "value": 1
  },
  {
    "op": "replace",
    "path": "/data/2/rank",
    "value": 2
  }
]

The problems I see with this approach:

a) Using the array indexes in path in patch operations. Each resource has already a unique ID. I would rather use that.

b) I am not sure to what the array index should refer in a paginated collection? I guess it should refer to the global index once all pages are received and merged back to back.

c) The index of a resource in a collection may be changed by other clients. What the current client thinks at index 1 may not be at that index anymore. I guess one could add test operation in the patch request first. So the full patch request would look like:

[
  {
    "op": "test",
    "path": "/data/0/id",
    "value": 12
  },
  {
    "op": "test",
    "path": "/data/1/id",
    "value": 35
  },
  {
    "op": "test",
    "path": "/data/2/id",
    "value": 67
  },
  {
    "op": "replace",
    "path": "/data/0/rank",
    "value": 0
  },
  {
    "op": "replace",
    "path": "/data/1/rank",
    "value": 1
  },
  {
    "op": "replace",
    "path": "/data/2/rank",
    "value": 2
  }
]

2) Make the collection a "dictionary"/ json object and use a patch request for a dictionary.

The advantage of this approach over 1) is that we could use the unique IDs in path in patch operations. The "data" in the returned resources would not be a list anymore:

{
  "pagination": {
    "prevMarker": 123,
    "nextMarker": 345
  },
  "data": {
    "12": {
      "id": 12,
      "rank": 2,
      "otherData": {}
    },
    "35": {
      "id": 35,
      "rank": 0,
      "otherData": {}
    },
    "67": {
      "id": 67,
      "rank": 1,
      "otherData": {}
    }
  }
}

Then I could use the unique ID in the patch operations. For example:

{
    "op": "replace",
    "path": "/data/12/rank",
    "value": 0
  }

The problems I see with this approach:

a) The my-resources collection can be large, I am having difficulty about the meaning of a paginated json object, or a paginated dictionary. I am not sure whether an iteration order can be defined on this large object.

3) Have a separate endpoint for modifying the ranks with PUT

We could add a new endpoint like this PUT /my-resource-ranks . And expect the complete list of the ordered id's to be passed in a PUT request. For example

[
  {
    "id": 12
  },
  {
    "id": 35
  },
  {
    "id": 67
  }
]

We would make the MyResource.rank a readOnly field so it cannot be modified through other endpoints.

The problems I see with this approach:

a) The need to send the complete ordered list. In the PUT request for /my-resource-ranks we will not include any other data, but only the unique id's of resources. It is less severe than sending the full resources but still the complete ordered list can be large.

4) Avoid the MyResource.rank field and the "rank" be the order in the /my-collections response.

The returned resources would not have the "rank" field in them and they will be already sorted with respect to their rank in the response.

{
  "pagination": {
    "prevMarker": 123,
    "nextMarker": 345
  },
  "data": [
    {
      "id": 35,
      "otherData": {}
    },
    {
      "id": 67,
      "otherData": {}
    },
    {
      "id": 12,
      "otherData": {}
    }
  ]
}

The user could change the ordering with the move operation in json-patch.

[
  {
    "op": "test",
    "path": "/data/2/id",
    "value": 12
  },
  {
    "op": "move",
    "from": "/data/2",
    "path": "/data/0"
  }
]

The problems I see with this approach:

a) I would prefer the freedom for the server to return to /my-collections in an "arbitrary" order from the client's point of view. As long as the order is consistent, the optimal order for a "simpler" server implementation may be different than the rank defined by the application.

b) Same concern as 1)b). Does index in the patch operation refer to the global index once all pages are received and merged back to back? Or does it refer to the index in the current page ?


Update:

Does anyone know further examples from an existing public API ? Looking for further inspiration. So far I have:

I would

  • Use PATCH
  • Define a specialized content-type specifically for updating the order.

The application/patch+json type is pretty great for doing straight-up modifications, but I think your use-case is unique enough to warrant a useful, minimal specialized content-type.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM