0

Does anyone know how I could define an express route (or very minimal number of routes) to take in search parameters in no specific order?

I'm talking about having some search parameters, e.g. movie details:

  • name (e.g. "foo")
  • year (e.g. 2015)
  • genre (e.g. "comedy")
  • type (e.g. "movie", "series", "episode")
  • season (e.g. 1)
  • episode (e.g. 1)

and be able to take inputs for them in a single URL, e.g.

/search/name/foo/genre/action

or equally:

/search/year/2016/season/5/episode/12/name/bar

I don't want to mandate an order to these parameters because only partial information may be known (i.e. not a value for every parameter), and I wouldn't want to force users to have to know the order given there may be many search parameters eventually.

Two thoughts I've had on this...

(1) Using express's app.use to specify some repeating pattern maybe something like:

/\/search\/(([name|year|type|genre|season|episode])\/(.*))+

and then somehow get access to the captured groups for parameter name and value inside the route handler function

(2) Use express's app.param to define a param for the search param, e.g.

/search/:searchParam/:searchValue

but I'm not sure how I can indicate that these are potentially repeating, and also in the param handler function you only seem to get access to the value being represent by the param itself, not adjoining or related param values, e.g.

app.param('searchParam', function(req,res,next,searchParam) {
  var paramRegex = new RegExp('name|year|type|season|episode|genre', i);

  if( !paramRegex.test(searchParam) ) {
    res.status(400)
       .send('URL component for "searchParam" must be like: "'+ paramRegex + '" (got:  "'+ searchParam + '")');
  } else {
    var query = req.query || {};

    query[searchParam] = '???'; // no way to access :searchValue here from what I can see?

    req.query = query;

    next();
  }
});

Anyone come across this issue before, and is there a more elegant way to do what I'd like here other than something like (1)?

UPDATE #1:

Ugh! Annoying as hell :(

Turns out that you can't use capturing groups with a quantifier this way in JS - it will only ever capture the last captured group's value (as per this post).

So I had tried to use this:

router.get( /^\/search(?:\/(name|year|type|season|episode|genre)\/(.*?))*$/i, search );

where search is simply:

function search(req,res) {  
  console.log('Params: ', req.params);
}

and when I try to access this URL:

.../search/year/2016/season/2/episode/12

what do I get in the console?:

Params:  { '0': 'episode', '1': '12' }

Only the last matched group's values. Looks like I'm just going to have to take in the entire URL and parse it "manually".

Community
  • 1
  • 1
gaelicyoda
  • 71
  • 1
  • 8
  • 1
    Maybe it's silly: you can use route `/search` and send params by `req.query`, e.g. `/search?genre=comedy&season=5` – Aikon Mogwai Jul 23 '16 at 12:48
  • Thanks for reading Aikon. Yeah, I guess I could do that, and there's an argument to say my option (1) above is really just a variation on that.... but my preference would be to have the search parameters actually make up the URL "path" as opposed to making them parameters in that way.... I think it just looks and reads better – gaelicyoda Jul 23 '16 at 19:33
  • 1
    @Alon Mogwai is right. Semantically, a URL should represent a unique "thing" in your domain. In the case of a search, that thing is an Arbitrary list of stuff based on some search terms which form a string of query terms. A query string... This is *exactly* what a querystring was meant to be used for. Have a look at every search engine ever. – Paul Jul 24 '16 at 11:15
  • I'm pretty sure you will have to parse that data manually ... but why don't you pass them in a query string? – rafaelcastrocouto Jul 24 '16 at 11:15
  • Thanks for reading and for the comments guys - to be fair, I knew from the start that I could do it like that, it's just to my eyes, having the search parameters as components in the URI looks much nicer. Not a semantic thing, just purely aesthetic. At this point, the value of that aesthetic is dwindling vs. the effoft of having doing it manually. The basis for the investigation was that `express` would go some of the way towards allowing me to do that easily enough, but now that I can see that's not really the case, I expect I probably will go with the query string route. – gaelicyoda Jul 24 '16 at 12:21
  • At this point I just want to see how it can be done. I'll post the solution when I get some more time to look at it – gaelicyoda Jul 24 '16 at 12:21

1 Answers1

0

Okay, so finally got a chance to look at this a bit more, and have come up with a way to do what I had originally intended (although I may or may not actually use this and may opt for a query string instead). But if you do find this useful, please give it a vote - much appreciated!

Here's what I did:

(1) Set up a route on my express router for my dynamic search URL (same as in the original question, I've not changed this part):

router.get( /^\/search(?:\/(name|year|type|season|episode|genre)\/(.*?))+$/i, 
            search );

(2) Updated the referenced search function as follows:

function search(req,res) {  
  console.log('URL: ' + req.url);

  var allParamsStr = req.url.match(/^\/search(\/.*)$/i);
  console.log('All parameters string: ' + allParamsStr[1]);

  var singleParamStr = allParamsStr[1].match(/(\/(?:name|year|type|season|episode|genre)\/\w+)/gi);
  console.log('Single parameter strings: ' + JSON.stringify(singleParamStr,null,2));

  for( var i = 0; i < singleParamStr.length; i++ ) {
    var params = singleParamStr[i].match(/^\/(name|year|type|season|episode|genre)\/(\w+)$/i);
    console.log('Parameter (key,value): (' + params[1] + ',' + params[2] + ')');
  }    
}

(3) Test this out by making a GET request on the following URL:

/search/year/2009/season/5/episode/10/genre/comedy

(4) I get the following output from the console:

URL: /search/year/2009/season/5/episode/10/genre/comedy
All parameters string: /year/2009/season/5/episode/10/genre/comedy
Single parameter strings: [
  "/year/2009",
  "/season/5",
  "/episode/10",
  "/genre/comedy"
]
Parameter (key,value): (year,2009)
Parameter (key,value): (season,5)
Parameter (key,value): (episode,10)
Parameter (key,value): (genre,comedy)
gaelicyoda
  • 71
  • 1
  • 8