001/* 002 * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl-2.1.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * bdelbosc 016 */ 017 018package org.nuxeo.elasticsearch.query; 019 020import java.util.Calendar; 021import java.util.Collection; 022import java.util.List; 023 024import org.elasticsearch.index.query.QueryBuilder; 025import org.elasticsearch.index.query.QueryBuilders; 026import org.nuxeo.ecm.core.api.DocumentModel; 027import org.nuxeo.ecm.core.query.sql.NXQL; 028import org.nuxeo.ecm.core.schema.utils.DateParser; 029import org.nuxeo.ecm.platform.query.api.PredicateDefinition; 030import org.nuxeo.ecm.platform.query.api.PredicateFieldDefinition; 031import org.nuxeo.ecm.platform.query.api.WhereClauseDefinition; 032import org.nuxeo.ecm.platform.query.nxql.NXQLQueryBuilder; 033 034/** 035 * Elasticsearch query builder for Native Page provider. 036 */ 037public class PageProviderQueryBuilder { 038 039 private PageProviderQueryBuilder() { 040 } 041 042 /** 043 * Create a ES request from a PP pattern 044 */ 045 public static QueryBuilder makeQuery(final String pattern, final Object[] params, 046 final boolean quotePatternParameters, final boolean escapePatternParameters, final boolean useNativeQuery) { 047 String query = pattern; 048 if (params != null) { 049 for (Object param : params) { 050 query = query.replaceFirst("\\?", convertParam(param, quotePatternParameters)); 051 } 052 } 053 if (useNativeQuery) { 054 return QueryBuilders.queryStringQuery(query); 055 } else { 056 return NxqlQueryConverter.toESQueryBuilder(query); 057 } 058 } 059 060 /** 061 * Create a ES request from a PP whereClause 062 */ 063 public static QueryBuilder makeQuery(final DocumentModel model, final WhereClauseDefinition whereClause, 064 final Object[] params, final boolean useNativeQuery) { 065 assert (model != null); 066 assert (whereClause != null); 067 NxqlQueryConverter.ExpressionBuilder eb = new NxqlQueryConverter.ExpressionBuilder("AND"); 068 String fixedPart = whereClause.getFixedPart(); 069 if (params != null) { 070 for (Object param : params) { 071 fixedPart = fixedPart.replaceFirst("\\?", convertParam(param, true)); 072 } 073 if (useNativeQuery) { 074 // Fixed part handled as query_string 075 eb.add(QueryBuilders.queryStringQuery(fixedPart)); 076 } else { 077 eb.add(NxqlQueryConverter.toESQueryBuilder(fixedPart)); 078 } 079 } 080 // Process predicates 081 for (PredicateDefinition predicate : whereClause.getPredicates()) { 082 PredicateFieldDefinition[] fieldDef = predicate.getValues(); 083 Object[] values = new Object[fieldDef.length]; 084 for (int fidx = 0; fidx < fieldDef.length; fidx++) { 085 if (fieldDef[fidx].getXpath() != null) { 086 values[fidx] = model.getPropertyValue(fieldDef[fidx].getXpath()); 087 } else { 088 values[fidx] = model.getProperty(fieldDef[fidx].getSchema(), fieldDef[fidx].getName()); 089 } 090 } 091 if (!isNonNullParam(values)) { 092 // skip predicate where all values are null 093 continue; 094 } 095 Object value = values[0]; 096 if (values[0] instanceof Collection<?>) { 097 Collection<?> vals = (Collection<?>) values[0]; 098 values = vals.toArray(new Object[vals.size()]); 099 } else if (values[0] instanceof Object[]) { 100 values = (Object[]) values[0]; 101 } 102 String name = predicate.getParameter(); 103 String operator = predicate.getOperator().toUpperCase(); 104 if ("FULLTEXT".equals(operator) || "FULLTEXT ALL".equals(operator)) { 105 operator = "="; 106 if (!name.startsWith(NXQL.ECM_FULLTEXT)) { 107 name = NXQL.ECM_FULLTEXT + "." + name; 108 } 109 } 110 eb.add(NxqlQueryConverter.makeQueryFromSimpleExpression(operator, name, value, values, null, null)); 111 } 112 return eb.get(); 113 } 114 115 /** 116 * Convert a params for fixed part 117 */ 118 protected static String convertParam(final Object param, boolean quote) { 119 String ret; 120 if (param == null) { 121 ret = ""; 122 } else if (param instanceof List<?>) { 123 StringBuilder stringBuilder = new StringBuilder(""); 124 NXQLQueryBuilder.appendStringList(stringBuilder, (List<?>) param, quote, true); 125 ret = stringBuilder.toString(); 126 // quote is already taken in account 127 quote = false; 128 } else if (param instanceof Calendar) { 129 ret = DateParser.formatW3CDateTime(((Calendar) param).getTime()); 130 } else { 131 ret = param.toString(); 132 } 133 if (quote && param instanceof String) { 134 ret = "\"" + ret + "\""; 135 } 136 return ret; 137 } 138 139 @SuppressWarnings("rawtypes") 140 protected static boolean isNonNullParam(final Object[] val) { 141 if (val == null) { 142 return false; 143 } 144 for (Object v : val) { 145 if (v != null) { 146 if (v instanceof String) { 147 if (!((String) v).isEmpty()) { 148 return true; 149 } 150 } else if (v instanceof String[]) { 151 if (((String[]) v).length > 0) { 152 return true; 153 } 154 } else if (v instanceof Collection) { 155 if (!((Collection) v).isEmpty()) { 156 return true; 157 } 158 } else { 159 return true; 160 } 161 } 162 } 163 return false; 164 } 165 166}