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.solr; 017 018import java.util.Collection; 019import java.util.HashMap; 020import java.util.List; 021import java.util.Map; 022import java.util.Set; 023 024import org.apache.avalon.framework.component.Component; 025import org.apache.avalon.framework.logger.AbstractLogEnabled; 026import org.apache.avalon.framework.service.ServiceException; 027import org.apache.avalon.framework.service.ServiceManager; 028import org.apache.avalon.framework.service.Serviceable; 029 030import org.ametys.cms.content.indexing.solr.SolrFieldHelper; 031import org.ametys.cms.contenttype.ContentType; 032import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 033import org.ametys.cms.contenttype.ContentTypesHelper; 034import org.ametys.cms.contenttype.MetadataDefinition; 035import org.ametys.cms.contenttype.MetadataType; 036import org.ametys.cms.contenttype.RepeaterDefinition; 037import org.ametys.cms.contenttype.indexing.IndexingField; 038import org.ametys.cms.contenttype.indexing.IndexingModel; 039import org.ametys.cms.contenttype.indexing.MetadataIndexingField; 040import org.ametys.cms.search.SearchField; 041import org.ametys.cms.search.model.SearchCriterion; 042import org.ametys.cms.search.model.SystemProperty; 043import org.ametys.cms.search.model.SystemPropertyExtensionPoint; 044import org.ametys.cms.search.ui.model.SearchUIModelExtensionPoint; 045import org.ametys.core.ui.Callable; 046 047/** 048 * Helper for solr query editor. 049 */ 050public class SolrQueryHelper extends AbstractLogEnabled implements Component, Serviceable 051{ 052 053 /** The component role. */ 054 public static final String ROLE = SolrQueryHelper.class.getName(); 055 056 /** The content type extension point. */ 057 protected ContentTypeExtensionPoint _cTypeEP; 058 059 /** The content types helper. */ 060 protected ContentTypesHelper _contentTypesHelper; 061 062 /** The search model helper. */ 063 protected SearchUIModelExtensionPoint _searchModelManager; 064 065 /** The extension point for system properties */ 066 protected SystemPropertyExtensionPoint _systemPropertyEP; 067 068 @Override 069 public void service(ServiceManager serviceManager) throws ServiceException 070 { 071 _cTypeEP = (ContentTypeExtensionPoint) serviceManager.lookup(ContentTypeExtensionPoint.ROLE); 072 _contentTypesHelper = (ContentTypesHelper) serviceManager.lookup(ContentTypesHelper.ROLE); 073 _searchModelManager = (SearchUIModelExtensionPoint) serviceManager.lookup(SearchUIModelExtensionPoint.ROLE); 074 _systemPropertyEP = (SystemPropertyExtensionPoint) serviceManager.lookup(SystemPropertyExtensionPoint.ROLE); 075 } 076 077 /** 078 * Get all the content types and their indexing fields. 079 * @param language the query language (because string field names are language-dependent). 080 * @return the content types and their metadatas. 081 */ 082 @Callable 083 public Map<String, Object> getAllContentTypeIndexingFields(String language) 084 { 085 Map<String, Object> results = new HashMap<>(); 086 Map<String, Object> contentTypes = new HashMap<>(); 087 results.put("contentTypes", contentTypes); 088 089 for (String cTypeId : _cTypeEP.getExtensionsIds()) 090 { 091 ContentType contentType = _cTypeEP.getExtension(cTypeId); 092 093 _putContentType(contentTypes, contentType, language); 094 } 095 096 return results; 097 } 098 099 private void _putContentType(Map<String, Object> contentTypes, ContentType contentType, String language) 100 { 101 String cTypeId = contentType.getId(); 102 103 Map<String, Object> properties = new HashMap<>(); 104 contentTypes.put(cTypeId, properties); 105 106 properties.put("type", "contentType"); 107 properties.put("superTypes", contentType.getSupertypeIds()); 108 109 Map<String, Object> fields = new HashMap<>(); 110 properties.put("fields", fields); 111 112 IndexingModel indexingModel = contentType.getIndexingModel(); 113 for (IndexingField field : indexingModel.getFields()) 114 { 115 if (field instanceof MetadataIndexingField) 116 { 117 MetadataIndexingField metaField = (MetadataIndexingField) field; 118 MetadataDefinition metaDef = metaField.getMetadataDefinition(); 119 120 if (metaDef != null) 121 { 122 _putMetadataIndexingField(fields, field.getName(), metaDef, cTypeId, language); 123 } 124 } 125 else 126 { 127 _putIndexingField(fields, field, language); 128 } 129 } 130 131 for (Map<String, List<String>> references : indexingModel.getReferences().values()) 132 { 133 for (String reference : references.keySet()) 134 { 135 IndexingField field = indexingModel.getField(reference); 136 if (field instanceof MetadataIndexingField) 137 { 138 MetadataIndexingField metaField = (MetadataIndexingField) field; 139 MetadataDefinition metaDef = metaField.getMetadataDefinition(); 140 if (metaDef != null) 141 { 142 _putReferenceField(fields, reference, metaDef.getName(), metaDef.getReferenceContentType()); 143 } 144 } 145 } 146 } 147 } 148 149 private void _putIndexingField(Map<String, Object> metadata, IndexingField field, String language) 150 { 151 String name = field.getName(); 152 MetadataType type = field.getType(); 153 154 Map<String, Object> properties = new HashMap<>(); 155 metadata.put(name, properties); 156 157 properties.put("type", type.name().toLowerCase()); 158 properties.put("fl", SolrFieldHelper.getIndexingFieldName(type, name)); 159 160 if (type == MetadataType.STRING) 161 { 162 properties.put("ftFl", name + SolrFieldHelper.getFulltextFieldSuffix(language)); 163 properties.put("wcFl", name + SolrFieldHelper.getWildcardFieldSuffix()); 164 } 165 else if (type == MetadataType.RICH_TEXT) 166 { 167 properties.put("ftFl", name + SolrFieldHelper.getFulltextFieldSuffix(language)); 168 } 169 } 170 171 private void _putMetadataIndexingField(Map<String, Object> fields, String name, MetadataDefinition metaDef, String cTypeId, String language) 172 { 173 if (metaDef.getReferenceContentType().equals(cTypeId)) 174 { 175 MetadataType type = metaDef.getType(); 176 177 Map<String, Object> properties = new HashMap<>(); 178 179 switch (type) 180 { 181 case STRING: 182 case MULTILINGUAL_STRING: 183 case LONG: 184 case DOUBLE: 185 case DATE: 186 case DATETIME: 187 case BOOLEAN: 188 case CONTENT: 189 case SUB_CONTENT: 190 case USER: 191 case GEOCODE: 192 case RICH_TEXT: 193 case BINARY: 194 case FILE: 195 case REFERENCE: 196 fields.put(name, properties); 197 _putSimpleMetadata(properties, metaDef, language); 198 break; 199 case COMPOSITE: 200 fields.put(name, properties); 201 _putCompositeMetadata(properties, metaDef, cTypeId, language); 202 break; 203 default: 204 break; 205 } 206 } 207 } 208 209 private void _putCompositeMetadata(Map<String, Object> properties, MetadataDefinition metaDef, String cTypeId, String language) 210 { 211 String name = metaDef.getName(); 212 213 // type 214 properties.put("type", metaDef instanceof RepeaterDefinition ? "repeater" : "composite"); 215 216 // solr field 217// String fl = (metaDef instanceof RepeaterDefinition) ? SolrFieldHelper.getJoinFieldName(name) : name; 218 properties.put("fl", name); 219 220 if (metaDef instanceof RepeaterDefinition) 221 { 222 properties.put("joinFl", SolrFieldHelper.getJoinFieldName(name)); 223 properties.put("isDisplayable", true); 224 } 225 226 Map<String, Object> subFields = new HashMap<>(); 227 properties.put("fields", subFields); 228 229 for (String subMetaName : metaDef.getMetadataNames()) 230 { 231 MetadataDefinition subMetaDef = metaDef.getMetadataDefinition(subMetaName); 232 _putMetadataIndexingField(subFields, subMetaName, subMetaDef, cTypeId, language); 233 } 234 } 235 236 private void _putSimpleMetadata(Map<String, Object> properties, MetadataDefinition metaDef, String language) 237 { 238 String name = metaDef.getName(); 239 MetadataType type = metaDef.getType(); 240 241 properties.put("type", type.name().toLowerCase()); 242 // solr field 243// properties.put("fl", SolrFieldHelper.getIndexingFieldName(type, path)); 244 properties.put("fl", SolrFieldHelper.getIndexingFieldName(type, name, language)); 245 246 if (type == MetadataType.STRING && metaDef.getEnumerator() == null) 247 { 248// properties.put("ftFl", name + SolrFieldHelper.getStringFieldSuffix(language, true)); 249 properties.put("ftFl", name + SolrFieldHelper.getFulltextFieldSuffix(language)); 250 properties.put("wcFl", name + SolrFieldHelper.getWildcardFieldSuffix()); 251 } 252 else if (type == MetadataType.RICH_TEXT) 253 { 254 properties.put("ftFl", name + SolrFieldHelper.getFulltextFieldSuffix(language)); 255 } 256 else if (type == MetadataType.CONTENT || type == MetadataType.SUB_CONTENT) 257 { 258 properties.put("cType", metaDef.getContentType()); 259 properties.put("joinFl", SolrFieldHelper.getJoinFieldName(name)); 260 } 261 262 properties.put("isDisplayable", true); 263 properties.put("isFacetable", _isFacetable(metaDef)); 264 properties.put("isMultiple", metaDef.isMultiple()); 265 } 266 267 private boolean _isFacetable(MetadataDefinition metaDef) 268 { 269 return metaDef != null && SearchCriterion.isFacetable(metaDef.getType(), metaDef.getEnumerator() != null); 270 } 271 272 private void _putReferenceField(Map<String, Object> fields, String name, String refField, String refCTypeId) 273 { 274 Map<String, Object> properties = new HashMap<>(); 275 fields.put(name, properties); 276 277 properties.put("isReference", true); 278 properties.put("refField", refField); 279 properties.put("refCType", refCTypeId); 280 } 281 282 /** 283 * Get content types' common ancestors. 284 * @param contentTypes The content types. 285 * @return a Map with the common ancestors 286 */ 287 @Callable 288 public Map<String, Object> getCommonAncestors(Collection<String> contentTypes) 289 { 290 Set<String> commonAncestors = _contentTypesHelper.getCommonAncestors(contentTypes); 291 return Map.of("commonAncestors", commonAncestors); 292 } 293 294 /** 295 * Get the common fields. 296 * @param language the query language (because string field names are language-dependent). 297 * @return the common fields. 298 */ 299 @Callable 300 public Map<String, Object> getCommonFields(String language) 301 { 302 Map<String, Object> results = new HashMap<>(); 303 304 Map<String, Object> systemProps = new HashMap<>(); 305 results.put("fields", systemProps); 306 307 _putTitleMetadata(systemProps, language); 308 309 Set<String> extensionsIds = _systemPropertyEP.getExtensionsIds(); 310 for (String extensionId : extensionsIds) 311 { 312 SystemProperty property = _systemPropertyEP.getExtension(extensionId); 313 SearchField searchField = property.getSearchField(); 314 if (searchField != null) 315 { 316 Map<String, Object> properties = new HashMap<>(); 317 systemProps.put(extensionId, properties); 318 319 // type 320 properties.put("type", property.getType().getId().toLowerCase()); 321 322 // solr field 323 properties.put("fl", searchField.getName()); 324 325 // is displayable 326 properties.put("isDisplayable", _systemPropertyEP.isDisplayable(extensionId)); 327 328 // is facetable 329 properties.put("isFacetable", property.isFacetable()); 330 331 // is multiple 332 properties.put("isMultiple", property.isMultiple()); 333 } 334 } 335 336 return results; 337 } 338 339 private void _putTitleMetadata(Map<String, Object> systemProps, String language) 340 { 341 for (String cTypeId : _cTypeEP.getExtensionsIds()) 342 { 343 ContentType cType = _cTypeEP.getExtension(cTypeId); 344 if (cType.hasMetadataDefinition("title")) 345 { 346 MetadataDefinition titleDef = cType.getMetadataDefinition("title"); 347 348 Map<String, Object> properties = new HashMap<>(); 349 systemProps.put("title", properties); 350 351 _putSimpleMetadata(properties, titleDef, language); 352 break; 353 } 354 } 355 } 356 357// private SearchCriterion _getCriterion(Map<String, SearchUICriterion> criteria, String criterionId) 358// { 359// for (SearchCriterion criterion : criteria.values()) 360// { 361// if (criterion instanceof IndexingFieldSearchUICriterion && ((IndexingFieldSearchUICriterion) criterion).getFieldPath().equals(criterionId) 362// || criterion instanceof SystemSearchUICriterion && ((SystemSearchUICriterion) criterion).getSystemPropertyId().equals(criterionId) 363// || criterion.getId().equals(criterionId)) 364// { 365// return criterion; 366// } 367// } 368// 369// return null; 370// } 371}