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 } 166 167 private void _putMetadataIndexingField(Map<String, Object> fields, String name, MetadataDefinition metaDef, String cTypeId, String language) 168 { 169 if (metaDef.getReferenceContentType().equals(cTypeId)) 170 { 171 MetadataType type = metaDef.getType(); 172 173 Map<String, Object> properties = new HashMap<>(); 174 175 switch (type) 176 { 177 case STRING: 178 case MULTILINGUAL_STRING: 179 case LONG: 180 case DOUBLE: 181 case DATE: 182 case DATETIME: 183 case BOOLEAN: 184 case CONTENT: 185 case SUB_CONTENT: 186 case USER: 187 case GEOCODE: 188 case RICH_TEXT: 189 case BINARY: 190 case FILE: 191 case REFERENCE: 192 fields.put(name, properties); 193 _putSimpleMetadata(properties, metaDef, language); 194 break; 195 case COMPOSITE: 196 fields.put(name, properties); 197 _putCompositeMetadata(properties, metaDef, cTypeId, language); 198 break; 199 default: 200 break; 201 } 202 } 203 } 204 205 private void _putCompositeMetadata(Map<String, Object> properties, MetadataDefinition metaDef, String cTypeId, String language) 206 { 207 String name = metaDef.getName(); 208 209 // type 210 properties.put("type", metaDef instanceof RepeaterDefinition ? "repeater" : "composite"); 211 212 // solr field 213// String fl = (metaDef instanceof RepeaterDefinition) ? SolrFieldHelper.getJoinFieldName(name) : name; 214 properties.put("fl", name); 215 216 if (metaDef instanceof RepeaterDefinition) 217 { 218 properties.put("joinFl", SolrFieldHelper.getJoinFieldName(name)); 219 properties.put("isDisplayable", true); 220 } 221 222 Map<String, Object> subFields = new HashMap<>(); 223 properties.put("fields", subFields); 224 225 for (String subMetaName : metaDef.getMetadataNames()) 226 { 227 MetadataDefinition subMetaDef = metaDef.getMetadataDefinition(subMetaName); 228 _putMetadataIndexingField(subFields, subMetaName, subMetaDef, cTypeId, language); 229 } 230 } 231 232 private void _putSimpleMetadata(Map<String, Object> properties, MetadataDefinition metaDef, String language) 233 { 234 String name = metaDef.getName(); 235 MetadataType type = metaDef.getType(); 236 237 properties.put("type", type.name().toLowerCase()); 238 // solr field 239// properties.put("fl", SolrFieldHelper.getIndexingFieldName(type, path)); 240 properties.put("fl", SolrFieldHelper.getIndexingFieldName(type, name, language)); 241 242 if (type == MetadataType.STRING && metaDef.getEnumerator() == null) 243 { 244// properties.put("ftFl", name + SolrFieldHelper.getStringFieldSuffix(language, true)); 245 properties.put("ftFl", name + SolrFieldHelper.getFulltextFieldSuffix(language)); 246 properties.put("wcFl", name + SolrFieldHelper.getWildcardFieldSuffix()); 247 } 248 else if (type == MetadataType.CONTENT || type == MetadataType.SUB_CONTENT) 249 { 250 properties.put("cType", metaDef.getContentType()); 251 properties.put("joinFl", SolrFieldHelper.getJoinFieldName(name)); 252 } 253 254 properties.put("isDisplayable", true); 255 properties.put("isFacetable", _isFacetable(metaDef)); 256 properties.put("isMultiple", metaDef.isMultiple()); 257 } 258 259 private boolean _isFacetable(MetadataDefinition metaDef) 260 { 261 return metaDef != null && SearchCriterion.isFacetable(metaDef.getType(), metaDef.getEnumerator() != null); 262 } 263 264 private void _putReferenceField(Map<String, Object> fields, String name, String refField, String refCTypeId) 265 { 266 Map<String, Object> properties = new HashMap<>(); 267 fields.put(name, properties); 268 269 properties.put("isReference", true); 270 properties.put("refField", refField); 271 properties.put("refCType", refCTypeId); 272 } 273 274 /** 275 * Get content types' common ancestors. 276 * @param contentTypes The content types. 277 * @return a Map with the common ancestors 278 */ 279 @Callable 280 public Map<String, Object> getCommonAncestors(Collection<String> contentTypes) 281 { 282 Set<String> commonAncestors = _contentTypesHelper.getCommonAncestors(contentTypes); 283 return Map.of("commonAncestors", commonAncestors); 284 } 285 286 /** 287 * Get the common fields. 288 * @param language the query language (because string field names are language-dependent). 289 * @return the common fields. 290 */ 291 @Callable 292 public Map<String, Object> getCommonFields(String language) 293 { 294 Map<String, Object> results = new HashMap<>(); 295 296 Map<String, Object> systemProps = new HashMap<>(); 297 results.put("fields", systemProps); 298 299 _putTitleMetadata(systemProps, language); 300 301 Set<String> extensionsIds = _systemPropertyEP.getExtensionsIds(); 302 for (String extensionId : extensionsIds) 303 { 304 SystemProperty property = _systemPropertyEP.getExtension(extensionId); 305 SearchField searchField = property.getSearchField(); 306 if (searchField != null) 307 { 308 Map<String, Object> properties = new HashMap<>(); 309 systemProps.put(extensionId, properties); 310 311 // type 312 properties.put("type", property.getType().name().toLowerCase()); 313 314 // solr field 315 properties.put("fl", searchField.getName()); 316 317 // is displayable 318 properties.put("isDisplayable", _systemPropertyEP.isDisplayable(extensionId)); 319 320 // is facetable 321 properties.put("isFacetable", property.isFacetable()); 322 323 // is multiple 324 properties.put("isMultiple", property.isMultiple()); 325 } 326 } 327 328 return results; 329 } 330 331 private void _putTitleMetadata(Map<String, Object> systemProps, String language) 332 { 333 for (String cTypeId : _cTypeEP.getExtensionsIds()) 334 { 335 ContentType cType = _cTypeEP.getExtension(cTypeId); 336 if (cType.hasMetadataDefinition("title")) 337 { 338 MetadataDefinition titleDef = cType.getMetadataDefinition("title"); 339 340 Map<String, Object> properties = new HashMap<>(); 341 systemProps.put("title", properties); 342 343 _putSimpleMetadata(properties, titleDef, language); 344 break; 345 } 346 } 347 } 348 349// private SearchCriterion _getCriterion(Map<String, SearchUICriterion> criteria, String criterionId) 350// { 351// for (SearchCriterion criterion : criteria.values()) 352// { 353// if (criterion instanceof IndexingFieldSearchUICriterion && ((IndexingFieldSearchUICriterion) criterion).getFieldPath().equals(criterionId) 354// || criterion instanceof SystemSearchUICriterion && ((SystemSearchUICriterion) criterion).getSystemPropertyId().equals(criterionId) 355// || criterion.getId().equals(criterionId)) 356// { 357// return criterion; 358// } 359// } 360// 361// return null; 362// } 363}