joemfb / ml-search-ng

angular module for MarkLogic search applications
https://joemfb.github.io/ml-search-ng/
9 stars 10 forks source link

Ability to register custom URL param parsing/populating #93

Closed ryanjdew closed 8 years ago

ryanjdew commented 8 years ago

I was thinking it would be nice to be able to register a handler for custom constraints. I envision the use being something like this:

mlSearch.registerPrefix({
  'prefix': 'myCustRangeStartPrefix',
  'toQuery': function(name, value) {
    return qb.ext.rangeConstraint(name, 'GE', value);
  }
});
mlSearch.registerPrefix({
  'prefix': 'myCustRangeEndPrefix',
  'toQuery': function(name, value) {
    return qb.ext.rangeConstraint(name, 'LE', value);
  }
});
mlSearch
  .customPrefix('myCustRangeStartPrefix', 'pubDateTime', '01-01-2010T0:00:00')
  .customPrefix('myCustRangeEndPrefix', 'pubDateTime', '01-01-2016T0:00:00')
  .search();
// Updates search with restricted pubDateTime queries and adds to the URL 'myCustRangeStartPrefix:pubDateTime=01-01-2010T0:00:00&myCustRangeEndPrefix:pubDateTime=01-01-2016T0:00:00'

I can work on it, but would love feedback on API or any other concerns that there may be.

joemfb commented 8 years ago

Hey, sorry for the delayed response. What would be the breadth of this feature? Is it just an additional JS API, or would it handle constraints in string-queries? Facets that match the prefix?

ryanjdew commented 8 years ago

I was thinking it could be used with custom constraints like the one I added for date ranges here: https://github.com/marklogic/slush-marklogic-node/pull/266/files. I was thinking that you could register a prefix like 'dateTimeStart' and 'dateTimeEnd' then mlsearch could handle writing to and generating an additional query based on the function registered from the URL.

joemfb commented 8 years ago

So, totally separate from the existing query-building approach for string queries, constraint queries, etc.?

ryanjdew commented 8 years ago

I was thinking it would only affect two places.

  1. Parsing the URL would look for the registered prefix and call the associates function to build the custom query.
  2. An added mlSearch function to add an additional query with the provided prefix function so that the URL can be updated appropriately when the value is set and search is executed.

In the function generating the query it can obviously choose to use MLQueryBuilder, etc. Perhaps mlSearch already has something that I'm missing that I could be leveraged to accomplish that? Basically the problem I'm trying to solve is adding custom additional queries and still allow it to be deep linked with minimal effort from the person using ml-search.

joemfb commented 8 years ago

There is some support in MLSearchController for handling additional URL params. Here's a basic example that conditionally adds a zip URL param and an associated radius query (you'd add the following to your search controller, based on https://github.com/joemfb/ml-search-ng/blob/master/sample/search-ctrl.js):

var qb = mlSearch.qb;
ctrl.zip = null;

// implement superCtrl extension method
ctrl.parseExtraURLParams = function () {
  var zip = $location.search().zip

  if (ctrl.zip !== zip) {
    ctrl.zip = zip;
    return true
  }

  return false
};

// implement superCtrl extension method
ctrl.updateExtraURLParams = function () {
  if (ctrl.zip) {
    $location.search('zip', ctrl.zip);
  } else {
    $location.search( 'zip', null );
  }
};

ctrl._search = function () {
  mlSearch.clearAdditionalQueries();
  addRadiusQuery();
  superCtrl._search.call(ctrl);
};

function addRadiusQuery () {
  if (ctrl.zip) {
    mlSearch.addAdditionalQuery({
    'custom-constraint-query': {
      'constraint-name': 'nearzip',
      // radius around zip code, in miles
      'radius': 10,
      'zip': ctrl.zip
    }});
  }
}

This works by extending the controller search implementation function (ctrl._search), and utilizing the extension methods ctrl.parseExtraURLParams and ctrl.updateExtraURLParams. The super ctrl takes care of calling those at the right time. (documented here: https://joemfb.github.io/ml-search-ng/MLSearchController.html; note that ctrl.parseExtraURLParams returns a boolean specifying whether or not a search should be run)

To me, the ugliest part about this is having to first clear all the additional queries, then conditionally add the custom-constraint-query; I'd prefer to have a method on mlSearch that just accepted a function that would conditionally return a query.

This is a totally generic extension pattern, so it's not exactly what you're looking for. However, it does show the edge cases you have to handle, and gives you a ctrl instance variable (ctrl.zip) that the UI can interact with.

I'm a little hesitant to add APIs for this feature without considering how it could more closely integrate with the rest of the query-building code. I'd personally like to have a flexible, unified system for managing different types of constraints rather than the basic facet constraints / string-query approach that we currently have, but I haven't had the time to propose a design for such a system ...

What are your thoughts on this example?

ryanjdew commented 8 years ago

I actually really like that example. I think it makes it simple enough and I'm fine with this ticket being closed.

joemfb commented 8 years ago

Great, I'm glad that's helpful. I need to incorporate examples into the documentation; the API surface is already too big. I'm not really sure how to do that on top of the existing JSDoc-based approach.

Let me know if you have or see any good documentation like that.

grtjn commented 8 years ago

Nice example indeed. Might be nice though if it were slightly easier to manage the additional queries, somehow categorize them. Would something like mlSearch.addExtraQueries('zip', radiusQuery), and mlSearch.removeExtraQueries('zip') make sense?