senfo
senfo

Reputation: 29046

RESTful URI for Filtering Nested Collections

I'm looking for a good way to form a URI for a resource that filters on a collection of values contained within records. For example, say you have a recipe database and you want to search for recipes that contain "cherry" (obviously most recipes would contain multiple ingredients).

If I just want to filter on single values, I could do something similar to the following:

/recipe/search/?name=Spaghetti

But what about filtering on multiple values? I was considering something like the following:

/recipe/search/?ingredients=contains=cherry

Any thoughts on this? Is there a "standard" for a filter of this kind?

Update: One problem I have with my idea is the way it gets handled on the backend (in my case Rails). When querying the server with this particular format, Rails generates a Ruby hash that could get ugly like the following:

{"ingredients"=>"contains=cherry",
"action"=>"search",
"controller"=>"recipe"}

Upvotes: 3

Views: 5148

Answers (3)

Samuel Herzog
Samuel Herzog

Reputation: 3611

Your URI

First of all, in contrast to other answers I'll start from a REST perspective and then find appropriate additions to it. I am not strong in Ruby so bear with no-code on the backend.

  1. Recipies are the entities you wanna present
    your users find them at /recipies
  2. HTTP has QUERYS for filtering
    wanna have sorted those recipies by date? use /recipies/?sortby=date&sort=asc
  3. Only recipies with cherries goes similarly: /recipies/?ingredients=cherry

So that's the REST way of structuring your basic URL.
For multiple matches there is no official way to do it, but i'd follow user1758003. This is an intuitive construction of the url and easily to parse on the backend, so we have /recipies/?ingredients=cherry,chocolate

Don't forget /recipies/search is not restful because it mirrors recipies and does not represent an independent entity. However it is a great place to host a searchform for visitors to your site.

Now there are some additional questions packed into the first, let me address them one by one:

I have a website consuming this api, how should the search form look like?

Give your visitors a /recipie/search page or a quick filtering possibility on /recipies. Just set the <form action="/recipies" type="GET">. The browser will add all parameters as an Query string after /recipies.

Advanced functionality

A request to /recipies should list all. If you add a query every parameter of the query must be respected. so /recipies/?ingredients=cheese MUST only return cheesy recipies. For multiply query parameter values there is no fixed standard but I'd like a service to be intuitive.
I read GET /recipies/?ingredients=cherry,chocolate as Get me the recipies which have ingredients of cherry and chocolate. To get a list of recipies containing either cherry or chocolate I'd want to write the URI like /recipies/?ingredients=cherry|chocolate which makes it visually very different from a comma and has a predefined meaning (OR) in programming contexts.

Upvotes: 3

user1758003
user1758003

Reputation: 162

I'm not familiar with the specifics of ruby hashes but I'm guessing the hash is created to uniquely identified the query both at the http and data access levels?

Regardless, you want to be careful about URL encoding if you wish to use json in a query parameter. Another thing to keep in mind is the term "search" could be considered repetitive. If your server is being accessed using a GET method and you have criteria then hopefully you're not modifying any state in the back end. Not your question but just thought I'd mention it.

So...

https://yourserver.com/approot/recipe/search?ingredients=cherry,cheese&type=cake

HTTP doesn't define commas as a query string separator so your framework should be able to parse 2 query string parameters:

ingredients: "cherry,cheese"

which you should be able to easily covert to an an array using split or whichever equivalent function ruby provides.

and

type: "cake" (extra query term added to illustrate a point and because cherry cheese cake is awesome and cake is not an ingredient)

If I understand your example correctly you would end up with:

{
"ingredients"=>"cherry,cheese",
"type"=>"cake",
"action"=>"search",
"controller"=>"recipe"
}

Is this what you where looking for?

Upvotes: 2

MKV
MKV

Reputation: 913

Most of the REST webservice using JSON data only.So use JSON format which will return single string value only. From this JSON format you can send the array value also.

For array value means you to convert that array into the JSON format like this

from php:

$ingredients = array('contains'=>array('fruits'=>'cherry,apple','vitamins'=>'a,d,e,k'));
$ingredients_json = json_encode($ingredients);
echo $ingredients_json;

it will return json format like this:

 {"contains":{"fruits":"cherry,apple","vitamins":"a,d,e,k"}} 

and you can use this JSON string in the url

/recipe/search/?ingredients={"contains":{"fruits":"cherry,apple","vitamins":"a,d,e,k"}}

in the server side we have the option to decode this JSON format value to the array.

{"ingredients"=>"{\"contains\":{\"fruits\":\"cherry,apple\",\"vitamins\":\"a,d,e,k\"}}",
"action"=>"search",
"controller"=>"recipe"}

Upvotes: 1

Related Questions