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