2

I am trying to build a RESTful search api (POST request) that can return different types of resources (e.g A, B, C, etc). I have defined a data model for each of the resource type. There are some propeties that are common across all the types and there are some that are specific to some types.

"A" : {
    "a_property1",
    "a_property2",
    "property1",
    "common_property"
}

"B": {
    "b_property1",
    "b_property2",
    "property1",
    "common_property"       
}

"C": {
    "c_property1",
    "common_property"           
}

example request

{
    q : "dog",
    filters: {
        "common_property" : 123, //put the common filters outside 
        "A" : {
            //put all type A specific filters inside
            "A.a_property1" : 100,
            "A.a_property2": "value",
        },
        "B" : {
            //put all type B specific filters here
            "B.b_property1": "value"
        }
    }
}

I am trying to tie the request object to the same data model (it would feel symmetric that way).

Questions:

  1. how to represent filters for properties that are part of more than one type but not all types. e.g "property1"?
  2. is it a good idea to tie the request to the data model of the resources?

EDIT:

Let us take Google as an example

/search
{
    "q" : "paris"
    "verticals" : ["web","images","maps","news"] // or ["all"]
    "filter" : {
        "common.last_modified": "2017-01-01"
    }
    "sort" : "relevance"
}


/search
{
    "q" : "paris"
    "verticals" : ["images"] 
    "filter": {
        "image.type" : "Line Drawing",
        "image.color" : "Red"
    }
"sort" : "relevance"
}

One reason I like the prefix is that it qualifies where the filter belongs. In this case these filters doesnt make any sense for web search, so we can throw an error.

/search 
{
    "q" : "paris"
    "verticals" : ["web"] // or ["all"]
    "filter": {
        "image.type" : "Line Drawing",
        "image.color" : "Red",
        "web.category": "travel"
    }
    "sort" : "relevance"
}

NOTE: I have modified the request a little bit to remove the sections like A, B, C in the request as it looked complicated.

  • 1
    The answer to (2) is usually not. – Eric Stein Aug 5 '16 at 17:44
  • can you elaborate why its not usually recommended ? – srini Aug 10 '16 at 2:48
0

In addition to

is it a good idea to tie the request to the data model of the resources?

I would say no.

Why?

You have exposed a very flat data model. But, what would you do with models like:

"D" : { 
     "a_property1", 
     "a_property2", 
     "property1", 
     "common_property" ,
     "complex_property":{
          "more_complexity":{...}
       }
     }

You are tightly coupling one model to many others, in consequence, every new entity that comes along the system forces to you to refactor the query's data model.

This approach won't scale well and its maintenance is going to be expensive eventually.

A good approach is to look the search as a resource. Check this question

Query data model

Let say we turn your example into something less coupled.

{ 
         "entity":"dog",
         "filters":{
               "name":"value",
               "name":"value",
               "name.attr.attr":"value"
           }
  }

And let name it QueryDTO.

Once on the server, we have to transform the query into filters applicable to every possible entity of the data model.

public interface QueryTransformer<T>{
      T transform(QueryDTO query);
}

"T" is any of the entities of the data model. Transformer maps filters to T's attributes. Those that does not match are ignored.

Transformer generates a example of T, that can be used (as a template) to generate the criteria for filtering such entity.

The idea is to split a big problem (query data model) into many but smaller (transformers).

The approach reduces the complexity. It does abstract the mapping and it uses the existing data model. It also scales better than the first purpose. Maintenance is also cheaper.

QueryDTO can be as complex as needed, but the more complex is, the harder is to implement these transformers. But still scalable and cheap.

  • I missed your answer and checked it only now. I am still interested in this question. I do like your suggestion. Now, I have clarified the question with one concrete example. One concern I have with this approach is that it can be confusing to the user which filter belongs to which backend. Moreover i would like to enable refinements based on the attributes returned in the response from different verticals. – srini May 9 '17 at 17:30
  • Ok I see. I guess that your verticals would be the different backends. Right? And filters will be only those related to the entities which belongs to the verticals so there could be attribute collision among entities-backend. I will review my answer. – Laiv May 9 '17 at 17:48
  • One more question. How would you inform the verticals? Like does your example of Google or by "dot" annotation? Or both? – Laiv May 9 '17 at 18:02
  • answer to your previous question: we would not have attribute collision as we try to keep the meaning of the filter consistent. for e.g if "last_modified_date" applies all verticals then we keep it as a common filter. if "color" applies only to [image,video] verticals, then we keep it as image.color and video.color for the fact that it doesnt apply to all verticals. Though theoretically it would enable the user to specify different color for image and videos, I am not sure whether we need that ability. I can think about giving up that freedom if it would make the api simpler. – srini May 9 '17 at 18:56
  • Actually, we do have a QueryTransformer in our current design. May be thats why i liked your answer. Now the question is only on request format and attribute names. – srini May 9 '17 at 19:57
1

It seems odd to use a POST (used for creating data on the server which may be what you want) for what is really a GET (retrieving data) request. The filters can be placed in the query string for caching purposes if needed and for easier debugging. For example.

GET /api/resources?q=dog&filter_common_property=123&filter_property1=value@filter_a_property1=100

You could parse specific object filters from the key such as filter_property1=value being global and filter_a_property1 being local to the A object.

From a REST perspective it would also seem to make more sense to query each resource individually GET /api/A?.... unless there are performance requirements I'm unaware of.

0

One approach would be to repeat the property for each type you're querying. In other words don't have the option for a "global" filter.

  • this is the approach i already decided to go with, thanks for your comment anyway. Main point that i dont like with this approach is that the properties are repeated across resources, though it makes things explicit, it turns out to be too verbose. – srini Aug 10 '16 at 2:47
  • In terms of data, are these massive queries? I find that optimizations (like not wanting to repeat the same filter five times) can lead to much more complex solutions. So it takes examination to decide if you want the benefits and drawbacks of one approach, or another approach. – edev Aug 10 '16 at 8:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.