Solr Custom RequestHandler - injecting query parameters
Asked Answered
A

4

6

Short question: I'm looking for a way (java) to intercept a query to Solr and inject a few extra filtering parameters provided by my business logic. What structures should I use?

Context: First of all, a little confession: I'm such a rookie regarding Solr. For me, setting up a server, defining a schema, coding a functional indexmanager and afterwards actually seeing the server returning the right results - exactly as intended! - was already much of an achievement for itself. Yay me!

However I'm currently working in an enterprise project that requires a little more than that. Roughly speaking, the solr instance is to be queried by several thousands of users through the very same requestHandler, being that the documents returned are automatically filtered according to a user's permission level. For example, if both the user A and the super-user B tried the very same search parameters (even the very same url), the user B would get all of user A's files and then some more. In order to accomplish this the documents are already indexed with the necessary permission level information.

Well, with this in mind and making use of Solr's extensive documentation for newb developers I tried to come up with a simple custom requestHandler that overrides the handleRequest function in order to inject the necessary extra parameters in the SolrQueryRequest. All is fine and dandy - except that I never see any difference at all in the QueryResponse, the server rudely ignoring my little manipulation. After a couple of days searching the web without so much of a hint weather if this the best approach, finally decided to come up and bother the fine folks here at StackOverflow.

So, in short, my questions are:

  • Is this a correct approach? Are there other alternatives? I can already grasp some of Solr's concepts, but admittedly there is much lacking and its entirely possible that am missing something.

  • If so, after modifying the query parameters is there anything I should do to force the QueryResponse to be updated? As far as I can tell these are merely encapsulating http requests, and I fail to sniff anything querying the server after the modifications are made.

Thanks in advance and so very sorry for the long post!

UPDATE

After a lot of reading APIs and specially much trial and error I've managed to get a functional solution. However I still fail to understand much of Solr's internals, therefore would still appreciate some enlightening. Feel free to bash at will, am still very aware of my rookiness.

The relevant part of the solution is this function which is called from by overriden handleRequestBody:

private void SearchDocumentsTypeII(SolrDocumentList results,
        SolrIndexSearcher searcher, String q, 
        UserPermissions up, int ndocs, SolrQueryRequest req,
        Map<String, SchemaField> fields, Set<Integer> alreadyFound)
        throws IOException, ParseException {


         BooleanQuery bq = new BooleanQuery();
         String permLvl = "PermissionLevel:" + up.getPermissionLevel();
         QParser parser = QParser.getParser(permLvl, null, req);
         bq.add(parser.getQuery(), Occur.MUST);

         Filter filter = CachingWrapperFilter(new QueryWrapperFilter(bq));   

         QueryParser qp = new QueryParser(q, new StandardAnalyzer());
         Query query =  qp.parse(q);                        

         append (results, searcher.search(
          query, filter, 50).scoreDocs,
          alreadyFound, fields, new HashMap<String,Object>(), 0,
          searcher.getReader(), true);

}

Basically the search query is not modified in any way, and instead a filter is applied containing the PermissionLevel of the user. Even so, why doesn't the following alternative work? The search query works perfectly when applied in the standard requestHandler, while in this case it simply doesn't hit any document.

private void SearchDocumentsTypeII(SolrDocumentList results,
        SolrIndexSearcher searcher, String q, 
        UserPermissions up, int ndocs, SolrQueryRequest req,
        Map<String, SchemaField> fields, Set<Integer> alreadyFound)
        throws IOException, ParseException {

         String qFiltered = q + " AND " + "PermissionLevel:" + up.getPermissionLevel();                              

         QueryParser qp = new QueryParser(qFiltered, new StandardAnalyzer());
         Query query =  qp.parse(qFiltered);                        

         append (results, searcher.search(
          query, null, 50).scoreDocs,
          alreadyFound, fields, new HashMap<String,Object>(), 0,
          searcher.getReader(), true);

}

Admittedly answered 31/5, 2011 at 1:43 Comment(2)
Can you put the code that you are currently using to query solr? You should be able to just modify the SolrQuery to do what you want before you send it on.Horus
@spullara yes, I can. I'll edit the post asap.Admittedly
A
1

Oh well. As previously stated, the answer that worked for me. Feel free to comment or bash!

   private void SearchDocumentsTypeII(SolrDocumentList results,
            SolrIndexSearcher searcher, String q, 
            UserPermissions up, int ndocs, SolrQueryRequest req,
            Map<String, SchemaField> fields, Set<Integer> alreadyFound)
            throws IOException, ParseException {


             BooleanQuery bq = new BooleanQuery();
             String permLvl = "PermissionLevel:" + up.getPermissionLevel();
             QParser parser = QParser.getParser(permLvl, null, req);
             bq.add(parser.getQuery(), Occur.MUST);

             Filter filter = CachingWrapperFilter(new QueryWrapperFilter(bq));   

             QueryParser qp = new QueryParser(q, new StandardAnalyzer());
             Query query =  qp.parse(q);                        

             append (results, searcher.search(
              query, filter, 50).scoreDocs,
              alreadyFound, fields, new HashMap<String,Object>(), 0,
              searcher.getReader(), true);
        }
Admittedly answered 2/6, 2011 at 16:29 Comment(1)
how have you created your jar file and added that to Solr Home.Bumkin
I
3

