001/* 002 * Copyright 2016 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.cms.search.query; 017 018import java.util.Arrays; 019import java.util.Collection; 020import java.util.stream.Collectors; 021 022import org.apache.commons.lang3.StringUtils; 023import org.apache.solr.client.solrj.util.ClientUtils; 024 025import org.ametys.cms.content.indexing.solr.SolrFieldHelper; 026import org.ametys.cms.search.query.join.JoinKey; 027import org.ametys.cms.search.solr.schema.SchemaHelper; 028import org.ametys.core.util.LambdaUtils; 029 030/** 031 * Represents a {@link Query} on a joined document. 032 */ 033public class JoinQuery implements Query 034{ 035 036 /** The field path. */ 037 protected Query _subQuery; 038 039 /** The join keys (paths and optional nested queries) */ 040 protected Collection<JoinKey> _joinKeys; 041 042 /** 043 * Build a join query. 044 * @param subQuery The sub query. 045 * @param joinPaths The field's join paths 046 */ 047 public JoinQuery(Query subQuery, String... joinPaths) 048 { 049 this(subQuery, Arrays.asList(joinPaths)); 050 } 051 052 /** 053 * Build a join query. 054 * @param subQuery The sub query. 055 * @param joinPaths The field's join paths 056 */ 057 public JoinQuery(Query subQuery, Collection<String> joinPaths) 058 { 059 this(joinPaths.stream().map(jp -> new JoinKey(jp, null)).collect(Collectors.toList())); 060 _subQuery = subQuery; 061 } 062 063 /** 064 * Build a join query. 065 * @param joinKeys The join paths and optional nested queries 066 */ 067 public JoinQuery(JoinKey... joinKeys) 068 { 069 this(Arrays.asList(joinKeys)); 070 } 071 072 /** 073 * Build a join query. 074 * @param joinKeys The join paths and optional nested queries 075 */ 076 public JoinQuery(Collection<JoinKey> joinKeys) 077 { 078 _joinKeys = joinKeys; 079 _checkValidPathNames(); 080 _checkJoinParams(); 081 } 082 083 private void _checkValidPathNames() 084 { 085 for (JoinKey joinKey : _joinKeys) 086 { 087 String path = joinKey.getKey(); 088 if (!SchemaHelper.isNameValid(path)) 089 { 090 throw new IllegalArgumentException("Invalid path name '" + path + "' in join."); 091 } 092 } 093 } 094 095 private void _checkJoinParams() 096 { 097 if (_subQuery == null && _joinKeys.isEmpty()) 098 { 099 throw new IllegalArgumentException("The join path is empty and there is no subquery in JoinQuery."); 100 } 101 } 102 103 @Override 104 public String build() throws QuerySyntaxException 105 { 106 StringBuilder queryString = new StringBuilder(); 107 108 queryString.append("{!ametys join=\""); 109 boolean first = true; 110 for (JoinKey joinKey : _joinKeys) 111 { 112 if (!first) 113 { 114 queryString.append("->"); 115 } 116 first = false; 117 String path = joinKey.getKey(); 118 queryString.append(SolrFieldHelper.getJoinFieldName(path)); 119 joinKey.getNestedQuery() 120 .map(LambdaUtils.wrap(this::_buildQuery)) 121 .filter(StringUtils::isNotBlank) 122 .ifPresent(nq -> queryString.append('[').append(nq).append(']')); 123 } 124 queryString.append('"'); 125 126 if (_subQuery != null) 127 { 128 String subQuery = _buildQuery(_subQuery); 129 queryString.append(" q=\"") 130 .append(subQuery) 131 .append('"'); 132 } 133 134 queryString.append('}'); 135 136 return queryString.toString(); 137 } 138 139 private String _buildQuery(Query query) throws QuerySyntaxException 140 { 141 return ClientUtils.escapeQueryChars(query.build()); 142 } 143 144 @Override 145 public int hashCode() 146 { 147 final int prime = 31; 148 int result = 1; 149 result = prime * result + ((_joinKeys == null) ? 0 : _joinKeys.hashCode()); 150 result = prime * result + ((_subQuery == null) ? 0 : _subQuery.hashCode()); 151 return result; 152 } 153 154 @Override 155 public boolean equals(Object obj) 156 { 157 if (this == obj) 158 { 159 return true; 160 } 161 if (obj == null) 162 { 163 return false; 164 } 165 if (getClass() != obj.getClass()) 166 { 167 return false; 168 } 169 JoinQuery other = (JoinQuery) obj; 170 if (_joinKeys == null) 171 { 172 if (other._joinKeys != null) 173 { 174 return false; 175 } 176 } 177 else if (!_joinKeys.equals(other._joinKeys)) 178 { 179 return false; 180 } 181 if (_subQuery == null) 182 { 183 if (other._subQuery != null) 184 { 185 return false; 186 } 187 } 188 else if (!_subQuery.equals(other._subQuery)) 189 { 190 return false; 191 } 192 return true; 193 } 194}