4

I use a SolrJ-based client to query Solr and I have been trying to construct HTTP requests where facet name/value pairs are excluded. The web interface I am working with has a refine further functionality, which allows excluding one or more facet values. I have 3 facet fields: domain, content type and author and I would like to be able to handle faceting by exclusion on each of them. For example, q = Dickens AND fq=-author:Dickens, Janet will construct the following HTTP request:

/solr/solrbase/select?q=Dickens&fq=-author:Dickens%2c+Janet&wt=json&indent=true

Whereas the XML dump will look like:

             <facets>
              <facet name="author">
                <facetEntry count="20">Dickens, Charles</facetEntry>
                <facetEntry count="10">Dickens, Sarah</facetEntry>
              </facet>
            </facets>

So far, the Java implementation I am working with does not seems to handle filter query exclusion:

private HttpSolrServer solrServer;
solrServer = new HttpSolrServer("http://localhost:8983/solr/");

private static final String CONFIG_SOLR_FACET_FIELD = "facet_field";
private String[] _facetFields = new String[] {"author"};

private static final String CONFIG_SOLR_FACETS = "facets"
     Element el = myParams.getChild(CONFIG_SOLR_FACETS);

        _facetUse = el.getAttributeValue("useFacets", "true");
        _facetMinCount = el.getAttributeValue("minCount", String.valueOf(1));
        _facetLimit = el.getAttributeValue("limit", String.valueOf(20));


List vals = el.getChildren(CONFIG_SOLR_FACET_FIELD);
        if (vals.size() > 0) {
            _facetFields = new String[vals.size()];
            for (int i=0; i < vals.size(); i++) {
            _facetFields[i] = ((Element)vals.get(i)).getTextTrim();
            }   
        }

SolrQuery query = new SolrQuery();
query.setQuery(qs);


List facetList = doc.getRootElement().getChildren("facet");
                    Iterator<String> it = facetList.iterator();
                    while (it.hasNext()) {
                        Element el = (Element)it.next(); //
                        String name = el.getAttributeValue("name"); 
                        String value = el.getTextTrim();
                        if (name != null && value != null) {    
                            facets.add(name+":"+value);
                        }

                    }


query.setQuery(qs).
           setFacet(Boolean.parseBoolean(_facetUse)).
           setFacetMinCount(Integer.parseInt(_facetMinCount)).
           setFacetLimit(Integer.parseInt(_facetLimit)).

        for (int i=0; i<_facetFields.length; i++) {
            query.addFacetField(_facetFields[i]);       
        };

        for (int i=0; i<facets.size(); i++) {
            query.addFilterQuery(facets.get(i));
        };
  return query;

    }

I was recommended to use something along these lines:

  SolrQuery solrQuery = new SolrQuery();
  solrQuery.set(CommonParams.FQ, “-author:Dickens,Janet”);

However, this seems to be a hardcoded approach and it cannot be easily applied across all 3 facets and all facet values. I have looked at this, but still it is not clear to me how I should include the exclusion variant in my current code. Can you help with this?

Thanks indeed,

I.

EDIT 1

I have attached the code to construct/prepare the Solr Query, but I should have also included how the Solr instance is actually queried:

private QueryResponse execQuery(SolrQuery query) throws SolrServerException {
    QueryResponse rsp = solrServer.query( query );
    return rsp;     

}

Moreover, it would be helpful to post the code that converts the Solr query response for the facets into something that can be understood by the web application:

Element elfacets = new Element("facets"); 
            List<FacetField> facets = rsp.getFacetFields();
            if (facets != null) {
                int i = 0;
                for (FacetField facet : facets) {
                    Element sfacet = new Element("facet");
                    sfacet.setAttribute("name", facet.getName());

                    List<Count> facetEntries = facet.getValues();

                    for(FacetField.Count fcount : facetEntries) {
                        Element facetEntry = new Element("facetEntry");
                        facetEntry.setText(fcount.getName());
                        facetEntry.setAttribute("count", String.valueOf(fcount.getCount()));
                        sfacet.addContent(facetEntry);
                    }
                    elfacets.addContent(sfacet);

            }
            root.addContent(elfacets);
        } 


        doc.addContent(root);

        return doc;
    }

"facets" is nothing more than the XSLT, which includes rules on how to map Solr facets with the facets as handled by the web application.

EDIT 2 I attach the "facets" template, which is called by the code as presented in EDIT 1:

<xsl:template name="facets">
                <xsl:param name="q" />
                <xsl:analyze-string select="$q" regex='AND facet_(.*?):\(("?.*?"?)\)'>
                        <xsl:matching-substring>
                        <xsl:choose>
                        <xsl:when test="regex-group(1) = 'author'">
                                    <facet name="author"><xsl:value-of select="regex-group(2)" /></facet>
                        </xsl:when>    
                        </xsl:choose>
                        </xsl:matching-substring>
                       <xsl:non-matching-substring>
                       <!--<xsl:analyze-string select="$q" regex='AND NOT facet_(.*?):\(("?.*?"?)\)'>
                       <xsl:matching-substring>
                       <xsl:choose>
                       <xsl:when test="regex-group(1) = 'author'">
                                    <facet name="author"><xsl:value-of select="regex-group(2)" /></facet>
                        </xsl:when>   
                        </xsl:choose>
                     </xsl:matching-substring>
                     </xsl:analyze-string>-->
                     </xsl:non-matching-substring>
                </xsl:analyze-string>
    </xsl:template>
</xsl:stylesheet>

The template only features the author facet, but I have 3 facets in total. It should be noted that my web application has the following syntax for excluding facets:

AND NOT facet_author:("Dickens, Janet")
Community
  • 1
  • 1
panza
  • 1,341
  • 7
  • 38
  • 68
  • 1
    You want to exclude `fq` on need basis, right? Can you control the passing of parameters to the query? – buddy86 Mar 03 '14 at 06:17
  • Yes, that is exactly what I would like to do! How would you pass the parameters to the query? Where? Can you provide examples? Please see my additional edit where I show the template facets. Thank you for your help. – panza Mar 03 '14 at 16:23

1 Answers1

2

I'm sure you you have the following lines inside some method. Instead of hard coding the fq part, have some variable there.

SolrQuery solrQuery = new SolrQuery();
solrQuery.set(CommonParams.FQ, “-author:Dickens,Janet”);

If you need to use the fq, pass proper parameter (e.g “-author:Dickens,Janet”). Otherwise pass an empty string. So, your query will be like

/solr/solrbase/select?q=Dickens&fq=&wt=json&indent=true

Then add your faceting part of the query. Though your query having fq=, it won't throw an error. It basically won't work for the fq part. But rest of the query will work fine.

Hope this will help.

buddy86
  • 1,434
  • 12
  • 20
  • apologies for the delay in replying, but I have been away. I cannot pass a proper parameter as in your example. Are you suggesting to use something like solrQuery.set(CommonParams.FQ)? Thanks indeed! – panza Mar 08 '14 at 23:15