View Javadoc
1   /*
2    * Copyright (C) 2003-2011 eXo Platform SAS.
3    *
4    * This program is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Affero General Public License
6    * as published by the Free Software Foundation; either version 3
7    * of the License, or (at your option) any later version.
8    *
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU General Public License
15   * along with this program; if not, see<http://www.gnu.org/licenses/>.
16   */
17  package org.exoplatform.services.wcm.search.base;
18  
19  import java.util.ArrayList;
20  import java.util.HashMap;
21  import java.util.HashSet;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.Set;
25  
26  import javax.jcr.Node;
27  import javax.jcr.NodeIterator;
28  import javax.jcr.Session;
29  import javax.jcr.query.Query;
30  import javax.jcr.query.QueryManager;
31  import javax.jcr.query.QueryResult;
32  import javax.jcr.query.Row;
33  import javax.jcr.query.RowIterator;
34  
35  import org.exoplatform.services.jcr.ext.common.SessionProvider;
36  import org.exoplatform.services.jcr.impl.core.query.QueryImpl;
37  import org.exoplatform.services.log.ExoLogger;
38  import org.exoplatform.services.log.Log;
39  import org.exoplatform.services.security.ConversationState;
40  import org.exoplatform.services.wcm.search.SiteSearchService;
41  import org.exoplatform.services.wcm.utils.WCMCoreUtils;
42  
43  /**
44   * Created by The eXo Platform SAS
45   * Author : eXoPlatform
46   *          exo@exoplatform.com
47   * Jun 17, 2011  
48   */
49  @SuppressWarnings("unchecked")
50  public class QueryResultPageList<E> extends AbstractPageList<E> {
51  
52    private static final Log LOG  = ExoLogger.getLogger(QueryResultPageList.class.getName());
53    
54    private static final String ORDER_BY = "ORDER BY";
55    
56    /** The query data */
57    private QueryData queryData_;
58    /** The buffer size */
59    private int bufferSize_;
60    
61    /** The nodes. */
62    protected List<E> buffer;
63    
64    private Map<E, Integer> dataSet;
65      
66    public QueryResultPageList(int pageSize, QueryData queryData, int total, int bufferSize,
67                               NodeSearchFilter filter, SearchDataCreator creator) {
68      super(pageSize);
69      queryData_ = queryData.clone();
70      offset_ = (int)queryData.getOffset();
71      bufferSize_ = bufferSize;
72      this.filter = filter;
73      this.searchDataCreator = creator;
74      this.setAvailablePage(total);
75      removeRedundantPages(Math.min(bufferSize_ / pageSize, 5));
76      dataSet = new HashMap<E, Integer>();
77      loadedAllData_ = false;
78    }
79    
80    public int getBufferSize() { return bufferSize_; }
81    public void setBufferSize(int bufferSize) { bufferSize_ = bufferSize;  }
82    
83    public int getOffset() { return offset_; }
84    public void setOffset(int offset) { offset_ = offset;  }
85    
86    public QueryData getQueryData() { return queryData_; }
87    public void setQueryData(QueryData queryData) { this.queryData_ = queryData; }
88    
89    /**
90     * Updates the page size.
91     *
92     * @param pageSize the new page size value
93     */
94    public void setPageSize(int pageSize)
95    {
96      super.setPageSize(pageSize);
97      offset_ = 0;
98    }
99  
100   @Override
101   public List getAll() throws Exception {
102     return null;
103   }
104 
105   @Override
106   protected void populateCurrentPage(int page) throws Exception {
107     if (buffer == null || buffer.size() == 0) {
108       queryDataForBuffer(page);
109     }
110     int firstBufferPage = offset_ / getPageSize() + 1;
111     int lastBufferPage = (offset_ + buffer.size() - 1) / getPageSize() + 1;
112     int bufferPage = bufferSize_ / getPageSize();
113         
114     int offsetPage = firstBufferPage;
115     if (page < firstBufferPage || page > lastBufferPage || buffer.size() == 0) {
116       if (page < firstBufferPage) {
117         offsetPage = Math.max(1, page - (bufferPage / 3 * 2));
118       } else if (page > lastBufferPage) {
119         offsetPage = page;
120       }
121       
122       offset_ = (offsetPage - 1) * getPageSize();
123       queryDataForBuffer(page);
124     }
125     
126     currentListPage_ = new ArrayList<E>();
127     for (int i = getFrom(); i < getTo(); i++) {
128       if (i - offset_ < buffer.size()) {
129         E data = buffer.get(i - offset_);
130         currentListPage_.add(data);
131       }
132     }
133     if (currentListPage_.size() < getPageSize()) {
134       loadedAllData_ = true;
135     }
136   }
137   
138   private void queryDataForBuffer(int queryPage) throws Exception {
139     buffer = new ArrayList<E>();
140     dataSet = new HashMap<E, Integer>();
141     SessionProvider sessionProvider = queryData_.isSystemSession() ? WCMCoreUtils.getSystemSessionProvider() :
142                                                                      WCMCoreUtils.getUserSessionProvider();
143     Session session = sessionProvider.getSession(queryData_.getWorkSpace(), WCMCoreUtils.getRepository());
144     QueryManager queryManager = session.getWorkspace().getQueryManager();
145     Query query = queryManager.createQuery(queryData_.getQueryStatement(), queryData_.getLanguage_());
146     int offset = offset_;
147     SiteSearchService siteSearchService = WCMCoreUtils.getService(SiteSearchService.class);
148     Map found = siteSearchService.getFoundNodes(ConversationState.getCurrent().getIdentity().getUserId(),
149                                                                 queryData_.getQueryStatement());
150     Map<Integer, Integer> drop = siteSearchService.getDropNodes(ConversationState.getCurrent().getIdentity().getUserId(),
151                                                                   queryData_.getQueryStatement());
152     for (int i = 0; i < queryPage; i++) {
153       if (drop.containsKey(i))
154         offset += drop.get(i);
155     }
156     ((QueryImpl)query).setOffset(offset);  
157     long prevSize = 0;
158     int bufSize = bufferSize_;
159     while (true) {
160       int position = offset_;
161       int page = position/getPageSize() + 1;
162       int prevPage = -1;
163       drop.put(page, 0);
164       ((QueryImpl)query).setLimit(bufSize);      
165       QueryResult queryResult = query.execute();
166       NodeIterator iter = queryResult.getNodes();
167       RowIterator rowIter = queryResult.getRows();
168       long size = iter.getSize();
169       int count = 0;
170       buffer.clear();
171       dataSet.clear();
172       
173       while (iter.hasNext() && count < bufferSize_) {
174         Node newNode = iter.nextNode();
175         Row newRow = rowIter.nextRow();
176         if (filter != null) {
177           newNode = filter.filterNodeToDisplay(newNode);
178         }
179         if (newNode != null && searchDataCreator != null) {
180           E data = searchDataCreator.createData(newNode, newRow, null);
181           if (data != null && !dataSet.containsKey(data) && (found == null || !found.containsKey(data) || ((Integer)found.get(data)) >= page)) {
182             buffer.add(data);
183             dataSet.put(data, page);
184             count ++;
185             position++;
186             page = (position-1)/getPageSize() + 1;
187             if (page != prevPage) {
188               prevPage = page;
189               drop.put(page, 0);
190             }
191             // increase page number for the last item
192             if (position % getPageSize() == 0) { page++; }
193           } else { if (drop.containsKey(page)) drop.put(page, drop.get(page) + 1); }
194         } else if (newNode == null) { if (drop.containsKey(page)) drop.put(page, drop.get(page) + 1); }
195       }
196       /* already query all data */
197       if (size == prevSize) {
198         loadedAllData_ = true;
199         break;
200       }
201       /* enough data to process*/
202       if (count == bufferSize_) break;
203       bufSize = 2 * bufSize;
204       prevSize = size;
205     }
206     for (Map.Entry<E, Integer> e : dataSet.entrySet())
207       found.put(e.getKey(), e.getValue());
208   }
209   
210   public void sortData() {
211     if (sortByField != null) {
212       String statement = queryData_.getQueryStatement().toUpperCase(); 
213       int orderByIndex = statement.lastIndexOf(ORDER_BY);
214       String[] orderStrings = orderByIndex >= 0 ? queryData_.getQueryStatement()
215                                                             .substring(orderByIndex
216                                                                 + ORDER_BY.length())
217                                                             .split(",") : new String[] {};
218       
219       StringBuffer newStatement = orderByIndex >= 0 ?
220         new StringBuffer(queryData_.getQueryStatement().substring(0, orderByIndex + ORDER_BY.length())) : 
221         new StringBuffer(queryData_.getQueryStatement());
222       
223       newStatement.append(" ").append(getSortByField(sortByField, queryData_.getLanguage_())).
224                    append(" ").append(getOrderForQuery(order, queryData_.getLanguage_()));
225       for(String orderString : orderStrings) {
226         if (!orderString.toUpperCase().contains(sortByField.toUpperCase())) {
227           newStatement.append(", ").append(orderString);
228         }
229       }
230       queryData_.setQueryStatement(newStatement.toString());
231       try {
232         buffer.clear();
233         populateCurrentPage(currentPage_);
234       } catch (Exception e) {
235         if (LOG.isWarnEnabled()) {
236           LOG.warn(e.getMessage());
237         }
238       }
239     }
240   }
241   
242   private String getSortByField(String sortField, String queryLanguage) {
243     return (Query.SQL.equals(queryLanguage)? sortField : "@" + sortField) +
244            ("jcr:score".equals(sortField.toLowerCase()) ? "()" : "");
245   }
246   
247   private String getOrderForQuery(String order, String queryLanguage) {
248     if (Query.SQL.equals(queryLanguage)) {
249       return order.toUpperCase().startsWith("A") ? "ASC" : "DESC";  
250     } else {
251       return order.toUpperCase().startsWith("A") ? "ascending" : "descending";
252     }
253   }
254 
255   @Override
256   public List<E> getPageWithOffsetCare(int page) throws Exception {
257     return getPage(offset_/getPageSize() + page);
258   }
259   
260   /**
261    * Returns the to index.
262    *
263    * @return the to index
264    */
265   @Override
266   public int getTo()
267   {
268      int to = currentPage_ * getPageSize();
269      if (to > available_ + offset_)
270         to = available_ + offset_;
271      return to;
272   }
273   
274   public boolean loadedAllData() {
275     return loadedAllData_;
276   }
277   
278 }