private void handleFilterExclusions() throws IOException { List<String> excludeTags = freq.domain.excludeTags; if (excludeTags == null || excludeTags.size() == 0) { return; } // TODO: somehow remove responsebuilder dependency ResponseBuilder rb = SolrRequestInfo.getRequestInfo().getResponseBuilder(); Map tagMap = (Map) rb.req.getContext().get("tags"); if (tagMap == null) { // no filters were tagged return; } IdentityHashMap<Query, Boolean> excludeSet = new IdentityHashMap<>(); for (String excludeTag : excludeTags) { Object olst = tagMap.get(excludeTag); // tagMap has entries of List<String,List<QParser>>, but subject to change in the future if (!(olst instanceof Collection)) continue; for (Object o : (Collection<?>) olst) { if (!(o instanceof QParser)) continue; QParser qp = (QParser) o; try { excludeSet.put(qp.getQuery(), Boolean.TRUE); } catch (SyntaxError syntaxError) { // This should not happen since we should only be retrieving a previously parsed query throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, syntaxError); } } } if (excludeSet.size() == 0) return; List<Query> qlist = new ArrayList<>(); // add the base query if (!excludeSet.containsKey(rb.getQuery())) { qlist.add(rb.getQuery()); } // add the filters if (rb.getFilters() != null) { for (Query q : rb.getFilters()) { if (!excludeSet.containsKey(q)) { qlist.add(q); } } } // now walk back up the context tree // TODO: we lose parent exclusions... for (FacetContext curr = fcontext; curr != null; curr = curr.parent) { if (curr.filter != null) { qlist.add(curr.filter); } } // recompute the base domain fcontext.base = fcontext.searcher.getDocSet(qlist); }
/** * factory method for invoking json facet framework as whole. Note: this is currently only used * from SimpleFacets, not from JSON Facet API itself. */ public static FacetProcessor<?> createProcessor( SolrQueryRequest req, Map<String, Object> params, DocSet docs) { FacetParser parser = new FacetTopParser(req); FacetRequest facetRequest = null; try { facetRequest = parser.parse(params); } catch (SyntaxError syntaxError) { throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, syntaxError); } FacetContext fcontext = new FacetContext(); fcontext.base = docs; fcontext.req = req; fcontext.searcher = req.getSearcher(); fcontext.qcontext = QueryContext.newContext(fcontext.searcher); return facetRequest.createFacetProcessor(fcontext); }
/** * @param filter The filter for the bucket that resulted in this context/domain. Can be null if * this is the root context. * @param domain The resulting set of documents for this facet. */ public FacetContext sub(Query filter, DocSet domain) { FacetContext ctx = new FacetContext(); ctx.parent = this; ctx.base = domain; ctx.filter = filter; // carry over from parent ctx.flags = flags; ctx.qcontext = qcontext; ctx.req = req; ctx.searcher = searcher; return ctx; }
private void handleDomainChanges() throws IOException { if (freq.domain == null) return; handleFilterExclusions(); // Check filters... if we do have filters they apply after domain changes. // We still calculate them first because we can use it in a parent->child domain change. evalFilters(); boolean appliedFilters = handleBlockJoin(); if (this.filter != null && !appliedFilters) { fcontext.base = fcontext.base.intersection(filter); } }
void processSubs(SimpleOrderedMap<Object> response, Query filter, DocSet domain) throws IOException { // TODO: what if a zero bucket has a sub-facet with an exclusion that would yield results? // should we check for domain-altering exclusions, or even ask the sub-facet for // it's domain and then only skip it if it's 0? if (domain == null || domain.size() == 0 && !freq.processEmpty) { return; } for (Map.Entry<String, FacetRequest> sub : freq.getSubFacets().entrySet()) { // make a new context for each sub-facet since they can change the domain FacetContext subContext = fcontext.sub(filter, domain); FacetProcessor subProcessor = sub.getValue().createFacetProcessor(subContext); if (fcontext.getDebugInfo() != null) { // if fcontext.debugInfo != null, it means rb.debug() == true FacetDebugInfo fdebug = new FacetDebugInfo(); subContext.setDebugInfo(fdebug); fcontext.getDebugInfo().addChild(fdebug); fdebug.setReqDescription(sub.getValue().getFacetDescription()); fdebug.setProcessor(subProcessor.getClass().getSimpleName()); if (subContext.filter != null) fdebug.setFilter(subContext.filter.toString()); final RTimer timer = new RTimer(); subProcessor.process(); long timeElapsed = (long) timer.getTime(); fdebug.setElapse(timeElapsed); fdebug.putInfoItem("domainSize", (long) subContext.base.size()); } else { subProcessor.process(); } response.add(sub.getKey(), subProcessor.getResponse()); } }
// returns "true" if filters were applied to fcontext.base already private boolean handleBlockJoin() throws IOException { boolean appliedFilters = false; if (!(freq.domain.toChildren || freq.domain.toParent)) return appliedFilters; // TODO: avoid query parsing per-bucket somehow... String parentStr = freq.domain.parents; Query parentQuery; try { QParser parser = QParser.getParser(parentStr, fcontext.req); parentQuery = parser.getQuery(); } catch (SyntaxError err) { throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "Error parsing block join parent specification: " + parentStr); } BitDocSet parents = fcontext.searcher.getDocSetBits(parentQuery); DocSet input = fcontext.base; DocSet result; if (freq.domain.toChildren) { // If there are filters on this facet, then use them as acceptDocs when executing toChildren. // We need to remember to not redundantly re-apply these filters after. DocSet acceptDocs = this.filter; if (acceptDocs == null) { acceptDocs = fcontext.searcher.getLiveDocs(); } else { appliedFilters = true; } result = BlockJoin.toChildren(input, parents, acceptDocs, fcontext.qcontext); } else { result = BlockJoin.toParents(input, parents, fcontext.qcontext); } fcontext.base = result; return appliedFilters; }