001/*
002 *  Copyright 2023 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.systemprop;
017
018import java.util.Map;
019import java.util.Optional;
020
021import org.apache.avalon.framework.configuration.Configuration;
022import org.apache.avalon.framework.configuration.ConfigurationException;
023import org.apache.avalon.framework.service.ServiceException;
024import org.apache.avalon.framework.service.ServiceManager;
025import org.apache.commons.lang3.StringUtils;
026import org.apache.solr.common.SolrInputDocument;
027
028import org.ametys.cms.contenttype.ContentTypesHelper;
029import org.ametys.cms.data.holder.impl.IndexableDataHolderHelper;
030import org.ametys.cms.data.type.indexing.IndexableDataContext;
031import org.ametys.cms.repository.Content;
032import org.ametys.cms.search.SearchField;
033import org.ametys.cms.search.model.SystemProperty;
034import org.ametys.cms.search.query.FullTextQuery;
035import org.ametys.cms.search.query.Query;
036import org.ametys.cms.search.query.Query.Operator;
037import org.ametys.runtime.model.View;
038import org.ametys.runtime.model.type.ModelItemTypeConstants;
039
040/**
041 * {@link SystemProperty} which represents the full textual content of a Content based on attributes of a view.
042 * If the view does not exist for content, nothing will be indexed.
043 */
044public class ViewBasedFullTextSystemProperty extends AbstractSystemProperty<String, Content>
045{
046    /** The contents types helper */
047    protected ContentTypesHelper _contentTypesHelper;
048    
049    /** The view name to fill the property */
050    protected String _viewName;
051    
052    @Override
053    public void service(ServiceManager manager) throws ServiceException
054    {
055        super.service(manager);
056        _contentTypesHelper = (ContentTypesHelper) manager.lookup(ContentTypesHelper.ROLE);
057    }
058    
059    @Override
060    public void configure(Configuration configuration) throws ConfigurationException
061    {
062        super.configure(configuration);
063        _viewName = configuration.getChild("view").getValue();
064        if (StringUtils.isBlank(_viewName))
065        {
066            throw new ConfigurationException("'view' configuration is missing for indexing property '" + this.getName() + "'");
067        }
068    }
069    
070    @Override
071    public void indexValue(SolrInputDocument document, Content content, IndexableDataContext context)
072    {
073        Optional<View> view = Optional.of(_viewName)
074                .map(v -> _contentTypesHelper.getView(v, content));
075        
076        if (view.isPresent())
077        {
078            IndexableDataContext clonedContext = IndexableDataContext.newInstance(context)
079                    .withIndexForFullTextField(true)
080                    .withFullTextFieldName(getName());
081            
082            IndexableDataHolderHelper.indexData(content, view.get(), document, document, StringUtils.EMPTY, clonedContext);
083        }
084    }
085    
086    @Override
087    public boolean isMultiple()
088    {
089        return false;
090    }
091    
092    @Override
093    public boolean isDisplayable()
094    {
095        return false;
096    }
097    
098    @Override
099    public boolean isSortable()
100    {
101        return false;
102    }
103    
104    @Override
105    public Query getQuery(Object value, Operator operator, String language, Map<String, Object> contextualParameters)
106    {
107        return new FullTextQuery((String) value, getName(), language, operator);
108    }
109    
110    @Override
111    public SearchField getSearchField()
112    {
113        // No value to index, not sortable: the field won't be used.
114        return null;
115    }
116    
117    @Override
118    public Object getValue(Content content)
119    {
120        // No real "value" to return here.
121        // The values are indexed separately, while walking the content attribute tree.
122        return null;
123    }
124
125    @Override
126    protected String _getTypeId()
127    {
128        return ModelItemTypeConstants.STRING_TYPE_ID;
129    }
130}