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.Locale; 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.contenttype.ContentType; 031import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 032import org.ametys.cms.contenttype.ContentTypesHelper; 033import org.ametys.cms.data.type.indexing.IndexableElementType; 034import org.ametys.cms.model.ContentElementDefinition; 035import org.ametys.cms.repository.Content; 036import org.ametys.cms.search.SearchField; 037import org.ametys.cms.search.model.SystemProperty; 038import org.ametys.cms.search.model.SystemPropertyExtensionPoint; 039import org.ametys.cms.search.ui.model.SearchUIModelExtensionPoint; 040import org.ametys.core.ui.Callable; 041import org.ametys.plugins.repository.model.RepeaterDefinition; 042import org.ametys.runtime.model.ElementDefinition; 043import org.ametys.runtime.model.ModelItem; 044import org.ametys.runtime.model.ModelItemContainer; 045import org.ametys.runtime.model.type.DataContext; 046import org.ametys.runtime.model.type.ModelItemType; 047 048/** 049 * Helper for solr query editor. 050 */ 051public class SolrQueryHelper extends AbstractLogEnabled implements Component, Serviceable 052{ 053 054 /** The component role. */ 055 public static final String ROLE = SolrQueryHelper.class.getName(); 056 057 /** The content type extension point. */ 058 protected ContentTypeExtensionPoint _cTypeEP; 059 060 /** The content types helper. */ 061 protected ContentTypesHelper _contentTypesHelper; 062 063 /** The search model helper. */ 064 protected SearchUIModelExtensionPoint _searchModelManager; 065 066 /** The extension point for system properties */ 067 protected SystemPropertyExtensionPoint _systemPropertyEP; 068 069 @Override 070 public void service(ServiceManager serviceManager) throws ServiceException 071 { 072 _cTypeEP = (ContentTypeExtensionPoint) serviceManager.lookup(ContentTypeExtensionPoint.ROLE); 073 _contentTypesHelper = (ContentTypesHelper) serviceManager.lookup(ContentTypesHelper.ROLE); 074 _searchModelManager = (SearchUIModelExtensionPoint) serviceManager.lookup(SearchUIModelExtensionPoint.ROLE); 075 _systemPropertyEP = (SystemPropertyExtensionPoint) serviceManager.lookup(SystemPropertyExtensionPoint.ROLE); 076 } 077 078 /** 079 * Get all the content types and their indexing fields. 080 * @param language the query language (because string field names are language-dependent). 081 * @return the content types and their metadatas. 082 */ 083 @Callable 084 public Map<String, Object> getAllContentTypeIndexingFields(String language) 085 { 086 Map<String, Object> results = new HashMap<>(); 087 Map<String, Object> contentTypes = new HashMap<>(); 088 results.put("contentTypes", contentTypes); 089 090 for (String cTypeId : _cTypeEP.getExtensionsIds()) 091 { 092 ContentType contentType = _cTypeEP.getExtension(cTypeId); 093 Map<String, Object> properties = _getContentTypeProperties(contentType, language); 094 contentTypes.put(cTypeId, properties); 095 } 096 097 return results; 098 } 099 100 private Map<String, Object> _getContentTypeProperties(ContentType contentType, String language) 101 { 102 Map<String, Object> properties = new HashMap<>(); 103 properties.put("type", "contentType"); 104 properties.put("superTypes", contentType.getSupertypeIds()); 105 106 Map<String, Object> fields = new HashMap<>(); 107 properties.put("fields", fields); 108 109 // Model items and properties 110 for (ModelItem modelItem : contentType.getModelItems()) 111 { 112 Map<String, Object> modelItemProperties = _getModelItemProperties(modelItem, language); 113 fields.put(modelItem.getName(), modelItemProperties); 114 } 115 116 return properties; 117 } 118 119 private Map<String, Object> _getModelItemProperties(ModelItem modelItem, String language) 120 { 121 Map<String, Object> properties = new HashMap<>(); 122 123 String name = modelItem.getName(); 124 ModelItemType type = modelItem.getType(); 125 properties.put("type", type.getId()); 126 127 if (modelItem instanceof ElementDefinition elementDefinition) 128 { 129 if (type instanceof IndexableElementType elementType) 130 { 131 DataContext context = DataContext.newInstance() 132 .withLocale(new Locale(language)) 133 .withModelItem(modelItem); 134 135 properties.put("fl", name + elementType.getIndexingFieldSuffix(context)); 136 elementType.getTextFieldSuffix(context) 137 .ifPresent(suffix -> properties.put("ftFl", name + suffix)); 138 elementType.getWildcardFieldSuffix(context) 139 .ifPresent(suffix -> properties.put("wcFl", name + suffix)); 140 141 if (elementDefinition instanceof ContentElementDefinition contentElementDefinition) 142 { 143 properties.put("cType", contentElementDefinition.getContentTypeId()); 144 properties.put("joinFl", name); 145 } 146 147 properties.put("isDisplayable", true); 148 properties.put("isFacetable", elementType.isFacetable(context)); 149 properties.put("isMultiple", elementDefinition.isMultiple()); 150 } 151 } 152 else if (modelItem instanceof ModelItemContainer modelItemContainer) 153 { 154 properties.put("fl", name); 155 156 if (modelItem instanceof RepeaterDefinition) 157 { 158 properties.put("joinFl", name); 159 properties.put("isDisplayable", true); 160 } 161 162 Map<String, Object> childrenProperties = new HashMap<>(); 163 properties.put("fields", childrenProperties); 164 for (ModelItem child : modelItemContainer.getModelItems()) 165 { 166 Map<String, Object> childProperties = _getModelItemProperties(child, language); 167 childrenProperties.put(child.getName(), childProperties); 168 } 169 } 170 171 return properties; 172 } 173 174 /** 175 * Get content types' common ancestors. 176 * @param contentTypes The content types. 177 * @return a Map with the common ancestors 178 */ 179 @Callable 180 public Map<String, Object> getCommonAncestors(Collection<String> contentTypes) 181 { 182 Set<String> commonAncestors = _contentTypesHelper.getCommonAncestors(contentTypes); 183 return Map.of("commonAncestors", commonAncestors); 184 } 185 186 /** 187 * Get the common fields. 188 * @param language the query language (because string field names are language-dependent). 189 * @return the common fields. 190 */ 191 @Callable 192 public Map<String, Object> getCommonFields(String language) 193 { 194 Map<String, Object> results = new HashMap<>(); 195 196 Map<String, Object> systemProps = new HashMap<>(); 197 results.put("fields", systemProps); 198 199 Map<String, Object> titleProperties = _getTitleProperties(language); 200 systemProps.put(Content.ATTRIBUTE_TITLE, titleProperties); 201 202 Set<String> extensionsIds = _systemPropertyEP.getExtensionsIds(); 203 for (String extensionId : extensionsIds) 204 { 205 SystemProperty property = _systemPropertyEP.getExtension(extensionId); 206 SearchField searchField = property.getSearchField(); 207 if (searchField != null) 208 { 209 Map<String, Object> properties = new HashMap<>(); 210 systemProps.put(extensionId, properties); 211 212 // type 213 properties.put("type", property.getType().getId()); 214 215 // solr field 216 properties.put("fl", searchField.getName()); 217 218 // is displayable 219 properties.put("isDisplayable", _systemPropertyEP.isDisplayable(extensionId)); 220 221 // is facetable 222 properties.put("isFacetable", property.isFacetable()); 223 224 // is multiple 225 properties.put("isMultiple", property.isMultiple()); 226 } 227 } 228 229 return results; 230 } 231 232 private Map<String, Object> _getTitleProperties(String language) 233 { 234 ModelItem titleDefinition = _contentTypesHelper.getTitleAttributeDefinition(); 235 return _getModelItemProperties(titleDefinition, language); 236 } 237}