@Override public boolean indexAccessible(final IndexInfo ii) throws QueryException { final int es = exprs.length; final boolean[] ng = new boolean[es]; int costs = 0; for (final FTExpr expr : exprs) { if (!expr.indexAccessible(ii)) return false; // use worst costs for estimation, as all index results may need to be scanned if (costs < ii.costs) costs = ii.costs; } ii.costs = costs; negated = ng; return true; }
/** * Merges a single predicate with the root expression and returns the resulting expression, or * returns a self reference if the expression cannot be merged. This function is e.g. called by * {@link Where#optimize}. * * @param qc query context * @param scp variable scope * @param root root expression * @return expression * @throws QueryException query exception */ public Expr merge(final Expr root, final QueryContext qc, final VarScope scp) throws QueryException { // only one predicate can be rewritten; root expression must yield nodes if (preds.length != 1 || !(root.seqType().type instanceof NodeType)) return this; final Expr pred = preds[0]; // a[.] -> a if (pred instanceof Context) return root; if (!pred.seqType().mayBeNumber()) { // a[b] -> a/b if (pred instanceof Path) return Path.get(info, root, pred).optimize(qc, scp); if (pred instanceof CmpG) { final CmpG cmp = (CmpG) pred; final Expr expr1 = cmp.exprs[0], expr2 = cmp.exprs[1]; // only first operand can depend on context if (!expr2.has(Flag.CTX)) { Expr path = null; // a[. = 'x'] -> a = 'x' if (expr1 instanceof Context) path = root; // a[text() = 'x'] -> a/text() = 'x' if (expr1 instanceof Path) path = Path.get(info, root, expr1).optimize(qc, scp); if (path != null) return new CmpG(path, expr2, cmp.op, cmp.coll, cmp.sc, cmp.info); } } if (pred instanceof FTContains) { final FTContains cmp = (FTContains) pred; final FTExpr ftexpr = cmp.ftexpr; // only first operand can depend on context if (!ftexpr.has(Flag.CTX)) { final Expr expr = cmp.expr; Expr path = null; // a[. contains text 'x'] -> a contains text 'x' if (expr instanceof Context) path = root; // [text() contains text 'x'] -> a/text() contains text 'x' if (expr instanceof Path) path = Path.get(info, root, expr).optimize(qc, scp); if (path != null) return new FTContains(path, ftexpr, cmp.info); } } } return this; }
@Override public FTExpr compile(final CompileContext cc) throws QueryException { super.compile(cc); boolean not = true; for (final FTExpr expr : exprs) not &= expr instanceof FTNot; if (not) { // convert (!A and !B and ...) to !(A or B or ...) final int es = exprs.length; for (int e = 0; e < es; ++e) exprs[e] = exprs[e].exprs[0]; return new FTNot(info, new FTOr(info, exprs)); } return this; }
@Override public NodeIter iter(final QueryContext ctx) throws QueryException { final FTIter ir = ftexpr.iter(ctx); return new NodeIter() { @Override public ANode next() throws QueryException { final FTNode it = ir.next(); if (it != null) { // add entry to visualization if (ctx.ftpos != null) ctx.ftpos.add(it.data, it.pre, it.all); // assign scoring, if not done yet it.score(); // remove matches reference to save memory it.all = null; } return it; } }; }
@Override public void plan(final Serializer ser) throws IOException { ser.openElement(this, DATA, token(ictx.data.meta.name)); ftexpr.plan(ser); ser.closeElement(); }
@Override public Expr remove(final Var v) { ftexpr.remove(v); return this; }
@Override public boolean removable(final Var v) { return ftexpr.removable(v); }
@Override public int count(final Var v) { return ftexpr.count(v); }
@Override public boolean uses(final Use u) { return ftexpr.uses(u); }