001/*
002 *  Copyright 2010 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.plugins.tagcloud.generators;
017
018import java.io.IOException;
019import java.net.URLDecoder;
020import java.util.ArrayList;
021import java.util.Collection;
022import java.util.Collections;
023import java.util.List;
024
025import org.apache.cocoon.ProcessingException;
026import org.apache.commons.lang3.StringUtils;
027import org.apache.solr.client.solrj.SolrQuery;
028import org.apache.solr.client.solrj.response.QueryResponse;
029
030import org.ametys.cms.search.query.AndQuery;
031import org.ametys.cms.search.query.FullTextQuery;
032import org.ametys.cms.search.query.OrQuery;
033import org.ametys.cms.search.query.Query;
034import org.ametys.cms.search.query.Query.Operator;
035import org.ametys.plugins.repository.metadata.CompositeMetadata;
036import org.ametys.runtime.i18n.I18nizableText;
037import org.ametys.web.search.query.PageContentQuery;
038import org.ametys.web.search.query.SiteQuery;
039import org.ametys.web.search.query.SitemapQuery;
040
041/**
042 * Generates a tag cloud based upon words
043 */
044public class TagCloudOnWordsGenerator extends AbstractTagCloudGenerator
045{
046    
047    @Override
048    protected List<TagCloudItem> getTagCloudItems(String siteName, String lang, CompositeMetadata serviceParameters) throws IOException, ProcessingException
049    {
050        // Content types
051        String[] cTypes = serviceParameters.getStringArray("content-types");
052
053        // Pages
054        String[] pages = serviceParameters.getStringArray("search-by-pages");
055        
056        // Get key words to match. 
057        // TODO If empty, use top ranking terms from the solr server.
058        String[] keywords = serviceParameters.getString("keywords").split("[,;\n]");
059        
060        // Set of {@link TagCloudItem} sorted by occurrence
061        List<TagCloudItem> tagCloud = new ArrayList<>();
062        
063        int pos = 0;
064        for (String keyword : keywords)
065        {
066            String trimKeyword = StringUtils.trimToNull(keyword);
067            if (trimKeyword != null)
068            {
069                try
070                {
071                    Query queryObject = getQuery(siteName, lang, trimKeyword, cTypes, pages);
072                    
073                    SolrQuery query = build(queryObject);
074                    
075                    if (getLogger().isInfoEnabled())
076                    {
077                        getLogger().info("Solr query: " + URLDecoder.decode(query.toString(), "UTF-8"));
078                    }
079                    
080                    String collection = _solrClientProvider.getCollectionName();
081                    QueryResponse response = _solrClient.query(collection, query);
082                    
083                    int count = (int) response.getResults().getNumFound();
084                    if (count > 0)
085                    {
086                        tagCloud.add(new WordTagCloudItem(count, trimKeyword, pos));
087                        pos++;
088                    }
089                }
090                catch (Exception e)
091                {
092                    getLogger().error("Query on keyword '" + trimKeyword + "' failed. Unable to get number of occurrence", e);
093                    throw new ProcessingException("Query on keyword '" + trimKeyword + "' failed. Unable to get number of occurrence", e);
094                }
095            }
096        }
097        
098        Collections.sort(tagCloud, OCCURRENCE_COMPARATOR);
099        
100        return tagCloud;
101    }
102    
103    /**
104     * Get the query
105     * @param siteName The site name.
106     * @param language The current language.
107     * @param keyword The key word
108     * @param contentTypes The content types
109     * @param pages The pages
110     * @return the query object.
111     * @throws IllegalArgumentException If the search field is invalid
112     */
113    protected Query getQuery(String siteName, String language, String keyword, String[] contentTypes, String[] pages) throws IllegalArgumentException
114    {
115        List<Query> queries = new ArrayList<>();
116        
117        Query siteQuery = new SiteQuery(siteName);
118        Query langQuery = new SitemapQuery(language);
119        
120        queries.add(siteQuery);
121        queries.add(langQuery);
122        
123        _addContentTypeQuery(queries, contentTypes);
124        
125        _addPagesQuery(queries, pages);
126        
127        _addTextFieldQuery(queries, language, keyword);
128        
129        return new AndQuery(queries);
130    }
131    
132    // One or more of the words
133    private void _addTextFieldQuery(Collection<Query> queries, String language, String keyword) throws IllegalArgumentException
134    {
135        if (StringUtils.isNotBlank(keyword))
136        {
137            Query query = new FullTextQuery(keyword, language, Operator.SEARCH);
138            Query contentQuery = new PageContentQuery(query);
139            
140            queries.add(new OrQuery(query, contentQuery));
141        }
142    }
143    
144    private class WordTagCloudItem implements TagCloudItem
145    {
146        private int _occurrence;
147        private String _keyword;
148        private int _position;
149        
150        /**
151         * Constructor
152         * 
153         * @param occurrence the number of occurrence
154         * @param keyword the keyword
155         * @param position the original position
156         */
157        public WordTagCloudItem(int occurrence, String keyword, int position)
158        {
159            _occurrence = occurrence;
160            _keyword = keyword;
161            _position = position;
162        }
163        
164        @Override
165        public I18nizableText getWord()
166        {
167            return new I18nizableText(_keyword);
168        }
169        
170        @Override
171        public int getOccurrenceCount()
172        {
173            return _occurrence;
174        }
175        
176        @Override
177        public int getPosition()
178        {
179            return _position;
180        }
181    }
182
183}
184