/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.addthis.hydra.data.query; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.concurrent.atomic.AtomicLong; import com.addthis.basis.util.CUID; import com.addthis.basis.util.LessStrings; import com.addthis.bundle.channel.DataChannelOutput; import com.addthis.codec.codables.Codable; import com.addthis.codec.json.CodecJSON; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; import org.apache.commons.lang3.mutable.MutableInt; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import io.netty.channel.ChannelProgressivePromise; /** * Object representation of a tree query. */ @JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE) public class Query implements Codable { public static final Logger traceLog = LoggerFactory.getLogger("query-trace"); private static final int MAX_PRINT_LENGTH = 3000; private static final String SESSION_ID = CUID.createCUID(); private static final AtomicLong queryIds = new AtomicLong(0); @JsonProperty private String[] paths; @JsonProperty private String[] ops; @JsonProperty private String job; @JsonProperty private boolean trace; @JsonProperty private String sessionId; @JsonProperty private long queryId; @JsonProperty private HashMap<String, String> params = new HashMap<>(); @JsonIgnore public transient ChannelProgressivePromise queryPromise = null; private Query() {} public Query(String job, String[] paths, String[] ops) { this.job = job; this.paths = paths; this.ops = ops; this.sessionId = SESSION_ID; this.queryId = queryIds.incrementAndGet(); } public ChannelProgressivePromise getQueryPromise() { return queryPromise; } public static String getPathString(QueryElement... path) { StringBuilder sb = new StringBuilder(); int i = 0; for (QueryElement e : path) { if (i++ > 0) { sb.append("/"); } e.toCompact(sb); } return sb.toString(); } public static String getShortPathString(QueryElement... path) { StringBuilder sb = new StringBuilder(); int i = 0; for (QueryElement e : path) { if (i++ > 0) { sb.append("/"); } e.toCompact(sb); } String queryString = sb.toString(); if (queryString.length() > MAX_PRINT_LENGTH) { queryString = queryString.substring(0, MAX_PRINT_LENGTH); } return queryString; } public String uuid() { return String.valueOf(queryId); } public long queryId() { return queryId; } public String sessionId() { return sessionId; } @Override public String toString() { try { String queryString = CodecJSON.encodeString(this); if (queryString != null && queryString.length() > MAX_PRINT_LENGTH) { queryString = queryString.substring(0, MAX_PRINT_LENGTH); } return queryString; } catch (Exception ex) { return LessStrings.join(paths, "|") .concat(";") .concat(ops != null ? LessStrings.join(ops, "|") : "") .concat(";") .concat(job != null ? job : ""); } } public String[] getPaths() { return paths; } public boolean isTraced() { return trace; } public Query setTraced(boolean traced) { this.trace = traced; return this; } public List<QueryElement[]> getQueryPaths() { ArrayList<QueryElement[]> list = new ArrayList<>(paths.length); for (String path : paths) { list.add(parseQueryPath(path)); } return list; } /** * turns compact query notation (+:+hits) into an object array */ private static QueryElement[] parseQueryPath(String path) { MutableInt column = new MutableInt(0); ArrayList<QueryElement> list = new ArrayList<>(); for (String pe : LessStrings.split(path, "/")) { list.add(new QueryElement().parse(pe, column)); } return list.toArray(new QueryElement[list.size()]); } public QueryOpProcessor newProcessor(DataChannelOutput output, ChannelProgressivePromise opPromise) { return new QueryOpProcessor(output, ops, opPromise); } public String getJob() { return job; } public void setJob(String job) { this.job = job; } public String[] getOps() { return ops; } /** * @return first a query suitable for the next query worker in the stack */ public Query createPipelinedQuery() { Query newQuery = cloneTo(new Query()); if (ops != null && ops.length > 0) { String[] newops = new String[ops.length - 1]; System.arraycopy(ops, 1, newops, 0, newops.length); newQuery.ops = newops; String pop = ops[0]; ops = new String[]{pop}; } return newQuery; } private Query cloneTo(Query q) { q.paths = paths; q.ops = ops; q.job = job; q.trace = trace; q.params = params; q.sessionId = sessionId; q.queryId = queryId; return q; } public HashMap<String, String> getParameters() { return params; } public Query setParameterIfNotYetSet(String key, Object value) { if (params.get(key) == null) { setParameter(key, value); } return this; } public Query setParameter(String key, Object value) { if (value != null) { params.put(key, value.toString()); } else { params.remove(key); } return this; } public String getParameter(String key) { return getParameter(key, null); } public String getParameter(String key, String defaultValue) { String val = params.get(key); return val != null ? val : defaultValue; } public String removeParameter(String key) { return params.remove(key); } }