package org.opencb.hpg.bigdata.core.lib; import org.apache.commons.lang3.StringUtils; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.regex.Pattern; /** * Created by joaquin on 8/19/16. */ public abstract class ParseQuery { protected static final Pattern REGION_PATTERN = Pattern.compile("[:-]"); protected static final Pattern COMPARISON_PATTERN = Pattern.compile("^(<=?|>=?|!=|!?=?~|==?)([^=<>]+.*)$"); protected Set<String> explodes; protected List<String> filters; protected StringBuilder sqlQueryString; public ParseQuery() { explodes = new LinkedHashSet<>(); filters = new ArrayList<>(); sqlQueryString = new StringBuilder(); } public abstract String parse(Query query, QueryOptions queryOptions, String viewName); protected void buildQueryString(String viewName, QueryOptions queryOptions) { if (queryOptions != null && StringUtils.isNotEmpty(queryOptions.getString(QueryOptions.INCLUDE))) { sqlQueryString.append("SELECT ").append(queryOptions.getString(QueryOptions.INCLUDE)).append(" "); } else { sqlQueryString.append("SELECT * "); } sqlQueryString.append("FROM ").append(viewName).append(" ").append(StringUtils.join(explodes.toArray(), " ")).append(" "); sqlQueryString.append("WHERE ").append(StringUtils.join(filters, " AND ")); System.err.println(sqlQueryString.toString()); } protected String processFilter(String key, String value, boolean isString, boolean isArray) { if (StringUtils.isEmpty(key) || StringUtils.isEmpty(value)) { throw new IllegalArgumentException("key or value are null or empty"); } boolean or = value.contains(","); boolean and = value.contains(";"); if (or && and) { throw new IllegalArgumentException("Command and semi-colon cannot be mixed: " + value); } String op1 = isString ? " = '" : ""; String op2 = isString ? "'" : ""; String openArraContains = isArray ? "array_contains(" : ""; String closeArrayContains = isArray ? ")" : ""; String[] values = value.split("[,;]"); StringBuilder filter = new StringBuilder(); if (values.length == 1) { filter.append(openArraContains).append(key).append(op1).append(value).append(op2).append(closeArrayContains); } else { String logicalComparator = or ? " OR " : " AND "; filter.append("("); filter.append(openArraContains).append(key).append(op1).append(values[0]).append(op2).append(closeArrayContains); for (int i = 1; i < values.length; i++) { filter.append(logicalComparator); filter.append(openArraContains).append(key).append(op1).append(values[i]).append(op2).append(closeArrayContains); } filter.append(")"); } return filter.toString(); } protected void processRegionQuery(String value, String chromLabel, String startLabel, String endLabel) { if (StringUtils.isEmpty(value)) { throw new IllegalArgumentException("value is null or empty"); } if (value.contains(";")) { throw new IllegalArgumentException("Semi-colon is not supported by region filter, only comma is allowed : " + value); } String[] regions = value.split("[,]"); StringBuilder filter = new StringBuilder(); if (regions.length == 1) { filter.append(processRegion(regions[0].trim(), chromLabel, startLabel, endLabel)); } else { filter.append("("); filter.append(processRegion(regions[0].trim(), chromLabel, startLabel, endLabel)); for (int i = 1; i < regions.length; i++) { filter.append(" OR "); filter.append(processRegion(regions[i].trim(), chromLabel, startLabel, endLabel)); } filter.append(")"); } filters.add(filter.toString()); } private String processRegion(String region, String chromLabel, String startLabel, String endLabel) { StringBuilder filter = new StringBuilder(); filter.append("("); if (region.contains(":")) { String[] fields = REGION_PATTERN.split(region, -1); if (fields.length == 3) { filter.append(chromLabel).append(" = '").append(fields[0]).append("'"); filter.append(" AND ").append(startLabel).append(" >= ").append(fields[1]); filter.append(" AND ").append(endLabel).append(" <= ").append(fields[2]); } else { if (fields.length == 2) { filter.append(chromLabel).append(" = '").append(fields[0]).append("'"); filter.append(" AND ").append(startLabel).append(" >= ").append(fields[1]); } } } else { filter.append(chromLabel).append(" = '").append(region).append("'"); } filter.append(")"); return filter.toString(); } public Set<String> getExplodes() { return explodes; } public void setExplodes(Set<String> explodes) { this.explodes = explodes; } }