/** * Checks if the predicates are successful for the specified item. * * @param it item to be checked * @param qc query context * @return result of check * @throws QueryException query exception */ protected final boolean preds(final Item it, final QueryContext qc) throws QueryException { if (preds.length == 0) return true; // set context value and position final Value cv = qc.value; try { if (qc.scoring) { double s = 0; for (final Expr p : preds) { qc.value = it; final Item i = p.test(qc, info); if (i == null) return false; s += i.score(); } it.score(Scoring.avg(s, preds.length)); } else { for (final Expr p : preds) { qc.value = it; if (p.test(qc, info) == null) return false; } } return true; } finally { qc.value = cv; } }
@Override public Expr optimize(final QueryContext qc, final VarScope scp) throws QueryException { // number of predicates may change in loop for (int p = 0; p < preds.length; p++) { final Expr pred = preds[p]; if (pred instanceof CmpG || pred instanceof CmpV) { final Cmp cmp = (Cmp) pred; if (cmp.exprs[0].isFunction(Function.POSITION)) { final Expr e2 = cmp.exprs[1]; final SeqType st2 = e2.seqType(); // position() = last() -> last() // position() = $n (numeric) -> $n if (e2.isFunction(Function.LAST) || st2.one() && st2.type.isNumber()) { if (cmp instanceof CmpG && ((CmpG) cmp).op == OpG.EQ || cmp instanceof CmpV && ((CmpV) cmp).op == OpV.EQ) { qc.compInfo(OPTWRITE, pred); preds[p] = e2; } } } } else if (pred instanceof And) { if (!pred.has(Flag.FCS)) { // replace AND expression with predicates (don't swap position tests) qc.compInfo(OPTPRED, pred); final Expr[] and = ((Arr) pred).exprs; final int m = and.length - 1; final ExprList el = new ExprList(preds.length + m); for (final Expr e : Arrays.asList(preds).subList(0, p)) el.add(e); for (final Expr a : and) { // wrap test with boolean() if the result is numeric el.add(Function.BOOLEAN.get(null, info, a).optimizeEbv(qc, scp)); } for (final Expr e : Arrays.asList(preds).subList(p + 1, preds.length)) el.add(e); preds = el.finish(); } } else if (pred instanceof ANum) { final ANum it = (ANum) pred; final long i = it.itr(); if (i == it.dbl()) { preds[p] = Pos.get(i, info); } else { qc.compInfo(OPTREMOVE, this, pred); return Empty.SEQ; } } else if (pred.isValue()) { if (pred.ebv(qc, info).bool(info)) { qc.compInfo(OPTREMOVE, this, pred); preds = Array.delete(preds, p--); } else { // handle statically known predicates qc.compInfo(OPTREMOVE, this, pred); return Empty.SEQ; } } } return this; }
@Override public Expr optimize(final QueryContext qc, final VarScope scp) throws QueryException { final Expr c = super.optimize(qc, scp); if (c != this) return c; final int es = exprs.length; final ExprList list = new ExprList(es); for (int i = 0; i < es; i++) { Expr e = exprs[i]; if (e instanceof CmpG) { // merge adjacent comparisons while (i + 1 < es && exprs[i + 1] instanceof CmpG) { final Expr tmp = ((CmpG) e).union((CmpG) exprs[i + 1], qc, scp); if (tmp != null) { e = tmp; i++; } else { break; } } } // expression will always return true if (e == Bln.TRUE) return optPre(Bln.TRUE, qc); // skip expression yielding false if (e != Bln.FALSE) list.add(e); } // all arguments return false if (list.isEmpty()) return optPre(Bln.FALSE, qc); if (es != list.size()) { qc.compInfo(OPTREWRITE_X, this); exprs = list.finish(); } compFlatten(qc); boolean not = true; for (final Expr expr : exprs) { if (!expr.isFunction(Function.NOT)) { not = false; break; } } if (not) { qc.compInfo(OPTREWRITE_X, this); final int el = exprs.length; final Expr[] inner = new Expr[el]; for (int e = 0; e < el; e++) inner[e] = ((Arr) exprs[e]).exprs[0]; final Expr ex = new And(info, inner).optimize(qc, scp); return Function.NOT.get(null, info, ex).optimize(qc, scp); } // return single expression if it yields a boolean return exprs.length == 1 ? compBln(exprs[0], info) : this; }
@Override public Expr compile(final QueryContext qc, final VarScope scp) throws QueryException { final Value init = qc.value; // never compile predicates with empty sequence as context value (#1016) if (init != null && init.isEmpty()) qc.value = null; try { final int pl = preds.length; for (int p = 0; p < pl; ++p) preds[p] = preds[p].compile(qc, scp).optimizeEbv(qc, scp); return this; } finally { qc.value = init; } }
/** * Returns all tokens of the query. * * @param qc query context * @return token list * @throws QueryException query exception */ private TokenList tokens(final QueryContext qc) throws QueryException { final TokenList tl = new TokenList(); final Iter ir = qc.iter(query); for (byte[] qu; (qu = nextToken(ir)) != null; ) { // skip empty tokens if not all results are needed if (qu.length != 0 || mode == FTMode.ALL || mode == FTMode.ALL_WORDS) tl.add(qu); } return tl; }