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.Map;
021import java.util.Set;
022
023import org.apache.avalon.framework.component.Component;
024import org.apache.avalon.framework.logger.AbstractLogEnabled;
025import org.apache.avalon.framework.service.ServiceException;
026import org.apache.avalon.framework.service.ServiceManager;
027import org.apache.avalon.framework.service.Serviceable;
028
029import org.ametys.cms.content.indexing.solr.SolrFieldHelper;
030import org.ametys.cms.contenttype.ContentType;
031import org.ametys.cms.contenttype.ContentTypeExtensionPoint;
032import org.ametys.cms.contenttype.ContentTypesHelper;
033import org.ametys.cms.contenttype.MetadataDefinition;
034import org.ametys.cms.contenttype.MetadataType;
035import org.ametys.cms.contenttype.RepeaterDefinition;
036import org.ametys.cms.contenttype.indexing.IndexingField;
037import org.ametys.cms.contenttype.indexing.IndexingModel;
038import org.ametys.cms.contenttype.indexing.MetadataIndexingField;
039import org.ametys.cms.search.SearchField;
040import org.ametys.cms.search.model.SystemProperty;
041import org.ametys.cms.search.model.SystemPropertyExtensionPoint;
042import org.ametys.core.ui.Callable;
043
044/**
045 * Helper for solr query editor.
046 */
047public class SolrQueryHelper extends AbstractLogEnabled implements Component, Serviceable
048{
049    
050    /** The component role. */
051    public static final String ROLE = SolrQueryHelper.class.getName();
052    
053    /** The content type extension point. */
054    protected ContentTypeExtensionPoint _cTypeEP;
055    
056    /** The content types helper. */
057    protected ContentTypesHelper _contentTypesHelper;
058    
059    /** The extension point for system properties */
060    protected SystemPropertyExtensionPoint _systemPropertyEP;   
061    
062    @Override
063    public void service(ServiceManager serviceManager) throws ServiceException
064    {
065        _cTypeEP = (ContentTypeExtensionPoint) serviceManager.lookup(ContentTypeExtensionPoint.ROLE);
066        _contentTypesHelper = (ContentTypesHelper) serviceManager.lookup(ContentTypesHelper.ROLE);
067        _systemPropertyEP = (SystemPropertyExtensionPoint) serviceManager.lookup(SystemPropertyExtensionPoint.ROLE);
068    }
069    
070    /**
071     * Get all the content types and their indexing fields.
072     * @param language the query language (because string field names are language-dependent).
073     * @return the content types and their metadatas.
074     */
075    @Callable
076    public Map<String, Object> getAllContentTypeIndexingFields(String language)
077    {
078        Map<String, Object> results = new HashMap<>();
079        Map<String, Object> contentTypes = new HashMap<>();
080        results.put("contentTypes", contentTypes);
081        
082        for (String cTypeId : _cTypeEP.getExtensionsIds())
083        {
084            ContentType contentType = _cTypeEP.getExtension(cTypeId);
085            
086            _putContentType(contentTypes, contentType, language);
087        }
088        
089        return results;
090    }
091    
092    private void _putContentType(Map<String, Object> contentTypes, ContentType contentType, String language)
093    {
094        String cTypeId = contentType.getId();
095        
096        Map<String, Object> properties = new HashMap<>();
097        contentTypes.put(cTypeId, properties);
098        
099        properties.put("type", "contentType");
100        properties.put("superTypes", contentType.getSupertypeIds());
101        
102        Map<String, Object> fields = new HashMap<>();
103        properties.put("fields", fields);
104        
105        IndexingModel indexingModel = contentType.getIndexingModel();
106        for (IndexingField field : indexingModel.getFields())
107        {
108            if (field instanceof MetadataIndexingField)
109            {
110                MetadataIndexingField metaField = (MetadataIndexingField) field;
111                MetadataDefinition metaDef = metaField.getMetadataDefinition();
112                
113                if (metaDef != null)
114                {
115                    _putMetadataIndexingField(fields, field.getName(), metaDef, cTypeId, language);
116                }
117            }
118            else
119            {
120                _putIndexingField(fields, field, language);
121            }
122        }
123    }
124    
125    private void _putIndexingField(Map<String, Object> metadata, IndexingField field, String language)
126    {
127        String name = field.getName();
128        MetadataType type = field.getType();
129        
130        Map<String, Object> properties = new HashMap<>();
131        metadata.put(name, properties);
132        
133        properties.put("type", type.name().toLowerCase());
134        properties.put("fl", SolrFieldHelper.getIndexingFieldName(type, name));
135        
136        if (type == MetadataType.STRING)
137        {
138            properties.put("ftFl", name + SolrFieldHelper.getFulltextFieldSuffix(language));
139            properties.put("wcFl", name + SolrFieldHelper.getWildcardFieldSuffix());
140        }
141    }
142    
143    private void _putMetadataIndexingField(Map<String, Object> fields, String name, MetadataDefinition metaDef, String cTypeId, String language)
144    {
145        if (metaDef.getReferenceContentType().equals(cTypeId))
146        {
147            MetadataType type = metaDef.getType();
148            
149            Map<String, Object> properties = new HashMap<>();
150            
151            switch (type)
152            {
153                case STRING:
154                case LONG:
155                case DOUBLE:
156                case DATE:
157                case DATETIME:
158                case BOOLEAN:
159                case CONTENT:
160                case SUB_CONTENT:
161                case USER:
162                case GEOCODE:
163                    fields.put(name, properties);
164                    _putSimpleMetadata(properties, metaDef, language);
165                    break;
166                case COMPOSITE:
167                    fields.put(name, properties);
168                    _putCompositeMetadata(properties, metaDef, cTypeId, language);
169                    break;
170                default:
171                    break;
172            }
173        }
174    }
175    
176    private void _putCompositeMetadata(Map<String, Object> properties, MetadataDefinition metaDef, String cTypeId, String language)
177    {
178        String name = metaDef.getName();
179        
180        // type
181        properties.put("type", metaDef instanceof RepeaterDefinition ? "repeater" : "composite");
182        
183        // solr field
184//        String fl = (metaDef instanceof RepeaterDefinition) ? SolrFieldHelper.getJoinFieldName(name) : name;
185        properties.put("fl", name);
186        
187        if (metaDef instanceof RepeaterDefinition)
188        {
189            properties.put("joinFl", SolrFieldHelper.getJoinFieldName(name));
190        }
191        
192        Map<String, Object> subFields = new HashMap<>();
193        properties.put("fields", subFields);
194        
195        for (String subMetaName : metaDef.getMetadataNames())
196        {
197            MetadataDefinition subMetaDef = metaDef.getMetadataDefinition(subMetaName);
198            _putMetadataIndexingField(subFields, subMetaName, subMetaDef, cTypeId, language);
199        }
200    }
201
202    private void _putSimpleMetadata(Map<String, Object> properties, MetadataDefinition metaDef, String language)
203    {
204        String name = metaDef.getName();
205        MetadataType type = metaDef.getType();
206        
207        properties.put("type", type.name().toLowerCase());
208        // solr field
209//        properties.put("fl", SolrFieldHelper.getIndexingFieldName(type, path));
210        properties.put("fl", SolrFieldHelper.getIndexingFieldName(type, name, language));
211        
212        if (type == MetadataType.STRING && metaDef.getEnumerator() == null)
213        {
214//            properties.put("ftFl", name + SolrFieldHelper.getStringFieldSuffix(language, true));
215            properties.put("ftFl", name + SolrFieldHelper.getFulltextFieldSuffix(language));
216            properties.put("wcFl", name + SolrFieldHelper.getWildcardFieldSuffix());
217        }
218        else if (type == MetadataType.CONTENT || type == MetadataType.SUB_CONTENT)
219        {
220            properties.put("cType", metaDef.getContentType());
221            properties.put("joinFl", SolrFieldHelper.getJoinFieldName(name));
222        }
223    }
224    
225    /**
226     * Get content types' common ancestor.
227     * @param contentTypes The content types.
228     * @return a Map with the common ancestor if found, an empty Map if not found.
229     */
230    @Callable
231    public Map<String, Object> getCommonAncestor(Collection<String> contentTypes)
232    {
233        Map<String, Object> results = new HashMap<>();
234        
235        String commonAncestor = _contentTypesHelper.getCommonAncestor(contentTypes);
236        if (commonAncestor != null)
237        {
238            results.put("commonAncestor", commonAncestor);
239        }
240        
241        return results;
242    }
243    
244    /**
245     * Get the common fields.
246     * @param language the query language (because string field names are language-dependent).
247     * @return the common fields.
248     */
249    @Callable
250    public Map<String, Object> getCommonFields(String language)
251    {
252        Map<String, Object> results = new HashMap<>();
253        
254        Map<String, Object> systemProps = new HashMap<>();
255        results.put("fields", systemProps);
256        
257        _putTitleMetadata(systemProps, language);
258        
259        Set<String> extensionsIds = _systemPropertyEP.getExtensionsIds();
260        for (String extensionId : extensionsIds)
261        {
262            SystemProperty property = _systemPropertyEP.getExtension(extensionId); 
263            SearchField searchField = property.getSearchField();
264            if (searchField != null)
265            {
266                Map<String, Object> properties = new HashMap<>();
267                systemProps.put(extensionId, properties);
268                
269                // type
270                properties.put("type", property.getType().name().toLowerCase());
271                
272                // solr field
273                properties.put("fl", searchField.getName());
274            }
275        }
276        
277        return results;
278    }
279    
280    private void _putTitleMetadata(Map<String, Object> systemProps, String language)
281    {
282        for (String cTypeId : _cTypeEP.getExtensionsIds())
283        {
284            ContentType cType = _cTypeEP.getExtension(cTypeId);
285            if (cType.hasMetadataDefinition("title"))
286            {
287                MetadataDefinition titleDef = cType.getMetadataDefinition("title");
288                
289                Map<String, Object> properties = new HashMap<>();
290                systemProps.put("title", properties);
291                
292                _putSimpleMetadata(properties, titleDef, language);
293                break;
294            }
295        }
296    }
297    
298}