Good news: you don't need to write any code to do that, you just have to configure Solr properly. The superuser would hit the standard request handler while the regular user would hit another request handler (also a solr.StandardRequestHandler) configured with an invariant with the filter query you want to force upon them.

See also http://wiki.apache.org/solr/SolrRequestHandler

Isadoraisadore answered 31/5, 2011 at 3:43 Comment(9)
@Mauricio, thank you for your answer, but all users hitting the same RequestHandler is actually part of the requirements. :) I'm sorry if I haven't made myself clear, but the custom RequestHandler is necessary, if only so I can access and inject each user's permission level.Admittedly
@12N: that's like saying that all users have to use the same SQL query with the same parameters. How can that be a requirement? It's an implementation detail and it doesn't make much sense...Isadoraisadore
@Mauricio: Maybe I'm not understanding correctly what is the true purpose of the RequestHandler then, at which point I'd ask you to clarify. The way I see it now - please keep in mind my inexperience - the RequestHandler is (or can be regarded as) the entry point and processor of the search queries. And that way not only I can expose only one entry point and protect the others (the solr instance is placed behind a spring security filter), but can better integrate with my business logic - for example, this specific query injection I'm trying to achieve. Herm, does it make sense?Admittedly
@Mauricio: Also, regarding your example: it's more like having all users using the same SELECT-FROM query but having the server deciding which WHERE clauses should be applied ;)Admittedly
@12N: yes, RequestHandlers are the entry points, but the actual search/query processing is done by search components ( wiki.apache.org/solr/SearchComponent )Isadoraisadore
@12N: are you trying to use HTTP authentication to pass security information to Solr?Isadoraisadore
@Mauricio: Good heavens, no. :) But I'd rather not elaborate much about that.Admittedly
@12N: if you absolutely must have a single request handler, then yes, I see no other way than applying security on the client (as opposed to Solr), or coding it yourself. But why complicate things when you can just set up a new request handler with invariants as I described? And you can also protect the other request handler at the web server level.Isadoraisadore
@Mauricio: First of all thank you for taking the time to discuss this, it's really enriching. =) Regarding your suggestion: these are not invariants. Although this example references a "PermissionLevel" which in theory would be a more-or-less stable collection of levels, the real life scenario involves a dynamic, neverending set of data that is virtually impossible of setting up as a set of invariants. I'm sorry if it is misleading, didn't mean to. Also, my concerns do not regard security, only the interception of search requests - maybe this was a bad example to start with. -_-Admittedly
A
1

Oh well. As previously stated, the answer that worked for me. Feel free to comment or bash!

   private void SearchDocumentsTypeII(SolrDocumentList results,
            SolrIndexSearcher searcher, String q, 
            UserPermissions up, int ndocs, SolrQueryRequest req,
            Map<String, SchemaField> fields, Set<Integer> alreadyFound)
            throws IOException, ParseException {


             BooleanQuery bq = new BooleanQuery();
             String permLvl = "PermissionLevel:" + up.getPermissionLevel();
             QParser parser = QParser.getParser(permLvl, null, req);
             bq.add(parser.getQuery(), Occur.MUST);

             Filter filter = CachingWrapperFilter(new QueryWrapperFilter(bq));   

             QueryParser qp = new QueryParser(q, new StandardAnalyzer());
             Query query =  qp.parse(q);                        

             append (results, searcher.search(
              query, filter, 50).scoreDocs,
              alreadyFound, fields, new HashMap<String,Object>(), 0,
              searcher.getReader(), true);
        }
Admittedly answered 2/6, 2011 at 16:29 Comment(1)
how have you created your jar file and added that to Solr Home.Bumkin
A
0

Take a look this solr wiki page it says we should first consider using Apache Manifold Framework, and if it doesn't suite your need, then write your own requestHandler

Arequipa answered 31/5, 2011 at 14:13 Comment(1)
Thanks for the suggestion! Had never heard of Manifold, and at a glance seems a bit overkill for what am trying to achieve here - specially when I already have my own working RequestHandler - but it's always nice to have options. =)Admittedly
E
0

I had exact same requirement. In case anyone seeing this, here is my solution.

Request handler

public class SearchRequestHanlder extends SearchHandler {

  @Override
  public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception {
    var map = req.getParams();
    if (map instanceof MultiMapSolrParams) {
      MultiMapSolrParams m = (MultiMapSolrParams) map;
      MultiMapSolrParams.addParam("bq", "category:film^220", m.getMap());
    }
    super.handleRequestBody(req, rsp);
  }

  @Override
  public String getDescription() {
    return "Custom SearchRequestHanlder";
  }

}

solrconf.xml

<lib dir="/opt/solr/data/cores/movies/lib" regex=".*\.jar" />

<!-- Make sure following line is after existing default <requestHandler name="/select" -->
<requestHandler name="/select" class="com.solrplugin.SearchRequestHanlder" />
Eve answered 30/4, 2022 at 5:8 Comment(2)
How have you created your Jar file and added that to Solr Home.Bumkin
@NitinJha Yes I used this to build fat jar https://mcmap.net/q/22062/-how-can-i-create-an-executable-runnable-jar-with-dependencies-using-maven and put it inside solr/data/cores/movies/lib folderEve

© 2022 - 2024 — McMap. All rights reserved.