@Test public void page_shortcut_for_limit_and_offset() { options.setPage(3, 10); assertThat(options.getLimit()).isEqualTo(10); assertThat(options.getOffset()).isEqualTo(20); }
@Test public void with_zero_page_size() { options.setPage(1, 0); assertThat(options.getLimit()).isEqualTo(0); assertThat(options.getOffset()).isEqualTo(0); assertThat(options.getPage()).isEqualTo(0); }
@Override public final Expr compile(final QueryContext qc, final VarScope scp) throws QueryException { if (root != null) root = root.compile(qc, scp); // no steps if (steps.length == 0) return root == null ? new Context(info) : root; final Value init = qc.value, cv = initial(qc); final boolean doc = cv != null && cv.type == NodeType.DOC; qc.value = cv; try { final int sl = steps.length; for (int s = 0; s < sl; s++) { Expr e = steps[s]; // axis step: if input is a document, its type is temporarily generalized final boolean as = e instanceof Step; if (as && s == 0 && doc) cv.type = NodeType.NOD; e = e.compile(qc, scp); if (e.isEmpty()) return optPre(qc); steps[s] = e; // no axis step: invalidate context value if (!as) qc.value = null; } } finally { if (doc) cv.type = NodeType.DOC; qc.value = init; } // optimize path return optimize(qc, scp); }
@Override public Value value(final QueryContext ctx) throws QueryException { final int o = (int) ctx.resources.output.size(); final Updates updates = ctx.resources.updates(); final ContextModifier tmp = updates.mod; final TransformModifier pu = new TransformModifier(); updates.mod = pu; try { for (final Let fo : copies) { final Iter ir = ctx.iter(fo.expr); Item i = ir.next(); if (!(i instanceof ANode) || ir.next() != null) throw UPCOPYMULT.get(fo.info, fo.var.name); // copy node to main memory data instance i = ((ANode) i).dbCopy(ctx.context.options); // add resulting node to variable ctx.set(fo.var, i, info); pu.addData(i.data()); } final Value v = ctx.value(expr[0]); if (!v.isEmpty()) throw BASEX_MOD.get(info); updates.prepare(); updates.apply(); return ctx.value(expr[1]); } finally { ctx.resources.output.size(o); updates.mod = tmp; } }
@Override public final Expr inline(final QueryContext ctx, final VarScope scp, final Var v, final Expr e) throws QueryException { final Value oldVal = ctx.value; try { ctx.value = root(ctx); final Expr rt = root == null ? null : root.inline(ctx, scp, v, e); if (rt != null) { setRoot(ctx, rt); ctx.value = oldVal; ctx.value = root(ctx); } boolean change = rt != null; for (int i = 0; i < steps.length; i++) { final Expr nw = steps[i].inline(ctx, scp, v, e); if (nw != null) { steps[i] = nw; change = true; } } return change ? optimize(ctx, scp) : null; } finally { ctx.value = oldVal; } }
/** * 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; } }
@Test public void do_not_request_facets_by_default() { assertThat(options.isFacet()).isFalse(); options.addFacets(Arrays.asList("polop")); assertThat(options.isFacet()).isTrue(); }
@Test public void page_starts_at_one() { options.setPage(1, 10); assertThat(options.getLimit()).isEqualTo(10); assertThat(options.getOffset()).isEqualTo(0); assertThat(options.getPage()).isEqualTo(1); }
@Test public void max_limit() { options.setLimit(42); assertThat(options.getLimit()).isEqualTo(42); options.setLimit(QueryContext.MAX_LIMIT + 10); assertThat(options.getLimit()).isEqualTo(QueryContext.MAX_LIMIT); }
/** * Converts descendant to child steps. * * @param qc query context * @param rt root value * @return original or new expression */ private Expr children(final QueryContext qc, final Value rt) { // skip if index does not exist or is out-dated, or if several namespaces occur in the input final Data data = rt.data(); if (data == null || !data.meta.uptodate || data.nspaces.globalNS() == null) return this; Path path = this; final int sl = steps.length; for (int s = 0; s < sl; s++) { // don't allow predicates in preceding location steps final Step prev = s > 0 ? axisStep(s - 1) : null; if (prev != null && prev.preds.length != 0) break; // ignore axes other than descendant, or numeric predicates final Step curr = axisStep(s); if (curr == null || curr.axis != DESC || curr.has(Flag.FCS)) continue; // check if child steps can be retrieved for current step ArrayList<PathNode> nodes = pathNodes(data, s); if (nodes == null) continue; // cache child steps final ArrayList<QNm> qnm = new ArrayList<>(); while (nodes.get(0).parent != null) { QNm nm = new QNm(data.elemNames.key(nodes.get(0).name)); // skip children with prefixes if (nm.hasPrefix()) return this; for (final PathNode p : nodes) { if (nodes.get(0).name != p.name) nm = null; } qnm.add(nm); nodes = PathSummary.parent(nodes); } qc.compInfo(OPTCHILD, steps[s]); // build new steps int ts = qnm.size(); final Expr[] stps = new Expr[ts + sl - s - 1]; for (int t = 0; t < ts; t++) { final Expr[] preds = t == ts - 1 ? ((Preds) steps[s]).preds : new Expr[0]; final QNm nm = qnm.get(ts - t - 1); final NameTest nt = nm == null ? new NameTest(false) : new NameTest(nm, Kind.NAME, false, null); stps[t] = Step.get(info, CHILD, nt, preds); } while (++s < sl) stps[ts++] = steps[s]; path = get(info, root, stps); break; } // check if all steps yield results; if not, return empty sequence final ArrayList<PathNode> nodes = pathNodes(qc); if (nodes != null && nodes.isEmpty()) { qc.compInfo(OPTPATH, path); return Empty.SEQ; } return path; }
@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; }
@Test public void testSub05NonRef_aggregationField() { QueryContext context = new QueryContext(raptorContext); context.setAllowFullTableScan(true); context.clearMetadataServices(); String query = "Environment.applications[@_type =& ApplicationService<@_type>{$max(@_type)}]"; IQueryResult result = queryService.query(query, context); Assert.assertEquals(1, result.getEntities().size()); }
@Test public void testSubQuery_selfAggregate_groupField() { String query = "Vlan[@resourceId =& Vlan<@resourceId>{@resourceId}]"; QueryContext context = new QueryContext(cmsdbContext); context.clearMetadataServices(); context.setAllowFullTableScan(true); IQueryResult result = queryService.query(query, context); Assert.assertEquals(1, result.getEntities().size()); }
@Test public void testSubQuery_countonlyMoreSub() { String query = "ServiceInstance[ not @runsOn =& Compute{@_oid}.runsOn!ServiceInstance ]{*}"; QueryContext qc = newQueryContext(raptorContext); qc.setCountOnly(true); qc.setMaxFetch(100); IQueryResult result = queryService.query(query, qc); Assert.assertEquals(2, result.getCount()); }
@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; }
@Test public void testSub05NonRef_aggregationField_01() { QueryContext context = new QueryContext(raptorContext); context.clearMetadataServices(); context.setAllowFullTableScan(true); String query = "ServiceInstance[@port =& ServiceInstance<@healthStatus>[@healthStatus=\"unknown\"]{$max(@port)}]"; IQueryResult result = queryService.query(query, context); Assert.assertEquals(1, result.getEntities().size()); }
@Override public final Expr compile(final QueryContext ctx, final VarScope scp) throws QueryException { if (root != null) setRoot(ctx, root.compile(ctx, scp)); final Value v = ctx.value; try { ctx.value = root(ctx); return compilePath(ctx, scp); } finally { ctx.value = v; } }
@Test public void testSubQuery_maxFetch() { String query = "ServiceInstance[ not @runsOn =& Compute{@_oid} ]{*}"; QueryContext qc = newQueryContext(raptorContext); qc.setMaxFetch(1); try { queryService.query(query, qc); Assert.fail(); } catch (QueryExecuteException qe) { // expected Assert.assertEquals(QueryErrCodeEnum.INCOMPLETE_JOIN_QUERY.getErrorCode(), qe.getErrorCode()); } }
@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; } }
/** * Performs the test-uris function. * * @param ctx query context * @return resulting value * @throws QueryException query exception */ private Item testUris(final QueryContext ctx) throws QueryException { checkCreate(ctx); final ArrayList<IO> inputs = new ArrayList<>(); final Iter ir = ctx.iter(expr[0]); for (Item it; (it = ir.next()) != null; ) inputs.add(checkPath(it, ctx)); return new Suite(ctx, info).test(inputs); }
@Override Value[] evalArgs(final QueryContext ctx) throws QueryException { final int al = expr.length - 1; final Value[] args = new Value[al]; for (int a = 0; a < al; ++a) args[a] = ctx.value(expr[a]); return args; }
@Override public final Value value(final QueryContext qc) throws QueryException { final int es = exprs.length; final Value[] args = new Value[es]; for (int e = 0; e < es; ++e) args[e] = qc.value(exprs[e]); return toValue(eval(args, qc), qc, sc); }
/** * Sets a new root expression and eliminates a superfluous context item. * * @param ctx query context * @param rt root expression */ private void setRoot(final QueryContext ctx, final Expr rt) { root = rt; if (root instanceof Context) { ctx.compInfo(OPTREMCTX); root = null; } }
/** * Optimizes descendant-or-self steps and static types. * * @param ctx query context */ void optSteps(final QueryContext ctx) { boolean opt = false; Expr[] st = steps; for (int l = 1; l < st.length; ++l) { if (!(st[l - 1] instanceof Step && st[l] instanceof Step)) continue; final Step prev = (Step) st[l - 1]; final Step curr = (Step) st[l]; if (!prev.simple(DESCORSELF, false)) continue; if (curr.axis == CHILD && !curr.has(Flag.FCS)) { // descendant-or-self::node()/child::X -> descendant::X final int sl = st.length; final Expr[] tmp = new Expr[sl - 1]; System.arraycopy(st, 0, tmp, 0, l - 1); System.arraycopy(st, l, tmp, l - 1, sl - l); st = tmp; curr.axis = DESC; opt = true; } else if (curr.axis == ATTR && !curr.has(Flag.FCS)) { // descendant-or-self::node()/@X -> descendant-or-self::*/@X prev.test = new NameTest(false); opt = true; } } if (opt) ctx.compInfo(OPTDESC); // set atomic type for single attribute steps to speedup predicate tests if (root == null && st.length == 1 && st[0] instanceof Step) { final Step curr = (Step) st[0]; if (curr.axis == ATTR && curr.test.mode == Mode.STD) curr.type = SeqType.NOD_ZO; } steps = st; }
@Override public QueryContextResponse queryContext(final QueryContext query) { logger.info("=> queryContext: {}", query.toString()); // check if the incoming query is on Room EntityId queryEntityId = query.getEntityIdList().get(0); if (queryEntityId.getId().contains("Room")) { return createRoomQueryResponse(queryEntityId); } else if (queryEntityId.getId().contains("Shutter")) { return createShutterQueryResponse(queryEntityId); } else { QueryContextResponse queryResponse = new QueryContextResponse(); queryResponse.setErrorCode(new StatusCode(CodeEnum.CODE_404, queryEntityId.getId())); return queryResponse; } }
/** * Performs the test-libraries function (still experimental). * * @param ctx query context * @return resulting value * @throws QueryException query exception */ private Item testLibraries(final QueryContext ctx) throws QueryException { checkCreate(ctx); final TokenList tl = new TokenList(); final Iter ir = ctx.iter(expr[0]); for (Item it; (it = ir.next()) != null; ) tl.add(checkStr(it)); return new Suite(ctx, info).test(tl); }
/** * Returns the specified function literal. * * @param name function name * @param arity number of arguments * @param dyn dynamic invocation flag * @param ctx query context * @param ii input info * @return literal function expression * @throws QueryException query exception */ public static FItem get( final QNm name, final long arity, final boolean dyn, final QueryContext ctx, final InputInfo ii) throws QueryException { final Expr[] args = new Expr[(int) arity]; final Var[] vars = new Var[args.length]; for (int i = 0; i < args.length; i++) { vars[i] = ctx.uniqueVar(ii, null); args[i] = new VarRef(ii, vars[i]); } final TypedFunc f = get(name, args, dyn, ctx, ii); if (f == null) { if (!dyn) FUNCUNKNOWN.thrw(ii, name + "#" + arity); return null; } // compile the function if it hasn't been done statically if (dyn && f.fun instanceof UserFuncCall) { final UserFunc usf = ((UserFuncCall) f.fun).func(); if (usf != null && usf.declared) usf.compile(ctx); } final FuncType ft = f.type; return new FuncItem(name, vars, f.fun, ft, false); }
/** * Extracts entries from the archive. * * @param ctx query context * @return text entries * @throws QueryException query exception */ private TokenList extract(final QueryContext ctx) throws QueryException { final B64 archive = (B64) checkType(checkItem(expr[0], ctx), AtomType.B64); TokenSet hs = null; if (expr.length > 1) { // filter result to specified entries hs = new TokenSet(); final Iter names = ctx.iter(expr[1]); for (Item en; (en = names.next()) != null; ) { hs.add(checkElmStr(en).string(info)); } } final TokenList tl = new TokenList(); final ArchiveIn in = ArchiveIn.get(archive.input(info), info); try { while (in.more()) { final ZipEntry ze = in.entry(); if (ze.isDirectory()) continue; if (hs == null || hs.delete(token(ze.getName())) != 0) tl.add(in.read()); } } catch (final IOException ex) { Util.debug(ex); ARCH_FAIL.thrw(info, ex); } finally { in.close(); } return tl; }
/** * Returns a serializer for the given output stream. Optional output declarations within the query * will be included in the serializer instance. * * @param os output stream * @return serializer instance * @throws IOException query exception * @throws QueryException query exception */ public Serializer getSerializer(final OutputStream os) throws IOException, QueryException { compile(); try { return Serializer.get(os, qc.serParams()); } catch (final QueryIOException ex) { throw ex.getCause(); } }
@Override public Value value(final QueryContext qc) throws QueryException { if (seqType().zeroOrOne()) { final Value v = item(qc, info); return v == null ? Empty.SEQ : v; } return qc.iter(this).value(); }