Many Thanks Pontus! :)
Miguel -----Original Message----- From: Pontus Strand [mailto:[EMAIL PROTECTED] Sent: terça-feira, 18 de Janeiro de 2005 13:17 To: 'Slide Users Mailing List' Subject: RE: Slide search performance issues Ok, it seems that zip-files aren't approved by our mailserver ... In the file AbstractRDBMSStore this was added: // TODO: Added method. Maybe a bad idea to expose this variable? public RDBMSAdapter getAdapter() { return adapter; } -------------------------------------------- In the file RDBMSExpressionFactory this method was modified: protected ComparableResourcesPool getRequestedResourcePool() { if (requestedResourcePool == null) { // TODO: Add comment here. // TODO: Add more pools here if need be ... if (_store.getAdapter() instanceof MySql41RDBMSAdapter) { requestedResourcePool = new MySql41ComparableResourcesPool(_store, _context, getQuery()); } else { requestedResourcePool = new RDBMSComparableResourcesPool(_store, _context, getQuery()); } } return requestedResourcePool; } ------------------------------------------------------ The file MySql41ComparableResourcesPool.java was created and looks like this: /* * $Header: /home/cvs/jakarta-slide/src/stores/org/apache/slide/store/impl/rdbms/MySql41 ComparableResourcesPool.java,v 1.10.2.4 2004/10/27 12:58:41 unico Exp $ * $Revision: 1.10.2.4 $ * $Date: 2004/10/27 12:58:41 $ * * ==================================================================== * * Copyright 1999-2004 The Apache Software Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.slide.store.impl.rdbms; import java.lang.reflect.Constructor; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.apache.slide.common.PropertyName; import org.apache.slide.common.RequestedProperties; import org.apache.slide.common.RequestedProperty; import org.apache.slide.common.RequestedPropertyImpl; import org.apache.slide.common.ServiceAccessException; import org.apache.slide.common.SlideException; import org.apache.slide.common.SlideRuntimeException; import org.apache.slide.content.NodeProperty; import org.apache.slide.search.BadQueryException; import org.apache.slide.search.PropertyProvider; import org.apache.slide.search.QueryScope; import org.apache.slide.search.SearchQuery; import org.apache.slide.search.SearchToken; import org.apache.slide.search.basic.ComparableResourceImpl; import org.apache.slide.search.basic.ComparableResourcesPool; import org.apache.slide.search.basic.IBasicQuery; import org.apache.slide.security.AccessDeniedException; import org.apache.slide.store.impl.rdbms.expression.RDBMSExpressionFactory; import org.apache.slide.store.impl.rdbms.expression.RDBMSQueryContext; import org.apache.slide.structure.ObjectNode; import org.apache.slide.util.logger.Logger; /** */ public class MySql41ComparableResourcesPool extends RDBMSComparableResourcesPool { private final AbstractRDBMSStore _store; private final RDBMSQueryContext _context; private final IBasicQuery _query; private final SearchToken _token; private final QueryScope _scope; private final Map _selectProperties; private final PropertyProvider _provider; private Set _pool; public MySql41ComparableResourcesPool(AbstractRDBMSStore store, RDBMSQueryContext context, IBasicQuery query) { super(store, context, query); _store = store; _context = context; _query = query; _token = _query.getSearchToken(); _scope = _query.getScope(); _selectProperties = new HashMap(); _provider = new RDBMSPropertyProvider(_query.getPropertyProvider(), _selectProperties); if (_query instanceof SearchQuery) { final RequestedProperties props = ((SearchQuery) _query).requestedProperties(); if (!props.isAllProp()) { final Iterator iter = props.getRequestedProperties(); while (iter.hasNext()) { final RequestedProperty property = (RequestedProperty) iter.next(); final String selectKey = property.getNamespace() + property.getName(); if (_context.selects().containsKey(selectKey)) { _selectProperties.put(property, new HashMap()); } } } } } public Iterator resourceIterator() { try { return getPool().iterator(); } catch (BadQueryException e) { throw new SlideRuntimeException(e.toString()); } } public Set getPool() throws BadQueryException { if (_pool == null) { try { // TODO: Fix comments to this if (_query.isLimitDefined()) { int limit = _query.getLimit(); int loopCount = 0; _pool = new HashSet(limit); while (_pool.size() < limit) { ObjectNode[] objects = retrieveObjects(loopCount * limit); for (int i = 0; (i < objects.length) && (_pool.size() < limit); i++) { try { _pool.add(new ComparableResourceImpl(objects[i], _token, _scope, _provider)); } catch (AccessDeniedException e) { // ignore: object is not visible } } // Don't continue loop if the number of returned hits is // below the limit!! if (objects.length < limit) { break; } } } else { super.getPool(); } } catch (ServiceAccessException e) { throw new BadQueryException(e); } catch (SlideException e) { throw new BadQueryException(e); } } return _pool; } public boolean partialResult() { return false; } public QueryScope getScope() { return _scope; } private ObjectNode[] retrieveObjects(int limitOffset) throws ServiceAccessException, BadQueryException { if (_store.getCurrentlyActiveTransactionalResource() == null) { Connection connection = null; try { connection = _store.getNewConnection(); return retrieveObjects(connection, limitOffset); } catch (SQLException e) { throw new ServiceAccessException(_store, e); } finally { if (connection != null) { try { connection.close(); } catch (SQLException e) { _store.getLogger().log(e, AbstractRDBMSStore.LOG_CHANNEL, Logger.WARNING); } } } } else { return retrieveObjects(_store.getCurrentConnection(), limitOffset); } } private ObjectNode[] retrieveObjects(Connection connection, int limitOffset) throws ServiceAccessException, BadQueryException { PreparedStatement statement = null; ResultSet result = null; ArrayList classNames = new ArrayList(); ArrayList uris = new ArrayList(); try { final String sql = compileSQL(limitOffset); if (_store.getLogger().isEnabled(Logger.INFO)) { _store.getLogger().log("executing: " + sql, AbstractRDBMSStore.LOG_CHANNEL, Logger.INFO); } statement = connection.prepareStatement(sql); result = statement.executeQuery(); while (result.next()) { final String uri = result.getString(1); final String className = result.getString(2); uris.add(uri); classNames.add(className); Iterator iter = _selectProperties.keySet().iterator(); while (iter.hasNext()) { final RequestedProperty requested = (RequestedProperty) iter.next(); final String name = requested.getName(); final String namespace = requested.getNamespace(); final String alias = RDBMSExpressionFactory.propertyToAlias(requested.getName()); final String value = result.getString(alias); final NodeProperty property = new NodeProperty(name, value, namespace); final Map properties = (Map) _selectProperties.get(requested); properties.put(uri, property); } } } catch (SQLException e) { throw new ServiceAccessException(_store, e); } finally { if (result != null) { try { result.close(); } catch (SQLException e) { _store.getLogger().log(e, AbstractRDBMSStore.LOG_CHANNEL, Logger.WARNING); } } if (statement != null) { try { statement.close(); } catch (SQLException e) { _store.getLogger().log(e, AbstractRDBMSStore.LOG_CHANNEL, Logger.WARNING); } } } int size = uris.size(); ObjectNode[] objects = new ObjectNode[size]; for (int i = 0; i < size; i++) { try { Class objclass = Class.forName((String) classNames.get(i)); Class argClasses[] = { String.class }; Object arguments[] = { uris.get(i) }; Constructor constructor = objclass.getConstructor(argClasses); objects[i] = (ObjectNode) constructor.newInstance(arguments); objects[i].setUri(objects[i].getUuri()); } catch (Exception e) { throw new ServiceAccessException(_store, e); } } return objects; } private String compileSQL(int limitOffset) throws BadQueryException { String uri = _token.getSlideContext().getSlidePath(_scope.getHref()); if (uri.endsWith("/")) { uri = uri.substring(0, uri.length()-1); } String query = "select " + compileSelect() + " from " + compileJoins() + " where " + compileScope(uri); final String criteria = compileCriteria(); query = (criteria != null && criteria.length() > 0) ? query + " AND " + criteria : query; query = query + " limit " + limitOffset + "," + _query.getLimit(); return query; } private String compileSelect() { String select = "u.URI_STRING, o.CLASS_NAME"; final Iterator iter = _selectProperties.keySet().iterator(); while (iter.hasNext()) { final RequestedProperty property = (RequestedProperty) iter.next(); final String selectKey = property.getNamespace() + property.getName(); String propSelect = (String) _context.selects().get(selectKey); if (propSelect != null) { select += ", " + propSelect; } } return select; } private String compileScope(String uri) { switch (_scope.getDepth()) { case QueryScope.DEPTH_0: { return " u.URI_STRING = '" + uri + "'"; } case QueryScope.DEPTH_1: { return " (u.URI_STRING = '" + uri + "'" + " OR (u.URI_STRING LIKE '" + uri + "/%'" + " AND u.URI_STRING NOT LIKE '" + uri + "/%/%'))"; } case QueryScope.DEPTH_INFINITY: default: { return " (u.URI_STRING = '" + uri + "'" + " OR u.URI_STRING LIKE '" + uri + "/%')"; } } } private String compileCriteria() { String result = null; if (_context.criteria().size() > 0) { result = ""; Iterator iter = _context.criteria().iterator(); while (iter.hasNext()) { result += iter.next(); } } return result; } private String compileJoins() { String joins = "((OBJECT o " + "inner join URI u on u.URI_ID = o.URI_ID) " + "inner join VERSION_HISTORY vh on vh.URI_ID = u.URI_ID) "; Iterator iter = _context.joins().iterator(); while (iter.hasNext()) { joins = "(" + joins + " " + iter.next() + ") "; } return joins; } private static class RDBMSPropertyProvider implements PropertyProvider { private final PropertyProvider _propertyProvider; private final Map _selectProperties; private RDBMSPropertyProvider(PropertyProvider propertyProvider, Map selectProperties) { _propertyProvider = propertyProvider; _selectProperties = selectProperties; } public boolean isSupportedProperty(String resourceUri, String propertyName, String propertyNamespace) throws SlideException { if (_selectProperties.containsKey(new RequestedPropertyImpl(propertyName, propertyNamespace))) { return true; } else if (_propertyProvider != null) { return _propertyProvider.isSupportedProperty(resourceUri, propertyName, propertyNamespace); } return false; } public Iterator getSupportedPropertiesNames(String resourceUri) throws SlideException { Iterator selected = _selectProperties.keySet().iterator(); Iterator provided = null; if (_propertyProvider != null) { provided = _propertyProvider.getSupportedPropertiesNames(resourceUri); } return new PropertyNamesIterator(selected, provided); } public NodeProperty getProperty(String resourceUri, String propertyName, String propertyNamespace) throws SlideException { Map properties = (Map) _selectProperties.get(new RequestedPropertyImpl(propertyName, propertyNamespace)); if (properties != null) { return (NodeProperty) properties.get(resourceUri); } else if (_propertyProvider != null) { return _propertyProvider.getProperty(resourceUri, propertyName, propertyNamespace); } return null; } public Iterator getSupportedProperties(String resourceUri) throws SlideException { Iterator selected = _selectProperties.values().iterator(); Iterator provided = null; if (_propertyProvider != null) { provided = _propertyProvider.getSupportedProperties(resourceUri); } return new NodePropertiesIterator(resourceUri, selected, provided); } private static class PropertyNamesIterator implements Iterator { private final Iterator _selectedProperties; private final Iterator _providedProperties; private PropertyNamesIterator(Iterator selectedProperties, Iterator providedProperties) { _selectedProperties = selectedProperties; if (providedProperties != null) { _providedProperties = providedProperties; } else { _providedProperties = Collections.EMPTY_LIST.iterator(); } } public void remove() { throw new UnsupportedOperationException(); } public boolean hasNext() { return _selectedProperties.hasNext() || _providedProperties.hasNext(); } public Object next() { if (_selectedProperties.hasNext()) { RequestedProperty property = (RequestedProperty) _selectedProperties.next(); return new PropertyName(property.getName(), property.getNamespace()); } return _providedProperties.next(); } } private static class NodePropertiesIterator implements Iterator { private String _resourceUri; private Iterator _selectedProperties; private Iterator _providedProperties; private NodePropertiesIterator(String resourceUri, Iterator selectedProperties, Iterator providedProperties) { _resourceUri = resourceUri; _selectedProperties = selectedProperties; if (providedProperties != null) { _providedProperties = providedProperties; } else { _providedProperties = Collections.EMPTY_LIST.iterator(); } } public void remove() { throw new UnsupportedOperationException(); } public boolean hasNext() { return _selectedProperties.hasNext() || _providedProperties.hasNext(); } public Object next() { if (_selectedProperties.hasNext()) { Map properties = (Map) _selectedProperties.next(); return (NodeProperty) properties.get(_resourceUri); } return _providedProperties.next(); } } } } ------------------------ That's it ... Hopefully this will work ... Best regards, Pontus > -----Original Message----- > From: Pontus Strand [mailto:[EMAIL PROTECTED] > Sent: Tuesday, January 18, 2005 2:06 PM > To: 'Slide Users Mailing List' > Subject: RE: Slide search performance issues > > > I have been asked to post my changes, please note that I > consider this a > quick fix or even a hack. > > The basic idea behind it is that the ComparableResourcesPool used is > specific to each database. In order to solve this the method > getRequestedResourcePool() in RDBMSExpressionFactory was > modified to check > which RDBMS-adapter is used. To achieve that I hade to modify > AbstractRDBMSStore to return the adapter, i.e. I added the method > getAdapter(). I'm not sure this is a good solution, hence my > designation of > this as a hack. > > Finally I added the class MySql41ComparableResourcesPool, that is an > extension of RDBMSComparableResourcesPool. The class is > basically a copy of > RDBMSComparableResourcesPool, but with a few small > differences. The method > getPool() check to see if a limit has been requested, if so > it tries to > retrieve objects matching the limit. Worth noting is that the > SQL-query can > return documents that the user doesn't have access to. So in > order to get > the right amount of documents the SQL-query is asked as many > times as needed > to get either the requested limit or until no more results > can be found. > > The other major method that was changed is compileSQL(), this > method was > modified to include the "limit"-clause. Also, the > retrieveObjects()-method > where modified to take the current starting offset as a parameter, a > parameter that is passed on to compileSQL(). > > I hope this desciption is good enough to understand the > changes made and the > logic behind those changes. Perhaps a seed has been planted > so that someone > can implement a more thought through solution. > > Best regards, > Pontus > > > -----Original Message----- > > From: Pontus Strand [mailto:[EMAIL PROTECTED] > > Sent: Tuesday, January 18, 2005 10:04 AM > > To: 'Slide Users Mailing List' > > Subject: RE: Slide search performance issues > > > > > > So I guess one wouldn't have to have an Exchange Server in order to > > implement those extensions? I'm not too happy about using > proprietary > > extensions to a standard, but sometimes you have to be a > > realist and accept > > things... > > > > Anyway, we have solved to problem by modifying the source to > > Slide. This is > > not a good way to solve the problem, as upgrades to newer > > versions of Slide > > will be difficult but it was necessary to get the > performance we need. > > Basically what we did was to add a MySQL-specific > > ComparableResourcesPool > > that adds the keyword "limit" to the select statement. This > > was a quick fix > > to a serious problem so it not thought through all the way > > yet so maybe we > > will do something else in the future. > > > > Regards, > > Pontus > > > > > -----Original Message----- > > > From: [EMAIL PROTECTED] [mailto:[EMAIL PROTECTED] > > > Sent: Monday, January 17, 2005 3:37 PM > > > To: 'Slide Users Mailing List' > > > Subject: AW: Slide search performance issues > > > > > > > > > My favourite approach concerning this issue would be to > > implement the > > > MS-Exchange extension that adds the ability to specify to > > > limit the result > > > set by specifying ranges of results. > > > This is described at the MS WebDAV pages as far as I > > > remember. But it is not > > > that easy to implement that on server side. But in fact would > > > lead a big > > > step towards Exchange compatibility... > > > I know that everybody hates MS to extend the standards, but > > > in the area of > > > WebDAV they've added some useful bits. So why not adopt it? > > > Some ranges could be mapped to sql commands by retrieving > > > only the first > > > rows, others could at least be filtered at java result set > > > level. Any ideas? > > > > > > Cheers, > > > Daniel > > > > > > > -----Ursprüngliche Nachricht----- > > > > Von: > > [EMAIL PROTECTED] > > > > > > > [mailto:[EMAIL PROTECTED] > > > pache.org] > > > > Im Auftrag von Pontus Strand > > > > Gesendet: Montag, 17. Januar 2005 08:22 > > > > An: 'Slide Users Mailing List' > > > > Betreff: RE: Slide search performance issues > > > > > > > > Ok, found the reason to why the query below didn't work, I > > > had failed to > > > > include a "group by"-clause. > > > > > > > > However, that doen't solve the problem. If I get lots of > > > hits when doing a > > > > search it still takes a long time to execute. What I would > > > like to do is > > > > to > > > > move the limititation from the Slide server to the > > > DB-server (where it > > > > really belong). As I understand it the keyword "limit" > > isn't part of > > > > SQL-standard (Orcale for one doen't implement it) so I > > > guess that this is > > > > difficult do in generic terms. This is, however, important > > > for us as we > > > > need > > > > the performance so which class/classes would I have to > > > override in order > > > > to > > > > modify the behaviour of the search method? Is this > > > recommended at all? Or > > > > should we try to limit the client so we can't do to > wide searches? > > > > > > > > /Pontus > > > > > > > > > -----Original Message----- > > > > > From: Pontus Strand [mailto:[EMAIL PROTECTED] > > > > > Sent: Friday, January 14, 2005 2:51 PM > > > > > To: 'Slide Users Mailing List' > > > > > Subject: RE: Slide search performance issues > > > > > > > > > > > > > > > A quick follow-up, when working with large number of files > > > > > the number of > > > > > hits when doing a search could be large. As I understand it, > > > > > it is possible > > > > > to limit the number of responses by using the DAV:limit XML > > > > > element from the > > > > > DASL specification. However, the specification also states > > > > > that the server > > > > > may disregard the requested limit. So my questions are: Does > > > > > Slide support > > > > > DAV:limit? And will it work when using the > > > > > "use-rdbms-expression-factory" > > > > > parameter? And, finally, am I using correct syntax in the > > > > > example below? > > > > > > > > > > <D:searchrequest xmlns:D =\"DAV:\"> > > > > > <D:basicsearch> > > > > > <D:select> > > > > > <D:allprop/> > > > > > </D:select> > > > > > <D:from> > > > > > <D:scope> > > > > > <D:href></D:href> > > > > > </D:scope> > > > > > </D:from> > > > > > <D:where> > > > > > <D:and> > > > > > <D:eq><D:prop><D:fileextension/></D:prop> > > > > > <D:literal>xml</D:literal></D:eq> > > > > > </D:and> > > > > > </D:where> > > > > > <D:limit> > > > > > <D:nresults> > > > > > 10 > > > > > </D:nresults> > > > > > </D:limit> > > > > > </D:basicsearch> > > > > > </D:searchrequest> > > > > > > > > > > The reason for my questions is that I can't get it to work > > > > > and I can't find > > > > > any references to this. Is there perhaps another way to limit > > > > > the number of > > > > > search hits? > > > > > > > > > > Regards, > > > > > Pontus > > > > > > > > > > > > > > > > --------------------------------------------------------------------- > > > > > To unsubscribe, e-mail: > > [EMAIL PROTECTED] > > > > > For additional commands, e-mail: > > > [EMAIL PROTECTED] > > > > > > > > > > > > > > > > > > > --------------------------------------------------------------------- > > > > To unsubscribe, e-mail: > [EMAIL PROTECTED] > > > > For additional commands, e-mail: > > [EMAIL PROTECTED] > > > > > > > > > > > > --------------------------------------------------------------------- > > > To unsubscribe, e-mail: [EMAIL PROTECTED] > > > For additional commands, e-mail: > [EMAIL PROTECTED] > > > > > > > > --------------------------------------------------------------------- > > To unsubscribe, e-mail: [EMAIL PROTECTED] > > For additional commands, e-mail: [EMAIL PROTECTED] > > > > > --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED] --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]