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.HashMap; 019import java.util.Map; 020import java.util.Set; 021 022import org.apache.avalon.framework.component.Component; 023import org.apache.avalon.framework.logger.AbstractLogEnabled; 024import org.apache.avalon.framework.service.ServiceException; 025import org.apache.avalon.framework.service.ServiceManager; 026import org.apache.avalon.framework.service.Serviceable; 027import org.apache.commons.lang3.LocaleUtils; 028 029import org.ametys.cms.contenttype.ContentType; 030import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 031import org.ametys.cms.contenttype.ContentTypesHelper; 032import org.ametys.cms.data.type.indexing.IndexableElementType; 033import org.ametys.cms.model.ContentElementDefinition; 034import org.ametys.cms.repository.Content; 035import org.ametys.cms.search.model.SystemProperty; 036import org.ametys.cms.search.model.SystemPropertyExtensionPoint; 037import org.ametys.cms.search.systemprop.IndexationAwareSystemProperty; 038import org.ametys.cms.search.ui.model.SearchUIModelExtensionPoint; 039import org.ametys.core.ui.Callable; 040import org.ametys.plugins.repository.model.RepeaterDefinition; 041import org.ametys.runtime.model.ElementDefinition; 042import org.ametys.runtime.model.ModelItem; 043import org.ametys.runtime.model.ModelItemContainer; 044import org.ametys.runtime.model.type.DataContext; 045import org.ametys.runtime.model.type.ModelItemType; 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 model items. 079 * @param language the query language (because string field names are language-dependent). 080 * @return the content types and their model items. 081 */ 082 @Callable(rights = Callable.NO_CHECK_REQUIRED) // Content type definition are public 083 public Map<String, Object> getAllContentTypeModelItems(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 Map<String, Object> properties = _getContentTypeProperties(contentType, language); 093 contentTypes.put(cTypeId, properties); 094 } 095 096 return results; 097 } 098 099 private Map<String, Object> _getContentTypeProperties(ContentType contentType, String language) 100 { 101 Map<String, Object> properties = new HashMap<>(); 102 properties.put("type", "contentType"); 103 properties.put("superTypes", contentType.getSupertypeIds()); 104 105 Map<String, Object> fields = new HashMap<>(); 106 properties.put("fields", fields); 107 108 // Model items and properties 109 for (ModelItem modelItem : contentType.getModelItems()) 110 { 111 Map<String, Object> modelItemProperties = _getModelItemProperties(modelItem, language); 112 fields.put(modelItem.getName(), modelItemProperties); 113 } 114 115 return properties; 116 } 117 118 private Map<String, Object> _getModelItemProperties(ModelItem modelItem, String language) 119 { 120 Map<String, Object> properties = new HashMap<>(); 121 122 String name = modelItem.getName(); 123 ModelItemType type = modelItem.getType(); 124 properties.put("type", type.getId()); 125 126 if (modelItem instanceof ElementDefinition elementDefinition) 127 { 128 if (type instanceof IndexableElementType elementType) 129 { 130 DataContext context = DataContext.newInstance() 131 .withLocale(LocaleUtils.toLocale(language)) 132 .withModelItem(modelItem); 133 134 properties.put("fl", name + elementType.getIndexingFieldSuffix(context)); 135 elementType.getTextFieldSuffix(context) 136 .ifPresent(suffix -> properties.put("ftFl", name + suffix)); 137 elementType.getWildcardFieldSuffix(context) 138 .ifPresent(suffix -> properties.put("wcFl", name + suffix)); 139 140 if (elementDefinition instanceof ContentElementDefinition contentElementDefinition) 141 { 142 properties.put("cType", contentElementDefinition.getContentTypeId()); 143 properties.put("joinFl", name); 144 } 145 146 properties.put("isDisplayable", true); 147 properties.put("isFacetable", elementType.isFacetable(context)); 148 properties.put("isMultiple", elementDefinition.isMultiple()); 149 } 150 } 151 else if (modelItem instanceof ModelItemContainer modelItemContainer) 152 { 153 properties.put("fl", name); 154 155 if (modelItem instanceof RepeaterDefinition) 156 { 157 properties.put("joinFl", name); 158 properties.put("isDisplayable", true); 159 } 160 161 Map<String, Object> childrenProperties = new HashMap<>(); 162 properties.put("fields", childrenProperties); 163 for (ModelItem child : modelItemContainer.getModelItems()) 164 { 165 Map<String, Object> childProperties = _getModelItemProperties(child, language); 166 childrenProperties.put(child.getName(), childProperties); 167 } 168 } 169 170 return properties; 171 } 172 173 /** 174 * Get the common fields. 175 * @param language the query language (because string field names are language-dependent). 176 * @return the common fields. 177 */ 178 @Callable(rights = Callable.NO_CHECK_REQUIRED) // Content type definition are public 179 public Map<String, Object> getCommonFields(String language) 180 { 181 Map<String, Object> results = new HashMap<>(); 182 183 Map<String, Object> systemProps = new HashMap<>(); 184 results.put("fields", systemProps); 185 186 Map<String, Object> titleProperties = _getTitleProperties(language); 187 systemProps.put(Content.ATTRIBUTE_TITLE, titleProperties); 188 189 Set<String> extensionsIds = _systemPropertyEP.getExtensionsIds(); 190 for (String extensionId : extensionsIds) 191 { 192 SystemProperty property = _systemPropertyEP.getExtension(extensionId); 193 if (property instanceof IndexationAwareSystemProperty indexationAwareProperty && indexationAwareProperty.getSolrFieldName() != null) 194 { 195 String solrFieldName = indexationAwareProperty.getSolrFieldName(); 196 Map<String, Object> properties = new HashMap<>(); 197 systemProps.put(extensionId, properties); 198 199 // type 200 properties.put("type", property.getType().getId()); 201 202 // solr field 203 properties.put("fl", solrFieldName); 204 205 // is displayable 206 properties.put("isDisplayable", _systemPropertyEP.isDisplayable(extensionId)); 207 208 // is facetable 209 properties.put("isFacetable", indexationAwareProperty.isFacetable()); 210 211 // is multiple 212 properties.put("isMultiple", property.isMultiple()); 213 } 214 } 215 216 return results; 217 } 218 219 private Map<String, Object> _getTitleProperties(String language) 220 { 221 ModelItem titleDefinition = _contentTypesHelper.getTitleAttributeDefinition(); 222 return _getModelItemProperties(titleDefinition, language); 223 } 224}