How to implement a Search API?


#1

I’d be interested in if it’s possible to model with JSON API a Search API that is capable to search across different resource types for a given text string. I want to issue a search query like “find me all comments which has sometext in their content and all posts that has sometext in their title” without issuing two separate queries to the comments and posts resources.

I.e. in the end I want to receive a response like

[{
  "type": "comments",
  "id": "123",
  "attributes": {
    "content": "sometext"
  }}, {
  "type": "posts",
  "id": "456",
  "attributes": {
    "title": "sometext"
  }},
  ...
]

… or would I need to create a resource searches and create a new search via PUT + data.attributes.search = sometext which then creates a new searches object containing relationships to other resource objects like comments and posts which contains that given search text? But this solution also doesn’t sound so good as you get then for each relationship type a separate list and not an unified one that can be easily paginated.

Is there any best practice for such a Search API or do you have any other ideas how to solve that?

Thanks a lot!


#2

JSON-API supports heterogenous collections as responses. There is nothing in the spec that prevents you from providing an endpoint like GET /search?filter[query]=my+search and having it return a mixed collection in data.

Does that help?


#3

Building on @tkellen’s reply, you can also split the query up over multiple query parameters if necessary, so the search “find me all comments which has sometext in their content and all posts that has sometext in their title” could become GET /resources?filter[comments][content][contains]=sometext&filter[posts][title][contains]=sometext.

Finally, if your searches get too complicated for query parameters, and you do have to use something like POST /searches instead, the resulting search resource can then just have a single results relationship, which can be uniformly paginated, and, as Tyler mentioned, can contain resources of more than one type.


#4

Great, thanks a lot for the replies, that really helped me! I was not sure if it’s valid to define an URL like /search if the resource type search is not existing. But if that’s valid I can give it a try.


#5

This actually draws out a distinction in Fielding’s REST that’s not present in most people’s popular understanding of REST: a ‘resource’ is actually a URL, not a database entry. Fielding calls database entries ‘entities’, and resources are groupings of entities.

So you do have a search resource: it just returns other kinds of things.


#6

@steveklabnik Thanks a lot for your explanation. I’ve unintentionally mapped ‘resource type’ on a ‘database table’ in my understanding. I should really have a read once Fielding’s REST dissertation.

Not sure if this is now out of scope here but if I’d like to implement this search resource via Endpoints or any other framework that’s available for JSON API how would I do this? Usually a resource type is defined via some framework specific data schema which again gets often directly mapped onto a database table that. But in my case this search resource can’t be really represented as a table or at least it’s more like a union among several tables or? So it’s for me unclear how a.) a data schema could look like for the search resource and b.) how I could realize it e.g. via Endpoints.

Any feedback is welcome :slight_smile:


#7

/cc @ag_dubs @tkellen , who should be able to help with endpoints :slight_smile:


#8

Endpoints currently provides abstractions around basic CRUD actions for handling relational data in an json-api compliant manner. Given the huge amount of possible approaches for handling search, we don’t have a unified or recommended interface for something dynamic like this. We do have an architecture that could support higher level abstractions around this, though.

If you roll your own and share the process you went through it’s very likely we can find a way to make an adapter out of it!


#9

@tkellen The idea of using an adapter sounds very interesting to cover such kind of stuff. As I’m currently just doing some research I’m not sure if I’ll really implement this but it’s good to know that it’s doable.