diff --git a/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/executor/CreateUploadedDataSetExecutor.java b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/executor/CreateUploadedDataSetExecutor.java
index ae944447b24b1800d8e6c30226871c88166192f5..20b81a77c8ae6f88d7be1e2d8e04f42af2091b56 100644
--- a/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/executor/CreateUploadedDataSetExecutor.java
+++ b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/executor/CreateUploadedDataSetExecutor.java
@@ -16,14 +16,12 @@
 
 package ch.ethz.sis.openbis.generic.server.dssapi.v3.executor;
 
-import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 
-import org.apache.log4j.Logger;
 import org.springframework.stereotype.Component;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.IApplicationServerApi;
@@ -46,8 +44,6 @@ import ch.ethz.sis.openbis.generic.dssapi.v3.dto.dataset.create.UploadedDataSetC
 import ch.ethz.sis.openbis.generic.server.asapi.v3.executor.OperationContext;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.utils.ExceptionUtils;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
-import ch.systemsx.cisd.common.logging.LogCategory;
-import ch.systemsx.cisd.common.logging.LogFactory;
 import ch.systemsx.cisd.etlserver.api.v1.PutDataSetService;
 import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService;
 import ch.systemsx.cisd.openbis.dss.generic.shared.ServiceProvider;
@@ -64,10 +60,6 @@ import ch.systemsx.cisd.openbis.dss.generic.shared.dto.DataSetInformation;
 public class CreateUploadedDataSetExecutor implements ICreateUploadedDataSetExecutor
 {
 
-    private static Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION, CreateUploadedDataSetExecutor.class);
-
-    private PutDataSetService putService;
-
     @Override
     public DataSetPermId create(String sessionToken, UploadedDataSetCreation creation)
     {
@@ -173,28 +165,6 @@ public class CreateUploadedDataSetExecutor implements ICreateUploadedDataSetExec
             }
         }
 
-        // upload id
-
-        if (creation.getUploadId() != null)
-        {
-            if (creation.getUploadId().contains("/"))
-            {
-                throw new UserFailureException("Upload id must not contain '/'");
-            }
-
-            File temporaryIncomingRoot = getPutService().getTemporaryIncomingRoot(typeCode);
-            File temporaryIncomingDir = new File(temporaryIncomingRoot, creation.getUploadId());
-
-            if (false == temporaryIncomingDir.exists())
-            {
-                throw new UserFailureException(
-                        "The folder for the upload id " + creation.getUploadId() + " could not be found. Have you uploaded the file(s) first?");
-            }
-        } else
-        {
-            throw new UserFailureException("Upload id cannot be null.");
-        }
-
         NewDataSetDTO newDataSet = new NewDataSetDTO(typeCode, owner, null, new ArrayList<FileInfoDssDTO>());
         newDataSet.setProperties(creation.getProperties());
         newDataSet.setParentDataSetCodes(parentCodes);
@@ -211,14 +181,8 @@ public class CreateUploadedDataSetExecutor implements ICreateUploadedDataSetExec
         return ServiceProvider.getOpenBISService();
     }
 
-    private synchronized PutDataSetService getPutService()
+    private PutDataSetService getPutService()
     {
-        if (putService == null)
-        {
-            putService = new PutDataSetService(ServiceProvider.getOpenBISService(), operationLog);
-            putService.setStoreDirectory(ServiceProvider.getConfigProvider().getStoreRoot());
-        }
-
-        return putService;
+        return (PutDataSetService) ServiceProvider.getDataStoreService().getPutDataSetService();
     }
 }
diff --git a/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/upload/StoreShareFileUploadServlet.java b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/upload/StoreShareFileUploadServlet.java
index e40df8eeb44a5fb311da1d059dedbd6e299243bb..242a2203b6a03f3325eb97d7234624d8090522db 100644
--- a/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/upload/StoreShareFileUploadServlet.java
+++ b/datastore_server/source/java/ch/ethz/sis/openbis/generic/server/dssapi/v3/upload/StoreShareFileUploadServlet.java
@@ -19,7 +19,6 @@ package ch.ethz.sis.openbis.generic.server.dssapi.v3.upload;
 import java.io.IOException;
 import java.io.InputStream;
 
-import javax.servlet.ServletConfig;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
@@ -61,17 +60,9 @@ public class StoreShareFileUploadServlet extends HttpServlet
 
     public static final String IGNORE_FILE_PATH_PARAM = "ignoreFilePath";
 
-    public static final String UPLOAD_ID_PARAM = "uploadID";
-
-    private PutDataSetService putService;
+    public static final String FOLDER_PATH_PARAM = "folderPath";
 
-    @Override
-    public final void init(final ServletConfig servletConfig) throws ServletException
-    {
-        super.init(servletConfig);
-        this.putService = new PutDataSetService(ServiceProvider.getOpenBISService(), operationLog);
-        putService.setStoreDirectory(ServiceProvider.getConfigProvider().getStoreRoot());
-    }
+    public static final String UPLOAD_ID_PARAM = "uploadID";
 
     @Override
     protected void doPost(HttpServletRequest request, HttpServletResponse response)
@@ -90,6 +81,8 @@ public class StoreShareFileUploadServlet extends HttpServlet
                 throw new UserFailureException("Please upload at least one file");
             }
 
+            PutDataSetService putService = (PutDataSetService) ServiceProvider.getDataStoreService().getPutDataSetService();
+
             while (iterator.hasNext())
             {
                 FileItemStream file = null;
@@ -109,8 +102,12 @@ public class StoreShareFileUploadServlet extends HttpServlet
                      */
                     String filePath = uploadRequest.isIgnoreFilePath() ? FilenameUtils.getName(file.getName()) : file.getName();
 
-                    putService.putFileToStoreShare(uploadRequest.getSessionId(), filePath, uploadRequest.getDataSetType(),
-                            uploadRequest.getUploadId(), stream);
+                    operationLog.info("Received file '" + filePath + "' for upload id '" + uploadRequest.getUploadId() + "' and data set type '"
+                            + uploadRequest.getDataSetType() + "'");
+
+                    putService.putFileToStoreShare(uploadRequest.getSessionId(), uploadRequest.getFolderPath(), filePath,
+                            uploadRequest.getDataSetType(), uploadRequest.getUploadId(), stream);
+
                 } finally
                 {
                     IOUtils.closeQuietly(stream);
@@ -161,6 +158,11 @@ public class StoreShareFileUploadServlet extends HttpServlet
             }
         }
 
+        public String getFolderPath()
+        {
+            return request.getParameter(FOLDER_PATH_PARAM);
+        }
+
         public FileItemIterator getFiles() throws FileUploadException, IOException
         {
             ServletFileUpload upload = new ServletFileUpload();
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/DataSetTypeToRegistratorMapper.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/DataSetTypeToRegistratorMapper.java
index 70c28293ea6292d024d8fc99f02eb1d4744cfa0c..6843a06f304fbd9b3fa0f67f39d86367514d9723 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/DataSetTypeToRegistratorMapper.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/DataSetTypeToRegistratorMapper.java
@@ -17,6 +17,8 @@
 package ch.systemsx.cisd.etlserver.api.v1;
 
 import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.Properties;
 
@@ -100,8 +102,7 @@ class DataSetTypeToRegistratorMapper
                             + dropboxName
                             + " = " + dropboxName);
             return defaultHandler;
-        }
-        else
+        } else
         {
             return plugin;
         }
@@ -117,6 +118,14 @@ class DataSetTypeToRegistratorMapper
         return (null == plugin) ? defaultHandler : plugin;
     }
 
+    public Collection<ITopLevelDataSetRegistrator> getRegistrators()
+    {
+        Collection<ITopLevelDataSetRegistrator> registrators = new ArrayList<ITopLevelDataSetRegistrator>();
+        registrators.add(defaultHandler);
+        registrators.addAll(handlerMap.values());
+        return registrators;
+    }
+
     public void initializeStoreRootDirectory(File storeDirectory)
     {
         initializeStoreRootDirectory(storeDirectory, defaultHandler);
diff --git a/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/PutDataSetService.java b/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/PutDataSetService.java
index f1e8fe136846c1aa27fd28d3c9e10ec557ccb478..8bd0b279faae8c11c174584a31200a3f803cb62c 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/PutDataSetService.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/etlserver/api/v1/PutDataSetService.java
@@ -21,6 +21,8 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Properties;
@@ -36,6 +38,7 @@ import ch.systemsx.cisd.base.exceptions.CheckedExceptionTunnel;
 import ch.systemsx.cisd.base.exceptions.IOExceptionUnchecked;
 import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
+import ch.systemsx.cisd.common.filesystem.QueueingPathRemoverService;
 import ch.systemsx.cisd.common.mail.IMailClient;
 import ch.systemsx.cisd.common.mail.MailClient;
 import ch.systemsx.cisd.etlserver.DataStrategyStore;
@@ -48,6 +51,7 @@ import ch.systemsx.cisd.openbis.common.io.ByteArrayBasedContentNode;
 import ch.systemsx.cisd.openbis.common.io.ConcatenatedContentInputStream;
 import ch.systemsx.cisd.openbis.dss.generic.shared.Constants;
 import ch.systemsx.cisd.openbis.dss.generic.shared.IEncapsulatedOpenBISService;
+import ch.systemsx.cisd.openbis.dss.generic.shared.IPutDataSetService;
 import ch.systemsx.cisd.openbis.dss.generic.shared.ServiceProvider;
 import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.FileInfoDssDTO;
 import ch.systemsx.cisd.openbis.dss.generic.shared.api.v1.NewDataSetDTO;
@@ -63,7 +67,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseInstance;
  * 
  * @author Chandrasekhar Ramakrishnan
  */
-public class PutDataSetService
+public class PutDataSetService implements IPutDataSetService
 {
 
     private static final String MULTIPLE_FILES_UPLOAD_DIR = "upload";
@@ -270,15 +274,48 @@ public class PutDataSetService
             doInitialization();
         }
 
-        String dataSetTypeOrNull = newDataSet.tryDataSetType();
-        ITopLevelDataSetRegistrator registrator = registratorMap.getRegistratorForType(dataSetTypeOrNull);
+        if (StringUtils.isBlank(sessionToken))
+        {
+            throw new UserFailureException("Session token cannot be null or empty");
+        }
+        if (sessionToken.contains("/"))
+        {
+            throw new UserFailureException("Session token must not contain '/'");
+        }
+        if (newDataSet == null)
+        {
+            throw new UserFailureException("New data set cannot be null");
+        }
+        if (StringUtils.isBlank(uploadId))
+        {
+            throw new UserFailureException("Upload id cannot be null or empty");
+        }
+        if (uploadId.contains("/"))
+        {
+            throw new UserFailureException("Upload id must not contain '/'");
+        }
+
+        ServiceProvider.getOpenBISService().checkSession(sessionToken);
+
+        String dataSetType = newDataSet.tryDataSetType();
+        ITopLevelDataSetRegistrator registrator = registratorMap.getRegistratorForType(dataSetType);
 
-        File uploadDir = new File(getTemporaryIncomingRoot(dataSetTypeOrNull), uploadId);
-        File multipleFilesUploadDir = new File(uploadDir, MULTIPLE_FILES_UPLOAD_DIR);
-        File[] uploadedFiles = multipleFilesUploadDir.listFiles();
+        File sessionUploadDir = new File(getTemporaryIncomingRoot(dataSetType), sessionToken);
+        File uploadIdDir = new File(sessionUploadDir, uploadId);
+        File multipleFilesUploadDir = new File(uploadIdDir, MULTIPLE_FILES_UPLOAD_DIR);
+
+        File[] uploadedFiles = null;
         File dataSet = null;
 
-        if (uploadedFiles != null && uploadedFiles.length == 1)
+        if (multipleFilesUploadDir.exists() && multipleFilesUploadDir.isDirectory())
+        {
+            uploadedFiles = multipleFilesUploadDir.listFiles();
+        }
+
+        if (uploadedFiles == null || uploadedFiles.length == 0)
+        {
+            throw new UserFailureException("No uploaded files found for upload id '" + uploadId + "'");
+        } else if (uploadedFiles.length == 1)
         {
             dataSet = uploadedFiles[0];
         } else
@@ -288,15 +325,16 @@ public class PutDataSetService
 
         if (registrator instanceof PutDataSetServerPluginHolder)
         {
-            return new PutDataSetExecutor(this, ((PutDataSetServerPluginHolder) registrator).getPlugin(), sessionToken, newDataSet, uploadDir,
+            return new PutDataSetExecutor(this, ((PutDataSetServerPluginHolder) registrator).getPlugin(), sessionToken, newDataSet, uploadIdDir,
                     dataSet).executeWithoutWriting();
         } else
         {
-            return new PutDataSetTopLevelDataSetHandler(this, registrator, sessionToken, newDataSet, uploadDir, dataSet).executeWithoutWriting();
+            return new PutDataSetTopLevelDataSetHandler(this, registrator, sessionToken, newDataSet, uploadIdDir, dataSet).executeWithoutWriting();
         }
     }
 
-    public void putFileToStoreShare(String sessionToken, String filePath, String dataSetType, String uploadId, InputStream inputStream)
+    public void putFileToStoreShare(String sessionToken, String folderPathOrNull, String filePath, String dataSetType, String uploadId,
+            InputStream inputStream)
     {
         if (false == isInitialized)
         {
@@ -312,10 +350,18 @@ public class PutDataSetService
             {
                 throw new UserFailureException("Session token cannot be null or empty");
             }
+            if (sessionToken.contains("/"))
+            {
+                throw new UserFailureException("Session token must not contain '/'");
+            }
             if (StringUtils.isBlank(filePath))
             {
                 throw new UserFailureException("File path cannot be null or empty");
             }
+            if (false == StringUtils.isBlank(folderPathOrNull) && folderPathOrNull.contains("../"))
+            {
+                throw new UserFailureException("Folder path must not contain '../'");
+            }
             if (filePath.contains("../"))
             {
                 throw new UserFailureException("File path must not contain '../'");
@@ -339,26 +385,30 @@ public class PutDataSetService
 
             ServiceProvider.getOpenBISService().checkSession(sessionToken);
 
-            File uploadDir = new File(getTemporaryIncomingRoot(dataSetType), uploadId);
-            if (false == uploadDir.exists())
+            File sessionUploadDir = new File(getTemporaryIncomingRoot(dataSetType), sessionToken);
+            File uploadIdDir = new File(sessionUploadDir, uploadId);
+            File multipleFilesUploadDir = new File(uploadIdDir, MULTIPLE_FILES_UPLOAD_DIR);
+
+            File filePathDir = null;
+            if (StringUtils.isBlank(folderPathOrNull))
             {
-                uploadDir.mkdir();
+                filePathDir = new File(multipleFilesUploadDir, FilenameUtils.getPath(filePath));
+            } else
+            {
+                filePathDir = new File(multipleFilesUploadDir, FilenameUtils.getPath(folderPathOrNull + "/" + filePath));
             }
 
-            File uploadSubDir = new File(uploadDir, MULTIPLE_FILES_UPLOAD_DIR);
-            if (false == uploadSubDir.exists())
+            if (false == filePathDir.exists())
             {
-                uploadSubDir.mkdir();
+                filePathDir.mkdirs();
             }
 
-            File filePathDir = new File(uploadSubDir, FilenameUtils.getPath(filePath));
-            filePathDir.mkdirs();
-
             file = new File(filePathDir, FilenameUtils.getName(filePath));
             outputStream = new FileOutputStream(file);
-
             IOUtils.copyLarge(inputStream, outputStream);
 
+            operationLog.info("File '" + filePath + "' with upload id: '" + uploadId + "' has been stored as '" + file.getCanonicalPath() + "'");
+
         } catch (IOException ioe)
         {
             IOUtils.closeQuietly(outputStream);
@@ -455,6 +505,22 @@ public class PutDataSetService
         return registratorMap.getRegistratorForType(dataSetTypeOrNull).getGlobalState();
     }
 
+    private Collection<TopLevelDataSetRegistratorGlobalState> getThreadGlobalStates()
+    {
+        Collection<TopLevelDataSetRegistratorGlobalState> states = new ArrayList<TopLevelDataSetRegistratorGlobalState>();
+        Collection<ITopLevelDataSetRegistrator> registrators = registratorMap.getRegistrators();
+
+        for (ITopLevelDataSetRegistrator registrator : registrators)
+        {
+            if (registrator != null && registrator.getGlobalState() != null)
+            {
+                states.add(registrator.getGlobalState());
+            }
+        }
+
+        return states;
+    }
+
     Logger getOperationLog()
     {
         return operationLog;
@@ -513,6 +579,16 @@ public class PutDataSetService
 
         TopLevelDataSetRegistratorGlobalState globalState =
                 getThreadGlobalState(dataSetTypeCodeOrNull);
+        return getTemporaryIncomingRoot(globalState);
+    }
+
+    private File getTemporaryIncomingRoot(TopLevelDataSetRegistratorGlobalState globalState)
+    {
+        if (false == isInitialized)
+        {
+            doInitialization();
+        }
+
         File storeRoot = globalState.getStoreRootDir();
         if (false == StringUtils.isBlank(globalState.getShareId()))
         {
@@ -530,6 +606,46 @@ public class PutDataSetService
         return storeRoot;
     }
 
+    public void cleanupSession(String sessionToken)
+    {
+        if (false == isInitialized)
+        {
+            doInitialization();
+        }
+
+        if (StringUtils.isBlank(sessionToken))
+        {
+            throw new IllegalArgumentException("Session token cannot be null or empty");
+        }
+        if (sessionToken.contains("/"))
+        {
+            throw new UserFailureException("Session token must not contain '/'");
+        }
+
+        Collection<TopLevelDataSetRegistratorGlobalState> states = getThreadGlobalStates();
+
+        for (TopLevelDataSetRegistratorGlobalState state : states)
+        {
+            File sessionUploadDir = null;
+
+            try
+            {
+                sessionUploadDir = new File(getTemporaryIncomingRoot(state), sessionToken);
+
+                if (sessionUploadDir.exists())
+                {
+                    operationLog.info("Cleaning up a user session upload folder '" + sessionUploadDir.getAbsolutePath() + "'");
+                    QueueingPathRemoverService.removeRecursively(sessionUploadDir);
+                }
+            } catch (Exception e)
+            {
+                operationLog.warn(
+                        "Could not clean up a user session upload folder '" + sessionUploadDir.getAbsolutePath() + "' together with the user session",
+                        e);
+            }
+        }
+    }
+
 }
 
 /**
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreService.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreService.java
index 15bfac52dd8cbd05ae3c5886e77f365123ff52b3..13697bc6c9a8e113896dc0ccf56a9cbc6167105c 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreService.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/server/DataStoreService.java
@@ -511,6 +511,8 @@ public class DataStoreService extends AbstractServiceWithLogger<IDataStoreServic
         {
             QueueingPathRemoverService.removeRecursively(sessionWorkspace);
         }
+
+        getPutDataSetService().cleanupSession(userSessionToken);
     }
 
     @Override
@@ -587,7 +589,8 @@ public class DataStoreService extends AbstractServiceWithLogger<IDataStoreServic
         return availableService;
     }
 
-    private PutDataSetService getPutDataSetService()
+    @Override
+    public PutDataSetService getPutDataSetService()
     {
         if (putService == null)
         {
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/IDataStoreServiceInternal.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/IDataStoreServiceInternal.java
index 8f88f2a3d3cbeb4ef5a2808a519473e5c95c49ff..4b186e7cd728867a720f4b6139ff76b09f921d2e 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/IDataStoreServiceInternal.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/IDataStoreServiceInternal.java
@@ -72,4 +72,9 @@ public interface IDataStoreServiceInternal extends IInitializable, IDataStoreSer
 
     public void scheduleTask(String taskKey, IProcessingPluginTask task, Map<String, String> parameterBindings,
             List<DatasetDescription> datasets, String userId, String userEmailOrNull, String userSessionToken);
+
+    /**
+     * Returns the put data set service.
+     */
+    IPutDataSetService getPutDataSetService();
 }
diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/IPutDataSetService.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/IPutDataSetService.java
new file mode 100644
index 0000000000000000000000000000000000000000..9fcb7e099a60b3e7e19c29e33d45194d7c17f68c
--- /dev/null
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/generic/shared/IPutDataSetService.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2018 ETH Zuerich, CISD
+ *
+ * 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 ch.systemsx.cisd.openbis.dss.generic.shared;
+
+/**
+ * @author pkupczyk
+ */
+public interface IPutDataSetService
+{
+
+}
diff --git a/datastore_server/sourceTest/java/ch/ethz/sis/openbis/generic/dss/systemtest/api/v3/CreateUploadedDataSetsTest.java b/datastore_server/sourceTest/java/ch/ethz/sis/openbis/generic/dss/systemtest/api/v3/CreateUploadedDataSetsTest.java
index 53f8ef2a2d9bc8f5479cfb95e161680c7994f55b..07eb6409ecb6923c36732bff7041ca785d5a3ce4 100644
--- a/datastore_server/sourceTest/java/ch/ethz/sis/openbis/generic/dss/systemtest/api/v3/CreateUploadedDataSetsTest.java
+++ b/datastore_server/sourceTest/java/ch/ethz/sis/openbis/generic/dss/systemtest/api/v3/CreateUploadedDataSetsTest.java
@@ -16,6 +16,8 @@
 
 package ch.ethz.sis.openbis.generic.dss.systemtest.api.v3;
 
+import java.io.File;
+import java.io.FilenameFilter;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
@@ -84,7 +86,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
     public void testUploadWithInvalidSession() throws Exception
     {
         ContentResponse response =
-                uploadFiles("admin-180211214633760xF769DD44CAFFAF7B50FBEADF00DBEE1F", UUID.randomUUID().toString(), "UNKNOWN", true,
+                uploadFiles("admin-180211214633760xF769DD44CAFFAF7B50FBEADF00DBEE1F", UUID.randomUUID().toString(), "UNKNOWN", true, null,
                         new FileToUpload());
         assertResponseError(response);
         assertTrue(response.getContentAsString(), response.getContentAsString().contains("user is not logged in"));
@@ -93,7 +95,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
     @Test
     public void testUploadWithoutSession() throws Exception
     {
-        ContentResponse response = uploadFiles(null, UUID.randomUUID().toString(), "UNKNOWN", true, new FileToUpload());
+        ContentResponse response = uploadFiles(null, UUID.randomUUID().toString(), "UNKNOWN", true, null, new FileToUpload());
         assertResponseError(response);
         assertTrue(response.getContentAsString(), response.getContentAsString().contains("Session token cannot be null or empty"));
     }
@@ -103,7 +105,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
     {
         String sessionToken = as.login(TEST_USER, PASSWORD);
 
-        ContentResponse response = uploadFiles(sessionToken, UUID.randomUUID().toString(), null, true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, UUID.randomUUID().toString(), null, true, null, new FileToUpload());
         assertResponseError(response);
         assertTrue(response.getContentAsString(), response.getContentAsString().contains("Data set type cannot be null or empty"));
     }
@@ -113,7 +115,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
     {
         String sessionToken = as.login(TEST_USER, PASSWORD);
 
-        ContentResponse response = uploadFiles(sessionToken, null, "UNKNOWN", true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, null, "UNKNOWN", true, null, new FileToUpload());
         assertResponseError(response);
         assertTrue(response.getContentAsString(), response.getContentAsString().contains("Upload id cannot be null or empty"));
     }
@@ -123,7 +125,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
     {
         String sessionToken = as.login(TEST_USER, PASSWORD);
 
-        ContentResponse response = uploadFiles(sessionToken, "iam/incorrect", "UNKNOWN", true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, "iam/incorrect", "UNKNOWN", true, null, new FileToUpload());
         assertResponseError(response);
         assertTrue(response.getContentAsString(), response.getContentAsString().contains("Upload id must not contain &apos;/&apos;"));
     }
@@ -133,7 +135,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
     {
         String sessionToken = as.login(TEST_USER, PASSWORD);
 
-        ContentResponse response = uploadFiles(sessionToken, UUID.randomUUID().toString(), "UNKNOWN", true);
+        ContentResponse response = uploadFiles(sessionToken, UUID.randomUUID().toString(), "UNKNOWN", true, null);
         assertResponseError(response);
         assertTrue(response.getContentAsString(), response.getContentAsString().contains("Please upload at least one file"));
     }
@@ -144,7 +146,8 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         String sessionToken = as.login(TEST_USER, PASSWORD);
 
         ContentResponse response =
-                uploadFiles(sessionToken, UUID.randomUUID().toString(), "UNKNOWN", false, new FileToUpload("name", "iam/../incorrect", "content"));
+                uploadFiles(sessionToken, UUID.randomUUID().toString(), "UNKNOWN", false, null,
+                        new FileToUpload("name", "iam/../incorrect", "content"));
         assertResponseError(response);
         assertTrue(response.getContentAsString(), response.getContentAsString().contains("File path must not contain &apos;../&apos;"));
     }
@@ -155,11 +158,68 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         String sessionToken = as.login(TEST_USER, PASSWORD);
 
         ContentResponse response =
-                uploadFiles(sessionToken, UUID.randomUUID().toString(), "UNKNOWN", false, new FileToUpload("name", null, "content"));
+                uploadFiles(sessionToken, UUID.randomUUID().toString(), "UNKNOWN", false, null, new FileToUpload("name", null, "content"));
         assertResponseError(response);
         assertTrue(response.getContentAsString(), response.getContentAsString().contains("File path cannot be null or empty"));
     }
 
+    @Test
+    public void testUploadWithFolderPathWithFolderUp() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        ContentResponse response =
+                uploadFiles(sessionToken, UUID.randomUUID().toString(), "UNKNOWN", false, "iam/../incorrect", new FileToUpload());
+        assertResponseError(response);
+        assertTrue(response.getContentAsString(), response.getContentAsString().contains("Folder path must not contain &apos;../&apos;"));
+    }
+
+    @Test
+    public void testUploadWithFilesCleanedAfterLogout() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        String uploadId = UUID.randomUUID().toString();
+        String dataSetType = "UNKNOWN";
+
+        FileToUpload file = new FileToUpload("file", "test.txt", "test content");
+
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, false, null, file);
+        assertResponseOK(response);
+
+        FilenameFilter sessionUploadDirFilter = new FilenameFilter()
+            {
+                @Override
+                public boolean accept(File dir, String name)
+                {
+                    return sessionToken.equals(name);
+                }
+            };
+
+        File rpcIncomingDir = new File(store, "1/rpc-incoming");
+
+        File[] listFiles = rpcIncomingDir.listFiles(sessionUploadDirFilter);
+        assertEquals(1, listFiles.length);
+
+        as.logout(sessionToken);
+
+        // clean up of the session and the upload folder is done asynchronously therefore we have to wait
+
+        long timeoutMillis = System.currentTimeMillis() + 5000;
+        while (System.currentTimeMillis() < timeoutMillis)
+        {
+            listFiles = rpcIncomingDir.listFiles(sessionUploadDirFilter);
+            if (listFiles.length == 0)
+            {
+                return;
+            } else
+            {
+                Thread.sleep(100);
+            }
+        }
+        fail("Session upload folder hasn't been removed after the logout");
+    }
+
     @Test
     public void testCreateWithInvalidSession() throws Exception
     {
@@ -185,7 +245,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setSampleId(new SampleIdentifier(sampleIdentifier));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, null, new FileToUpload());
         assertResponseOK(response);
 
         try
@@ -212,7 +272,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setSampleId(new SampleIdentifier(sampleIdentifier));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, null, new FileToUpload());
         assertResponseOK(response);
 
         try
@@ -237,7 +297,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setTypeId(new EntityTypePermId(dataSetType));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, null, new FileToUpload());
         assertResponseOK(response);
 
         try
@@ -264,7 +324,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setSampleId(new SampleIdentifier(sampleIdentifier));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, null, new FileToUpload());
         assertResponseOK(response);
 
         try
@@ -291,7 +351,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setSampleId(new SampleIdentifier(sampleIdentifier));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, null, new FileToUpload());
         assertResponseOK(response);
 
         DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
@@ -314,7 +374,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setSampleId(new SamplePermId(samplePermId));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, null, new FileToUpload());
         assertResponseOK(response);
 
         DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
@@ -337,7 +397,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, null, new FileToUpload());
         assertResponseOK(response);
 
         try
@@ -365,7 +425,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, null, new FileToUpload());
         assertResponseOK(response);
 
         DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
@@ -388,7 +448,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setExperimentId(new ExperimentPermId(experimentPermId));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, null, new FileToUpload());
         assertResponseOK(response);
 
         DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
@@ -415,7 +475,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setUploadId(uploadId);
         creation.setProperties(properties);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, null, new FileToUpload());
         assertResponseOK(response);
 
         DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
@@ -441,7 +501,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setParentIds(Arrays.asList(parentId));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, new FileToUpload());
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, null, new FileToUpload());
         assertResponseOK(response);
 
         DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
@@ -517,9 +577,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
             fail();
         } catch (UserFailureException e)
         {
-            assertTrue(e.getMessage(),
-                    e.getMessage()
-                            .contains("The folder for the upload id " + uploadId + " could not be found. Have you uploaded the file(s) first?"));
+            assertTrue(e.getMessage(), e.getMessage().contains("No uploaded files found for upload id '" + uploadId + "'"));
         }
     }
 
@@ -539,7 +597,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, false, file);
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, false, null, file);
         assertResponseOK(response);
 
         DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
@@ -574,7 +632,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, false, file1, file2, file3, file4);
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, false, null, file1, file2, file3, file4);
         assertResponseOK(response);
 
         DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
@@ -612,7 +670,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, null, file1, file2);
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, null, null, file1, file2);
         assertResponseOK(response);
 
         DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
@@ -647,7 +705,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, false, file1, file2);
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, false, null, file1, file2);
         assertResponseOK(response);
 
         DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
@@ -666,6 +724,100 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         assertFile(files.get(5), "original/upload/test1.txt", file1.content);
     }
 
+    @Test
+    public void testCreateWithOneFolderPath() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        String uploadId = UUID.randomUUID().toString();
+        String dataSetType = "UNKNOWN";
+        String experimentIdentifier = "/CISD/NEMO/EXP1";
+
+        FileToUpload file1 = new FileToUpload("file1", "path/to/ignore/test1.txt", "test1 content");
+
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, "folderPath", file1);
+        assertResponseOK(response);
+
+        FileToUpload file2 = new FileToUpload("file2", "filePath/test2.txt", "test2 content");
+
+        response = uploadFiles(sessionToken, uploadId, dataSetType, false, "/folderPath", file2);
+        assertResponseOK(response);
+
+        FileToUpload file3 = new FileToUpload("file3", "/filePath/test3.txt", "test3 content");
+
+        response = uploadFiles(sessionToken, uploadId, dataSetType, false, "/folderPath/", file3);
+        assertResponseOK(response);
+
+        UploadedDataSetCreation creation = new UploadedDataSetCreation();
+        creation.setTypeId(new EntityTypePermId(dataSetType));
+        creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
+        creation.setUploadId(uploadId);
+
+        DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
+
+        assertEquals(dataSetType, dataSet.getType().getCode());
+        assertEquals(experimentIdentifier, dataSet.getExperiment().getIdentifier().getIdentifier());
+
+        List<DownloadedFile> files = downloadFiles(sessionToken, dataSet.getPermId());
+        assertEquals(7, files.size());
+
+        assertDirectory(files.get(0), "");
+        assertDirectory(files.get(1), "original");
+        assertDirectory(files.get(2), "original/folderPath");
+        assertDirectory(files.get(3), "original/folderPath/filePath");
+        assertFile(files.get(4), "original/folderPath/filePath/test2.txt", file2.content);
+        assertFile(files.get(5), "original/folderPath/filePath/test3.txt", file3.content);
+        assertFile(files.get(6), "original/folderPath/test1.txt", file1.content);
+    }
+
+    @Test
+    public void testCreateWithMultipleFolderPaths() throws Exception
+    {
+        String sessionToken = as.login(TEST_USER, PASSWORD);
+
+        String uploadId = UUID.randomUUID().toString();
+        String dataSetType = "UNKNOWN";
+        String experimentIdentifier = "/CISD/NEMO/EXP1";
+
+        FileToUpload file1 = new FileToUpload("file1", "path/to/ignore/test1.txt", "test1 content");
+
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, "folderPathA", file1);
+        assertResponseOK(response);
+
+        FileToUpload file2 = new FileToUpload("file2", "filePath/test2.txt", "test2 content");
+
+        response = uploadFiles(sessionToken, uploadId, dataSetType, false, "/folderPathB", file2);
+        assertResponseOK(response);
+
+        FileToUpload file3 = new FileToUpload("file3", "/filePath/test3.txt", "test3 content");
+
+        response = uploadFiles(sessionToken, uploadId, dataSetType, false, "/folderPathB/", file3);
+        assertResponseOK(response);
+
+        UploadedDataSetCreation creation = new UploadedDataSetCreation();
+        creation.setTypeId(new EntityTypePermId(dataSetType));
+        creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
+        creation.setUploadId(uploadId);
+
+        DataSet dataSet = getCreateAndGetDataSet(sessionToken, creation);
+
+        assertEquals(dataSetType, dataSet.getType().getCode());
+        assertEquals(experimentIdentifier, dataSet.getExperiment().getIdentifier().getIdentifier());
+
+        List<DownloadedFile> files = downloadFiles(sessionToken, dataSet.getPermId());
+        assertEquals(9, files.size());
+
+        assertDirectory(files.get(0), "");
+        assertDirectory(files.get(1), "original");
+        assertDirectory(files.get(2), "original/upload");
+        assertDirectory(files.get(3), "original/upload/folderPathA");
+        assertFile(files.get(4), "original/upload/folderPathA/test1.txt", file1.content);
+        assertDirectory(files.get(5), "original/upload/folderPathB");
+        assertDirectory(files.get(6), "original/upload/folderPathB/filePath");
+        assertFile(files.get(7), "original/upload/folderPathB/filePath/test2.txt", file2.content);
+        assertFile(files.get(8), "original/upload/folderPathB/filePath/test3.txt", file3.content);
+    }
+
     @Test
     public void testCreateWithMultipleAttempts() throws Exception
     {
@@ -686,7 +838,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setProperties(properties);
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, file);
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, null, file);
         assertResponseOK(response);
 
         // first attempt
@@ -708,7 +860,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         } catch (Exception e)
         {
             String fullStackTrace = ExceptionUtils.getFullStackTrace(e);
-            assertTrue(fullStackTrace, fullStackTrace.contains("The folder for the upload id " + uploadId + " could not be found"));
+            assertTrue(fullStackTrace, fullStackTrace.contains("No uploaded files found for upload id '" + uploadId + "'"));
         }
     }
 
@@ -728,7 +880,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         creation.setExperimentId(new ExperimentIdentifier(experimentIdentifier));
         creation.setUploadId(uploadId);
 
-        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, file);
+        ContentResponse response = uploadFiles(sessionToken, uploadId, dataSetType, true, null, file);
         assertResponseOK(response);
 
         if (user.isDisabledProjectUser())
@@ -767,7 +919,7 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         }
     }
 
-    private ContentResponse uploadFiles(String sessionToken, String uploadId, String dataSetType, Boolean ignoreFilePath,
+    private ContentResponse uploadFiles(String sessionToken, String uploadId, String dataSetType, Boolean ignoreFilePath, String folderPath,
             FileToUpload... filesToUpload)
             throws InterruptedException, TimeoutException, ExecutionException
     {
@@ -795,6 +947,10 @@ public class CreateUploadedDataSetsTest extends AbstractFileTest
         {
             request.param(StoreShareFileUploadServlet.IGNORE_FILE_PATH_PARAM, String.valueOf(ignoreFilePath));
         }
+        if (folderPath != null)
+        {
+            request.param(StoreShareFileUploadServlet.FOLDER_PATH_PARAM, String.valueOf(folderPath));
+        }
         if (dataSetType != null)
         {
             request.param(StoreShareFileUploadServlet.DATA_SET_TYPE_PARAM, dataSetType);
diff --git a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-create.js b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-create.js
index 8ed2989b2e1144976e6cc5d38f585b2db9edd368..95925de5c24e5ff84e9c0996c1ed8f5a6d0edeb6 100644
--- a/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-create.js
+++ b/js-test/servers/common/core-plugins/tests/1/as/webapps/openbis-v3-api-test/html/test/test-create.js
@@ -756,15 +756,15 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 				// Selenium tests does not allow to use FormData class which
 				// would make constructing form data much easier
 
-				var formData = "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file\"; filename=\"file1.txt\"\r\nContent-Type: text/plain\r\n\r\n\r\ncontent1\r\n"
-						+ "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file\"; filename=\"file2.txt\"\r\nContent-Type: text/plain\r\n\r\n\r\ncontent2\r\n"
+				var formData = "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file\"; filename=\"filePath/file1.txt\"\r\nContent-Type: text/plain\r\n\r\n\r\ncontent1\r\n"
+						+ "------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"file\"; filename=\"filePath/file2.txt\"\r\nContent-Type: text/plain\r\n\r\n\r\ncontent2\r\n"
 						+ "------WebKitFormBoundary7MA4YWxkTrZu0gW--";
 
 				var dataStoreFacade = facade.getDataStoreFacade("DSS1");
 
 				return dataStoreFacade.createDataSetUpload("UNKNOWN").then(function(upload) {
 					return $.ajax({
-						url : upload.url,
+						url : upload.getUrl("testFolder", false),
 						type : "POST",
 						processData : false,
 						contentType : "multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW",
@@ -772,8 +772,8 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 					}).then(function() {
 						return c.createExperiment(facade).then(function(experimentPermId) {
 							var creation = new c.UploadedDataSetCreation();
-							creation.setUploadId(upload.id);
-							creation.setTypeId(new c.EntityTypePermId(upload.dataSetType));
+							creation.setUploadId(upload.getId());
+							creation.setTypeId(new c.EntityTypePermId(upload.getDataSetType()));
 							creation.setExperimentId(experimentPermId);
 							creation.setProperty("DESCRIPTION", "test description");
 							creation.setParentIds([ new c.DataSetPermId("20130424111751432-431") ]);
@@ -794,12 +794,13 @@ define([ 'jquery', 'underscore', 'openbis', 'test/openbis-execute-operations', '
 
 					return dataStoreFacade.searchFiles(criteria, c.createDataSetFileFetchOptions()).then(function(result) {
 						var files = result.getObjects();
-						c.assertEqual(files.length, 5, "Number of files");
+						c.assertEqual(files.length, 6, "Number of files");
 						c.assertEqual(files[0].path, "", "Path 0");
 						c.assertEqual(files[1].path, "original", "Path 1");
-						c.assertEqual(files[2].path, "original/upload", "Path 2");
-						c.assertEqual(files[3].path, "original/upload/file1.txt", "Path 3");
-						c.assertEqual(files[4].path, "original/upload/file2.txt", "Path 4");
+						c.assertEqual(files[2].path, "original/testFolder", "Path 2");
+						c.assertEqual(files[3].path, "original/testFolder/filePath", "Path 3");
+						c.assertEqual(files[4].path, "original/testFolder/filePath/file1.txt", "Path 4");
+						c.assertEqual(files[5].path, "original/testFolder/filePath/file2.txt", "Path 5");
 
 						c.assertEqual(dataSet.getType().getCode(), "UNKNOWN", "Type code");
 						c.assertEqual(dataSet.getProperty("DESCRIPTION"), "test description", "'DESCRIPTION' property value");
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/common/get/AbstractGetObjectsOperationExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/common/get/AbstractGetObjectsOperationExecutor.java
index 00c85047085d96909289eb6217b711cf72b6852f..2ced14b81ed9eed9d5bde3b2f2afe72f2be1e367 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/common/get/AbstractGetObjectsOperationExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/common/get/AbstractGetObjectsOperationExecutor.java
@@ -67,7 +67,7 @@ public abstract class AbstractGetObjectsOperationExecutor<OBJECT_ID extends IObj
         
         // sort and page the objects internal collections - ignore the top level changes
         // (we want to maintain all the results and keep them in order of the passed ids)
-        new SortAndPage().sortAndPage(idToObjectMap.values(), operation.getFetchOptions());
+        new SortAndPage().sortAndPage(idToObjectMap.values(), null, operation.getFetchOptions());
 
         return getOperationResult(idToObjectMap);
     }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/common/search/AbstractSearchObjectsOperationExecutor.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/common/search/AbstractSearchObjectsOperationExecutor.java
index 8abc1cce9a98236f9da569b064b0b75bf848c20a..fe8b92e6a27c171687e1eaa6c55c16b93d834f17 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/common/search/AbstractSearchObjectsOperationExecutor.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/executor/common/search/AbstractSearchObjectsOperationExecutor.java
@@ -77,7 +77,7 @@ public abstract class AbstractSearchObjectsOperationExecutor<OBJECT, OBJECT_PE,
         }
 
         Collection<OBJECT> allResults = searchAndTranslate(context, criteria, fetchOptions);
-        List<OBJECT> sortedAndPaged = sortAndPage(context, allResults, fetchOptions);
+        List<OBJECT> sortedAndPaged = sortAndPage(context, allResults, criteria, fetchOptions);
 
         SearchResult<OBJECT> searchResult = new SearchResult<OBJECT>(sortedAndPaged, allResults.size());
         return getOperationResult(searchResult);
@@ -119,7 +119,7 @@ public abstract class AbstractSearchObjectsOperationExecutor<OBJECT, OBJECT_PE,
         return objects;
     }
 
-    private List<OBJECT> sortAndPage(IOperationContext context, Collection<OBJECT> results, FETCH_OPTIONS fetchOptions)
+    private List<OBJECT> sortAndPage(IOperationContext context, Collection<OBJECT> results,  CRITERIA criteria, FETCH_OPTIONS fetchOptions)
     {
         if (results == null || results.isEmpty())
         {
@@ -127,7 +127,7 @@ public abstract class AbstractSearchObjectsOperationExecutor<OBJECT, OBJECT_PE,
         }
 
         SortAndPage sap = new SortAndPage();
-        Collection<OBJECT> objects = sap.sortAndPage(results, fetchOptions);
+        Collection<OBJECT> objects = sap.sortAndPage(results, criteria, fetchOptions);
 
         operationLog.info("Return " + objects.size() + " object(s) after sorting and paging.");
 
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/authorizationgroup/AuthorizationGroupComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/authorizationgroup/AuthorizationGroupComparatorFactory.java
index ab031cbce349128caf9e51e454fd4de75981cebd..67a721fe9ff5e8424c949b31ed8f92d4e418f2e5 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/authorizationgroup/AuthorizationGroupComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/authorizationgroup/AuthorizationGroupComparatorFactory.java
@@ -17,9 +17,12 @@
 package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.authorizationgroup;
 
 import java.util.Comparator;
+import java.util.Map;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.authorizationgroup.AuthorizationGroup;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.authorizationgroup.fetchoptions.AuthorizationGroupSortOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort.CodeComparator;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort.ComparatorFactory;
 
@@ -38,7 +41,7 @@ public class AuthorizationGroupComparatorFactory extends ComparatorFactory
     }
 
     @Override
-    public Comparator<AuthorizationGroup> getComparator(String field)
+    public Comparator<AuthorizationGroup> getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria)
     {
         if (AuthorizationGroupSortOptions.CODE.equals(field))
         {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/experiment/ExperimentComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/experiment/ExperimentComparatorFactory.java
index f63b3cfb13dcac2a429e5982effde3d11db5fce9..5ef251a313681ed659e72476d5f15702a8119fcb 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/experiment/ExperimentComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/experiment/ExperimentComparatorFactory.java
@@ -17,7 +17,10 @@
 package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.experiment;
 
 import java.util.Comparator;
+import java.util.Map;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.Experiment;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.fetchoptions.ExperimentSortOptions;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort.EntityWithPropertiesComparatorFactory;
@@ -36,13 +39,13 @@ public class ExperimentComparatorFactory extends EntityWithPropertiesComparatorF
     }
 
     @Override
-    public Comparator<Experiment> getComparator(String field)
+    public Comparator<Experiment> getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria)
     {
         if (ExperimentSortOptions.IDENTIFIER.equals(field))
         {
             return new IdentifierComparator<Experiment>();
         }
-        return super.getComparator(field);
+        return super.getComparator(field, parameters, criteria);
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/globalsearch/GlobalSearchObjectComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/globalsearch/GlobalSearchObjectComparatorFactory.java
index ee861127a9479ed7ad9fe272f3b10267c223b56c..51d0e92d3478a1655c5569ae1789733fbd6c726d 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/globalsearch/GlobalSearchObjectComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/globalsearch/GlobalSearchObjectComparatorFactory.java
@@ -18,7 +18,10 @@ package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.globalsearch;
 
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.Map;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.global.GlobalSearchObject;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.global.fetchoptions.GlobalSearchObjectSortOptions;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort.ComparatorFactory;
@@ -37,7 +40,7 @@ public class GlobalSearchObjectComparatorFactory extends ComparatorFactory
     }
 
     @Override
-    public Comparator<GlobalSearchObject> getComparator(String field)
+    public Comparator<GlobalSearchObject> getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria)
     {
         if (GlobalSearchObjectSortOptions.SCORE.equals(field))
         {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/plugin/PluginComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/plugin/PluginComparatorFactory.java
index 808276fd3e228dd9da12e7ffc9a9e87a47440622..7e53021ebf677de23a8d0ad1d32b2cd75aaeceba 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/plugin/PluginComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/plugin/PluginComparatorFactory.java
@@ -17,7 +17,10 @@
 package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.plugin;
 
 import java.util.Comparator;
+import java.util.Map;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.Plugin;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.plugin.fetchoptions.PluginSortOptions;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort.AbstractStringComparator;
@@ -44,7 +47,7 @@ public class PluginComparatorFactory extends ComparatorFactory
     }
 
     @Override
-    public Comparator<Plugin> getComparator(String field)
+    public Comparator<Plugin> getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria)
     {
         if (PluginSortOptions.NAME.equals(field))
         {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/project/ProjectComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/project/ProjectComparatorFactory.java
index ed573d531597e6c4878586dd40d82b4b30dbca57..b469076fd30d10b3198d8a428f18010eda805888 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/project/ProjectComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/project/ProjectComparatorFactory.java
@@ -17,7 +17,10 @@
 package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.project;
 
 import java.util.Comparator;
+import java.util.Map;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.Project;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.fetchoptions.ProjectSortOptions;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort.EntityComparatorFactory;
@@ -36,13 +39,13 @@ public class ProjectComparatorFactory extends EntityComparatorFactory<Project>
     }
 
     @Override
-    public Comparator<Project> getComparator(String field)
+    public Comparator<Project> getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria)
     {
         if (ProjectSortOptions.IDENTIFIER.equals(field))
         {
             return new IdentifierComparator<Project>();
         }
-        return super.getComparator(field);
+        return super.getComparator(field, parameters, criteria);
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/property/PropertyAssignmentComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/property/PropertyAssignmentComparatorFactory.java
index 251b1e47e6ce9f6e24d2712d3194a33341a6c80f..93b07657b74121a7b1661b68ac6a50b1a01f279f 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/property/PropertyAssignmentComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/property/PropertyAssignmentComparatorFactory.java
@@ -20,6 +20,8 @@ import java.util.Comparator;
 import java.util.HashMap;
 import java.util.Map;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.PropertyAssignment;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.PropertyType;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.fetchoptions.PropertyAssignmentSortOptions;
@@ -69,7 +71,7 @@ public class PropertyAssignmentComparatorFactory extends ComparatorFactory
     }
 
     @Override
-    public Comparator<?> getComparator(String field)
+    public Comparator<?> getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria)
     {
         return COMPARATORS_BY_FIELD.get(field);
     }
@@ -77,7 +79,7 @@ public class PropertyAssignmentComparatorFactory extends ComparatorFactory
     @Override
     public Comparator<?> getDefaultComparator()
     {
-        return getComparator(PropertyAssignmentSortOptions.ORDINAL);
+        return getComparator(PropertyAssignmentSortOptions.ORDINAL, null, null);
     }
 
     private abstract static class AbstractPropertyAssignmentComparator extends AbstractStringComparator<PropertyAssignment>
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/property/PropertyTypeComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/property/PropertyTypeComparatorFactory.java
index ef68061cd5891c649d89942ebe5f7949aa52b019..f8c044fa6956b20ee6e015144637a0a3b52b10aa 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/property/PropertyTypeComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/property/PropertyTypeComparatorFactory.java
@@ -17,7 +17,10 @@
 package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.property;
 
 import java.util.Comparator;
+import java.util.Map;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.PropertyType;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.fetchoptions.PropertyTypeSortOptions;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.fetchoptions.VocabularySortOptions;
@@ -38,7 +41,7 @@ public class PropertyTypeComparatorFactory extends ComparatorFactory
     }
 
     @Override
-    public Comparator<PropertyType> getComparator(String field)
+    public Comparator<PropertyType> getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria)
     {
         if (VocabularySortOptions.CODE.equals(field))
         {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sample/SampleComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sample/SampleComparatorFactory.java
index 4218ac4f930ce5bdd693a38c79dde335ef794b4c..8a1c3ddb2d2c19e2d8a0c32568fc2a557438b771 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sample/SampleComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sample/SampleComparatorFactory.java
@@ -17,7 +17,10 @@
 package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sample;
 
 import java.util.Comparator;
+import java.util.Map;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.Sample;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.fetchoptions.SampleSortOptions;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort.EntityWithPropertiesComparatorFactory;
@@ -36,13 +39,13 @@ public class SampleComparatorFactory extends EntityWithPropertiesComparatorFacto
     }
 
     @Override
-    public Comparator<Sample> getComparator(String field)
+    public Comparator<Sample> getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria)
     {
         if (SampleSortOptions.IDENTIFIER.equals(field))
         {
             return new IdentifierComparator<Sample>();
         }
-        return super.getComparator(field);
+        return super.getComparator(field, parameters, criteria);
     }
 
 }
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/ComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/ComparatorFactory.java
index cfa927e6528b38826028c43e2bf1917312200d88..16e7ff12ed63c71d547e4e63c20328f185c7457d 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/ComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/ComparatorFactory.java
@@ -19,7 +19,10 @@ package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort;
 import java.util.Comparator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.authorizationgroup.AuthorizationGroupComparatorFactory;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.dataset.DataSetComparatorFactory;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.experiment.ExperimentComparatorFactory;
@@ -64,7 +67,7 @@ public abstract class ComparatorFactory
 
     public abstract boolean accepts(Class<?> sortOptionsClass);
 
-    public abstract Comparator getComparator(String field);
+    public abstract Comparator getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria);
 
     public abstract Comparator getDefaultComparator();
 
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/EntityComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/EntityComparatorFactory.java
index 88deff478fa9755fa49d8bd693a0cde6784363c9..213a197831c0dde96a9e2651166fc5221f49f3d5 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/EntityComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/EntityComparatorFactory.java
@@ -17,12 +17,15 @@
 package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort;
 
 import java.util.Comparator;
+import java.util.Map;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.EntitySortOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ICodeHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IModificationDateHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IPermIdHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IRegistrationDateHolder;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 
 /**
  * @author pkupczyk
@@ -38,7 +41,7 @@ public class EntityComparatorFactory<OBJECT extends ICodeHolder & IPermIdHolder
     }
 
     @Override
-    public Comparator<OBJECT> getComparator(String field)
+    public Comparator<OBJECT> getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria)
     {
         if (EntitySortOptions.CODE.equals(field))
         {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/EntityWithPropertiesComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/EntityWithPropertiesComparatorFactory.java
index 0ba498077cf6387dc6e502ca08d367b5e4d9ae22..e57c58e1198c839afe649fc9fcd1bec7028da1ad 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/EntityWithPropertiesComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/EntityWithPropertiesComparatorFactory.java
@@ -17,14 +17,17 @@
 package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort;
 
 import java.util.Comparator;
+import java.util.Map;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.EntityWithPropertiesSortOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ICodeHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IEntityTypeHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IModificationDateHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IPermIdHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IPropertiesHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IRegistrationDateHolder;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 
 /**
  * @author pkupczyk
@@ -40,9 +43,12 @@ public class EntityWithPropertiesComparatorFactory<OBJECT extends ICodeHolder &
     }
 
     @Override
-    public Comparator<OBJECT> getComparator(String field)
+    public Comparator<OBJECT> getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria)
     {
-        if (field.equals(EntityWithPropertiesSortOptions.TYPE))
+    	if (field.equals(EntityWithPropertiesSortOptions.FETCHED_FIELDS_SCORE))
+        {
+            return new FetchedFieldsScoreComparator<OBJECT>(parameters, criteria);
+        } if (field.equals(EntityWithPropertiesSortOptions.TYPE))
         {
             return new TypeComparator<OBJECT>();
         } else if (field.startsWith(EntityWithPropertiesSortOptions.PROPERTY))
@@ -50,7 +56,7 @@ public class EntityWithPropertiesComparatorFactory<OBJECT extends ICodeHolder &
             return new PropertyComparator<OBJECT>(field.substring(EntityWithPropertiesSortOptions.PROPERTY.length()));
         } else
         {
-            return super.getComparator(field);
+            return super.getComparator(field, parameters, criteria);
         }
     }
 
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/FetchedFieldsScoreComparator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/FetchedFieldsScoreComparator.java
new file mode 100644
index 0000000000000000000000000000000000000000..c02894de135373f3eb7d0ad30aab2b035979a69d
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/FetchedFieldsScoreComparator.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright 2016 ETH Zuerich, CISD
+ *
+ * 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 ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IEntityTypeHolder;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IPermIdHolder;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IPropertiesHolder;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.AbstractEntitySearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.CodeSearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.StringPropertySearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.DataSet;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.search.DataSetTypeSearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.search.AbstractEntityTypeSearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.Experiment;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.search.ExperimentTypeSearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.material.Material;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.material.search.MaterialTypeSearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.Sample;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.search.SampleTypeSearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ICodeHolder;
+
+
+/**
+ * @author juanf
+ */
+public class FetchedFieldsScoreComparator<OBJECT extends IEntityTypeHolder & IPropertiesHolder & ICodeHolder & IPermIdHolder> extends AbstractComparator<OBJECT, Integer>
+{
+
+	private Map<SortParameter, String> parameters;
+	private AbstractEntitySearchCriteria criteria;
+    private List<Pattern> partialMatchTerms = new ArrayList<Pattern>();
+    private List<String> exactMatchTerms = new ArrayList<String>();
+    private List<Boost> boosts = new ArrayList<Boost>();
+    private Map<OBJECT, Integer> scoreCache = new HashMap<>();
+    
+    private Integer fullCodeBoost = 0;
+    private Integer partialCodeBoost = 0;
+    private Integer fullPropertyBoost = 0;
+    private Integer fullTypeBoost = 0;
+    private Integer partialPropertyBoost = 0;
+	
+	public FetchedFieldsScoreComparator(Map<SortParameter, String> parameters, ISearchCriteria criteria) 
+	{
+		if (criteria == null || (criteria instanceof AbstractEntitySearchCriteria) == false)
+        {
+            throw new IllegalArgumentException("Missing criteria");
+        }
+        
+        if (parameters == null)
+        {
+            throw new IllegalArgumentException("Missing score parameters");
+        } else 
+        {
+	        	if(parameters.containsKey(SortParameter.FULL_CODE_BOOST)) 
+	        	{
+	        		fullCodeBoost = Integer.parseInt(parameters.get(SortParameter.FULL_CODE_BOOST));
+	        	}
+	        	if(parameters.containsKey(SortParameter.PARTIAL_CODE_BOOST)) 
+	        	{
+	        		partialCodeBoost = Integer.parseInt(parameters.get(SortParameter.PARTIAL_CODE_BOOST));
+	        	}
+	        	if(parameters.containsKey(SortParameter.FULL_PROPERTY_BOOST)) 
+	        	{
+	        		fullPropertyBoost = Integer.parseInt(parameters.get(SortParameter.FULL_PROPERTY_BOOST));
+	        	}
+	        	if(parameters.containsKey(SortParameter.FULL_TYPE_BOOST)) 
+	        	{
+	        		fullTypeBoost = Integer.parseInt(parameters.get(SortParameter.FULL_TYPE_BOOST));
+	        	}
+	        	if(parameters.containsKey(SortParameter.PARTIAL_PROPERTY_BOOST)) 
+	        	{
+	        		partialPropertyBoost = Integer.parseInt(parameters.get(SortParameter.PARTIAL_PROPERTY_BOOST));
+	        	}
+        }
+        
+		this.parameters = parameters;
+		this.criteria = (AbstractEntitySearchCriteria) criteria;
+		
+        // Shared
+        this.partialMatchTerms = new ArrayList<Pattern>();
+        this.exactMatchTerms = new ArrayList<String>();
+        this.boosts = new ArrayList<Boost>();
+
+        for(ISearchCriteria subCriteria:this.criteria.getCriteria()) 
+        {
+        		ISearchCriteriaParser<ISearchCriteria> parser = criteriaParsers.get(subCriteria.getClass());
+        		
+        		if(parser != null) 
+        		{
+        			String value = parser.getValue(subCriteria);
+        			
+        			// Full Index
+        			partialMatchTerms.add(getPartialMatchTerm(value));
+        			exactMatchTerms.add(getExactMatchTerm(value));
+        			boosts.add(parser.getBoost(subCriteria, 10));
+
+        			// Split Index
+        			String[] splitIndexes = value.replace("*", " ").replace("?", " ").replaceAll("\\s+", " ").trim().split(" ");
+
+        			for (String splitIndex : splitIndexes)
+        			{
+        				partialMatchTerms.add(getPartialMatchTerm(splitIndex));
+        				exactMatchTerms.add(getExactMatchTerm(splitIndex));
+        				boosts.add(parser.getBoost(subCriteria, 1));
+        			}
+        		}
+        		
+        }
+        
+	}
+    
+	@Override
+    public int compare(OBJECT o1, OBJECT o2)
+    {
+        return -1 * super.compare(o1, o2); // Higher scores first
+    }
+	
+    @Override
+    protected Integer getValue(OBJECT o)
+    {
+    		Integer score = scoreCache.get(o);
+    		if(score == null) {
+    			score = calculateScore(o);
+    			scoreCache.put(o, score);
+    		}
+    		return score;
+    }
+    
+    //
+    // V3 Helper Methods
+    //
+    
+    private static Map<Class, ISearchCriteriaParser> criteriaParsers = new HashMap<>(3);
+    
+    static {
+    		criteriaParsers.put(CodeSearchCriteria.class, new CodeCriteriaParser());
+    		criteriaParsers.put(StringPropertySearchCriteria.class, new StringPropertySearchCriteriaParser());
+    		criteriaParsers.put(SampleTypeSearchCriteria.class, new AbstractEntityTypeSearchCriteriaParser());
+    		criteriaParsers.put(ExperimentTypeSearchCriteria.class, new AbstractEntityTypeSearchCriteriaParser());
+    		criteriaParsers.put(DataSetTypeSearchCriteria.class, new AbstractEntityTypeSearchCriteriaParser());
+    		criteriaParsers.put(MaterialTypeSearchCriteria.class, new AbstractEntityTypeSearchCriteriaParser());
+    }
+
+    private static interface ISearchCriteriaParser<ISearchCriteria> {
+    		public String getValue(ISearchCriteria criteria);
+    		public Boost getBoost(ISearchCriteria criteria, int boost);
+    }
+    
+    private static class CodeCriteriaParser implements ISearchCriteriaParser<CodeSearchCriteria> {
+		@Override
+		public String getValue(CodeSearchCriteria criteria) {
+			return criteria.getFieldValue().getValue();
+		}
+
+		@Override
+		public Boost getBoost(CodeSearchCriteria criteria, int boost) {
+			return new Boost(boost, 0, 0, 0, null);
+		}
+    }
+    
+    private static class StringPropertySearchCriteriaParser implements ISearchCriteriaParser<StringPropertySearchCriteria> {
+		@Override
+		public String getValue(StringPropertySearchCriteria criteria) {
+			return criteria.getFieldValue().getValue();
+		}
+
+		@Override
+		public Boost getBoost(StringPropertySearchCriteria criteria, int boost) {
+			return new Boost(0, 0, 0, boost, criteria.getFieldName());
+		}
+    }
+    
+    private static class AbstractEntityTypeSearchCriteriaParser implements ISearchCriteriaParser<AbstractEntityTypeSearchCriteria> {
+		@Override
+		public String getValue(AbstractEntityTypeSearchCriteria criteria) {
+			for(ISearchCriteria subCriteria:criteria.getCriteria()) {
+				if(subCriteria instanceof CodeSearchCriteria) {
+					return criteriaParsers.get(subCriteria.getClass()).getValue(subCriteria);
+				}
+			}
+			return null;
+		}
+
+		@Override
+		public Boost getBoost(AbstractEntityTypeSearchCriteria criteria, int boost) {
+			return new Boost(0, boost, 0, 0, null);
+		}
+    }
+    
+    //
+    // Scoring algorithm
+    //
+	
+    private boolean hasType(OBJECT o ) {
+    		if(o instanceof Sample) {
+    			return ((Sample)o).getFetchOptions().hasType();
+    		} else if(o instanceof Experiment) {
+    			return ((Experiment)o).getFetchOptions().hasType();
+    		} if(o instanceof DataSet) {
+    			return ((DataSet)o).getFetchOptions().hasType();
+    		} if(o instanceof Material) {
+    			return ((Material)o).getFetchOptions().hasType();
+    		} else {
+    			return false;
+    		}
+    }
+    
+    private boolean hasProperties(OBJECT o ) {
+		if(o instanceof Sample) {
+			return ((Sample)o).getFetchOptions().hasProperties();
+		} else if(o instanceof Experiment) {
+			return ((Experiment)o).getFetchOptions().hasProperties();
+		} if(o instanceof DataSet) {
+			return ((DataSet)o).getFetchOptions().hasProperties();
+		} if(o instanceof Material) {
+			return ((Material)o).getFetchOptions().hasProperties();
+		} else {
+			return false;
+		}
+}
+    
+	private Integer calculateScore(OBJECT o ) {
+		int score = 0;
+		String code = o.getCode();
+		String typeCode = (hasType(o))?o.getType().getCode():null;
+		Map<String, String> properties = (hasProperties(o))?o.getProperties():null;
+		
+		for (int i = 0; i < exactMatchTerms.size(); i++)
+	    {
+	        Pattern partialTerm = partialMatchTerms.get(i);
+	        String exactTerm = exactMatchTerms.get(i);
+	        Boost boost = boosts.get(i);
+	
+	        // 1. Code
+	        if(code != null) {
+	        		if (isPartialMatch(code, partialTerm))
+	            { // If code matches partially
+	                score += partialCodeBoost * boost.getCodeBoost();
+	                if (isExactMatch(code, exactTerm))
+	                { // If code matches exactly
+	                    score += fullCodeBoost * boost.getCodeBoost();
+	                }
+	            }
+	        }
+	
+	        // 2. Entity type code
+	        if(typeCode != null) 
+	        {
+	        		if (isExactMatch(typeCode, exactTerm))
+	            { // If type matches exactly
+	                score += fullTypeBoost * boost.getTypeCodeBoost();
+	            }
+	        }
+	        
+	
+	        // 3. Properties
+	        if(properties != null) {
+	        		if (properties != null && properties.keySet() != null)
+	            {
+	                for (String propertykey : properties.keySet())
+	                {
+	                    String propertyValue = properties.get(propertykey);
+	                    if (isPartialMatch(propertyValue, partialTerm))
+	                    { // If property matches partially
+	                        score += partialPropertyBoost * boost.getPropertyBoost(propertykey);
+	                        if (isExactMatch(propertyValue, exactTerm))
+	                        { // If property matches exactly
+	                            score += fullPropertyBoost * boost.getPropertyBoost(propertykey);
+	                        }
+	                    }
+	                }
+	            }
+	        }
+	    }
+		
+		return score;
+	}
+	
+    //
+    // Helper Methods
+    //
+    
+    private static class Boost
+    {
+        private int codeBoost;
+
+        private int typeCodeBoost;
+
+        private int propertyBoost;
+
+        private int propertyDefaultBoost;
+
+        private String propertyName;
+
+        public Boost(int codeBoost, int typeCodeBoost, int propertyDefaultBoost, int propertyBoost, String propertyName)
+        {
+            super();
+            this.codeBoost = codeBoost;
+            this.typeCodeBoost = typeCodeBoost;
+            this.propertyDefaultBoost = propertyDefaultBoost;
+            this.propertyBoost = propertyBoost;
+            this.propertyName = propertyName;
+        }
+
+        public int getCodeBoost()
+        {
+            return codeBoost;
+        }
+
+        public int getTypeCodeBoost()
+        {
+            return typeCodeBoost;
+        }
+
+        public int getPropertyBoost(String propertyNameToBoost)
+        {
+            if (this.propertyName != null && this.propertyName.equals(propertyNameToBoost))
+            {
+                return propertyBoost;
+            } else
+            {
+                return propertyDefaultBoost;
+            }
+        }
+
+    }
+
+    private Pattern getPartialMatchTerm(String term)
+    {
+        return Pattern.compile(("*" + term + "*").replace("*", ".*").replace("?", ".?"), Pattern.CASE_INSENSITIVE);
+    }
+
+    private String getExactMatchTerm(String term)
+    {
+        return term.replace("*", "").replace("?", "");
+    }
+
+    private boolean isExactMatch(String value, String term)
+    {
+        if (value != null && term != null)
+        {
+            return value.equalsIgnoreCase(term);
+        } else
+        {
+            return false;
+        }
+    }
+
+    private boolean isPartialMatch(String value, Pattern pattern)
+    {
+        if (value != null && pattern != null)
+        {
+            return pattern.matcher(value).matches();
+        } else
+        {
+            return false;
+        }
+    }
+}
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/SortAndPage.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/SortAndPage.java
index 63d9eba47c8a832aaf58eacbccf6945ac0090d39..80afc702f5a927b4b90e14365330e6f6e79ed41f 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/SortAndPage.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/SortAndPage.java
@@ -37,6 +37,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.Sorting;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.view.AbstractCollectionView;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.view.ListView;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.view.SetView;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 
 /**
  * @author pkupczyk
@@ -49,7 +50,7 @@ public class SortAndPage
 
     private MethodsCache methodsCache = new MethodsCache();
 
-    public <T, C extends Collection<T>> C sortAndPage(C objects, FetchOptions fo)
+    public <T, C extends Collection<T>> C sortAndPage(C objects, ISearchCriteria c, FetchOptions fo)
     {
         C newObjects = objects;
 
@@ -58,21 +59,21 @@ public class SortAndPage
             newObjects = (C) ((AbstractCollectionView) objects).getOriginalCollection();
         }
 
-        newObjects = (C) sort(newObjects, fo);
+        newObjects = (C) sort(newObjects, c, fo);
         newObjects = (C) page(newObjects, fo);
-        nest(newObjects, fo);
+        nest(newObjects, c, fo);
 
         return newObjects;
     }
-
-    private Collection sort(Collection objects, FetchOptions fo)
+    
+    private Collection sort(Collection objects, ISearchCriteria c, FetchOptions fo)
     {
         if (objects == null || objects.isEmpty())
         {
             return objects;
         }
 
-        Comparator comparator = getComparator(fo);
+        Comparator comparator = getComparator(c, fo);
 
         if (comparator != null)
         {
@@ -135,7 +136,7 @@ public class SortAndPage
         }
     }
 
-    private void nest(Collection objects, FetchOptions fo)
+    private void nest(Collection objects, ISearchCriteria c, FetchOptions fo)
     {
         if (objects == null || objects.isEmpty())
         {
@@ -177,14 +178,14 @@ public class SortAndPage
                         {
                             if (value instanceof Collection)
                             {
-                                Collection newValue = sortAndPage((Collection) value, subFo);
+                                Collection newValue = sortAndPage((Collection) value, c, subFo);
                                 setMethod.invoke(object, newValue);
                             } else if (value instanceof Map)
                             {
-                                sortAndPage(((Map) value).values(), subFo);
+                                sortAndPage(((Map) value).values(), c, subFo);
                             } else
                             {
-                                Collection newValue = sortAndPage(Collections.singleton(value), subFo);
+                                Collection newValue = sortAndPage(Collections.singleton(value), c, subFo);
                                 if (setMethod != null)
                                 {
                                     setMethod.invoke(object, newValue.iterator().next());
@@ -200,7 +201,7 @@ public class SortAndPage
         }
     }
 
-    protected Comparator getComparator(FetchOptions fetchOptions)
+    protected Comparator getComparator(ISearchCriteria criteria, FetchOptions fetchOptions)
     {
         if (fetchOptions == null)
         {
@@ -240,7 +241,7 @@ public class SortAndPage
                             throw new IllegalArgumentException("Comparator factory for sort by " + sortByClass + " not found");
                         }
 
-                        Comparator aComparator = comparatorFactory.getComparator(sorting.getField());
+                        Comparator aComparator = comparatorFactory.getComparator(sorting.getField(), sorting.getParameters(), criteria);
 
                         if (aComparator == null)
                         {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/tag/TagComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/tag/TagComparatorFactory.java
index 0ae8a397c2f22dac8df5f4c86dc90d9b0e2e8e65..598231a9f07c14cc2f6cae2f2db2af44bfd92b25 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/tag/TagComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/tag/TagComparatorFactory.java
@@ -17,7 +17,10 @@
 package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.tag;
 
 import java.util.Comparator;
+import java.util.Map;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.Tag;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.fetchoptions.TagSortOptions;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort.CodeComparator;
@@ -37,7 +40,7 @@ public class TagComparatorFactory extends ComparatorFactory
     }
 
     @Override
-    public Comparator<Tag> getComparator(String field)
+    public Comparator<Tag> getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria)
     {
         if (TagSortOptions.CODE.equals(field))
         {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/vocabulary/VocabularyComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/vocabulary/VocabularyComparatorFactory.java
index a6740821d46a54076e9774439a2cbebe95b88f10..dab46dbd380183d7a2f5ae4c48c2074d0374499c 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/vocabulary/VocabularyComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/vocabulary/VocabularyComparatorFactory.java
@@ -17,7 +17,10 @@
 package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.vocabulary;
 
 import java.util.Comparator;
+import java.util.Map;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.Vocabulary;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.fetchoptions.VocabularySortOptions;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort.CodeComparator;
@@ -37,7 +40,7 @@ public class VocabularyComparatorFactory extends ComparatorFactory
     }
 
     @Override
-    public Comparator<Vocabulary> getComparator(String field)
+    public Comparator<Vocabulary> getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria)
     {
         if (VocabularySortOptions.CODE.equals(field))
         {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/vocabulary/VocabularyTermComparatorFactory.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/vocabulary/VocabularyTermComparatorFactory.java
index 7d4fb30ccc8d5758753674bdaea8ad3b3c2d40c6..35bdb6d11d92c8c323e1ed42e861e2f7620e1780 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/vocabulary/VocabularyTermComparatorFactory.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/vocabulary/VocabularyTermComparatorFactory.java
@@ -17,7 +17,10 @@
 package ch.ethz.sis.openbis.generic.server.asapi.v3.helper.vocabulary;
 
 import java.util.Comparator;
+import java.util.Map;
 
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.SortParameter;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ISearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.VocabularyTerm;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.vocabulary.fetchoptions.VocabularyTermSortOptions;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort.CodeComparator;
@@ -36,7 +39,7 @@ public class VocabularyTermComparatorFactory extends ComparatorFactory
     }
 
     @Override
-    public Comparator<VocabularyTerm> getComparator(String field)
+    public Comparator<VocabularyTerm> getComparator(String field, Map<SortParameter, String> parameters, ISearchCriteria criteria)
     {
         if (VocabularyTermSortOptions.CODE.equals(field))
         {
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleTypeTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleTypeTranslator.java
index 5763f56f49b6e009bdd368c5c2625f408b8268a5..03e40acf46d9f60659637643f7d2eefd59712ab5 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleTypeTranslator.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/translator/sample/SampleTypeTranslator.java
@@ -105,7 +105,7 @@ public class SampleTypeTranslator extends AbstractCachingTranslator<Long, Sample
         {
             Collection<PropertyAssignment> assignments = relations.get(ISamplePropertyAssignmentTranslator.class, typeId);
             List<PropertyAssignment> propertyAssignments = new ArrayList<>(assignments);
-            result.setPropertyAssignments(new SortAndPage().sortAndPage(propertyAssignments, fetchOptions.withPropertyAssignments()));
+            result.setPropertyAssignments(new SortAndPage().sortAndPage(propertyAssignments, null, fetchOptions.withPropertyAssignments()));
         }
 
         if (fetchOptions.hasSemanticAnnotations())
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/EntityWithPropertiesSortOptions.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/EntityWithPropertiesSortOptions.js
index a9b6cce007cc153b3f78df8ad64ab9e32e818a1c..e289ffceb3c4592a22071ab72daec7200c215fd9 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/EntityWithPropertiesSortOptions.js
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/EntityWithPropertiesSortOptions.js
@@ -1,17 +1,31 @@
-define([ "require", "stjs", "as/dto/common/fetchoptions/EntitySortOptions" ], function(require, stjs, EntitySortOptions) {
+define([ "require", "stjs", "as/dto/common/fetchoptions/EntitySortOptions", "as/dto/common/fetchoptions/SortParameter" ], function(require, stjs, EntitySortOptions, SortParameter) {
 	var EntityWithPropertiesSortOptions = function() {
 		EntitySortOptions.call(this);
 	};
 
 	var fields = {
+		FETCHED_FIELDS_SCORE : "FETCHED_FIELDS_SCORE",
 		TYPE : "TYPE",
 		PROPERTY : "PROPERTY"
 	};
-
+    
 	stjs.extend(EntityWithPropertiesSortOptions, EntitySortOptions, [ EntitySortOptions ], function(constructor, prototype) {
 		prototype['@type'] = 'as.dto.common.fetchoptions.EntityWithPropertiesSortOptions';
 		constructor.serialVersionUID = 1;
 
+		prototype.fetchedFieldsScore = function() {
+			var parameters = {};
+				parameters[SortParameter.FULL_CODE_BOOST] = 		"1000000";
+				parameters[SortParameter.PARTIAL_CODE_BOOST] = 	 "100000";
+				parameters[SortParameter.FULL_PROPERTY_BOOST] = 	  "10000";
+				parameters[SortParameter.FULL_TYPE_BOOST] = 		   "1000";
+				parameters[SortParameter.PARTIAL_PROPERTY_BOOST] =   "100";
+			
+			return this.getOrCreateSortingWithParameters(fields.FETCHED_FIELDS_SCORE, parameters);
+		};
+		prototype.getFetchedFieldsScore = function() {
+			return this.getSorting(fields.FETCHED_FIELDS_SCORE);
+		};
 		prototype.type = function() {
 			return this.getOrCreateSorting(fields.TYPE);
 		};
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/SortOptions.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/SortOptions.js
index d9dc882cef503ae2f1a601a223fc8bca35e6a283..588341e28fc49f4418a1f61716ad1a390e738c3e 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/SortOptions.js
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/SortOptions.js
@@ -6,14 +6,20 @@ define([ "require", "stjs", "as/dto/common/fetchoptions/SortOrder", "as/dto/comm
 	stjs.extend(SortOptions, null, [], function(constructor, prototype) {
 		prototype['@type'] = 'as.dto.common.fetchoptions.SortOptions';
 		constructor.serialVersionUID = 1;
+		
 		prototype.getOrCreateSorting = function(field) {
+			return this.getOrCreateSortingWithParameters(field, null);
+		};
+		
+		prototype.getOrCreateSortingWithParameters = function(field, parameters) {
 			var order = this.getSorting(field);
 			if (order == null) {
 				order = new SortOrder();
-				this.sortings.push(new Sorting(field, order));
+				this.sortings.push(new Sorting(field, order, parameters));
 			}
 			return order;
 		};
+		
 		prototype.getSorting = function(field) {
 			var order = null;
 			this.sortings.forEach(function(sorting) {
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/SortParameter.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/SortParameter.js
new file mode 100644
index 0000000000000000000000000000000000000000..fd223f6c74c9e0cfb6a0ee77d4f5c5543d188b2d
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/SortParameter.js
@@ -0,0 +1,12 @@
+/**
+ * @author juanf
+ */
+
+define([ "stjs", "as/dto/common/Enum" ], function(stjs, Enum) {
+	var SortParameter = function() {
+		Enum.call(this, [ "FULL_CODE_BOOST", "PARTIAL_CODE_BOOST", "FULL_PROPERTY_BOOST", "FULL_TYPE_BOOST", "PARTIAL_PROPERTY_BOOST" ]);
+	};
+	stjs.extend(SortParameter, Enum, [ Enum ], function(constructor, prototype) {
+	}, {});
+	return new SortParameter();
+})
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/Sorting.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/Sorting.js
index 534db78593281c3681b2b8fbc8b1c64132f661c9..2cd97cffec8146c3b863aca403ccd27cd45cb715 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/Sorting.js
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/as/dto/common/fetchoptions/Sorting.js
@@ -1,7 +1,8 @@
 define([ "require", "stjs" ], function(require, stjs) {
-	var Sorting = function(field, order) {
+	var Sorting = function(field, order, parameters) {
 		this.field = field;
 		this.order = order;
+		this.parameters = parameters;
 	};
 
 	stjs.extend(Sorting, null, [], function(constructor, prototype) {
@@ -13,6 +14,9 @@ define([ "require", "stjs" ], function(require, stjs) {
 		prototype.getOrder = function() {
 			return this.order;
 		};
+		prototype.getParameters = function() {
+			return this.parameters;
+		};
 	}, {});
 	return Sorting;
 })
\ No newline at end of file
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/openbis.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/openbis.js
index 51cf65ac0aeace286b8af04b449f8c74078d4d2f..7147310bf3d1d24c26de27c3b058b9a854c7ee91 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/openbis.js
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/resources/api/v3/openbis.js
@@ -228,22 +228,35 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 						if (dataStores.length > 1) {
 							dfd.reject("Please specify exactly one data store");
 						} else {
+							var dataStore = dataStores[0];
 							var now = new Date();
 							var id = "upload-" + now.getFullYear() + pad(now.getMonth() + 1, 2) + pad(now.getDate(), 2) + pad(now.getHours(), 2) + pad(now.getMinutes(), 2) + pad(now.getSeconds(), 2)
 									+ "-" + pad(Math.round(Math.random() * 100000), 5);
 
-							var params = {
-								"sessionID" : facade._private.sessionToken,
-								"uploadID" : id,
-								"dataSetType" : dataSetType
-							};
-
-							var url = dataStores[0].downloadUrl + "/datastore_server/store_share_file_upload?" + jquery.param(params);
-
 							dfd.resolve({
-								"id" : id,
-								"url" : url,
-								"dataSetType" : dataSetType
+								"getId" : function() {
+									return id;
+								},
+								"getUrl" : function(folderPath, ignoreFilePath) {
+									var params = {
+										"sessionID" : facade._private.sessionToken,
+										"uploadID" : id,
+										"dataSetType" : dataSetType
+									};
+
+									if (folderPath != null) {
+										params["folderPath"] = folderPath;
+									}
+
+									if (ignoreFilePath != null) {
+										params["ignoreFilePath"] = ignoreFilePath;
+									}
+
+									return dataStore.downloadUrl + "/datastore_server/store_share_file_upload?" + jquery.param(params);
+								},
+								"getDataSetType" : function() {
+									return dataSetType;
+								}
 							});
 						}
 
@@ -569,7 +582,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.createTags = function(creations) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -765,7 +778,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.updatePropertyTypes = function(updates) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -798,7 +811,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.updateVocabularyTerms = function(updates) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -809,7 +822,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.updateTags = function(updates) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -999,7 +1012,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.getVocabularyTerms = function(ids, fetchOptions) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -1511,7 +1524,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.deletePropertyTypes = function(ids, deletionOptions) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -1533,7 +1546,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.deleteVocabularyTerms = function(ids, deletionOptions) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
@@ -1544,7 +1557,7 @@ define([ 'jquery', 'util/Json', 'as/dto/datastore/search/DataStoreSearchCriteria
 				}
 			});
 		}
-		
+
 		this.deleteEntityTypes = function(ids, deletionOptions) {
 			var thisFacade = this;
 			return thisFacade._private.ajaxRequest({
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/SortAndPageTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/SortAndPageTest.java
index 45fc0ff733c529039b33f129baef5a799cad2dd0..211a2290c29b8e48ecb2060aa7142a0a18817b65 100644
--- a/openbis/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/SortAndPageTest.java
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/generic/server/asapi/v3/helper/sort/SortAndPageTest.java
@@ -16,17 +16,168 @@ import org.testng.annotations.Test;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.material.Material;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.material.fetchoptions.MaterialFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.material.search.MaterialSearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.person.Person;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.project.Project;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.Sample;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.SampleType;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.fetchoptions.SampleFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.search.SampleSearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.space.Space;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.tag.Tag;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.helper.sort.SortAndPage;
 
 public class SortAndPageTest
 {
+	@Test
+    public void testFetchedFieldsScore_CodeScore()
+    {
+		SampleType sampleTypeA = new SampleType();
+		sampleTypeA.setCode("DUMMY_CODE_A");
+		
+		SampleType sampleTypeB = new SampleType();
+		sampleTypeB.setCode("DUMMY_CODE_B");
+		
+		String propertyCode = "DUMMY_PROPERTY";
+		
+		SampleSearchCriteria c = new SampleSearchCriteria();
+		c.withOrOperator();
+        c.withCode().thatEquals("S2");
+        
+		SampleFetchOptions fo = new SampleFetchOptions();
+		fo.withType();
+		fo.withProperties();
+		fo.sortBy().fetchedFieldsScore();
+		
+        Sample sample1 = new Sample();
+        sample1.setType(sampleTypeA);
+        sample1.setCode("S1");
+        sample1.setProperty(propertyCode, "DUMMY_S1");
+        sample1.setFetchOptions(fo);
+
+        Sample sample2 = new Sample();
+        sample2.setType(sampleTypeB);
+        sample2.setCode("S2");
+        sample2.setProperty(propertyCode, "DUMMY_S2");
+        sample2.setFetchOptions(fo);
+
+        Sample sample3 = new Sample();
+        sample3.setType(sampleTypeA);
+        sample3.setCode("S3");
+        sample3.setProperty(propertyCode, "DUMMY_S3");
+        sample3.setFetchOptions(fo);
+
+        List<Sample> samples = new ArrayList<Sample>();
+        samples.add(sample1);
+        samples.add(sample2);
+        samples.add(sample3);
+
+        Collection<Sample> results = new SortAndPage().sortAndPage(samples, c, fo);
+
+        assertEquals(results, list(sample2, sample1, sample3));
+    }
+	
+	@Test
+    public void testFetchedFieldsScore_PropertyScore()
+    {
+		SampleType sampleTypeA = new SampleType();
+		sampleTypeA.setCode("DUMMY_CODE_A");
+		
+		SampleType sampleTypeB = new SampleType();
+		sampleTypeB.setCode("DUMMY_CODE_B");
+		
+		String propertyCode = "DUMMY_PROPERTY";
+		
+		SampleSearchCriteria c = new SampleSearchCriteria();
+		c.withOrOperator();
+        c.withProperty(propertyCode).thatEquals("DUMMY_S3");
+        
+		SampleFetchOptions fo = new SampleFetchOptions();
+		fo.withType();
+		fo.withProperties();
+		fo.sortBy().fetchedFieldsScore();
+		
+        Sample sample1 = new Sample();
+        sample1.setType(sampleTypeA);
+        sample1.setCode("S1");
+        sample1.setProperty(propertyCode, "DUMMY_S1");
+        sample1.setFetchOptions(fo);
+
+        Sample sample2 = new Sample();
+        sample2.setType(sampleTypeB);
+        sample2.setCode("S2");
+        sample2.setProperty(propertyCode, "DUMMY_S2");
+        sample2.setFetchOptions(fo);
+
+        Sample sample3 = new Sample();
+        sample3.setType(sampleTypeA);
+        sample3.setCode("S3");
+        sample3.setProperty(propertyCode, "DUMMY_S3");
+        sample3.setFetchOptions(fo);
+
+        List<Sample> samples = new ArrayList<Sample>();
+        samples.add(sample1);
+        samples.add(sample2);
+        samples.add(sample3);
+
+        Collection<Sample> results = new SortAndPage().sortAndPage(samples, c, fo);
+
+        assertEquals(results, list(sample3, sample1, sample2));
+    }
+	
+	@Test
+    public void testFetchedFieldsScore_TypeScore()
+    {
+		SampleType sampleTypeA = new SampleType();
+		sampleTypeA.setCode("DUMMY_CODE_A");
+		
+		SampleType sampleTypeB = new SampleType();
+		sampleTypeB.setCode("DUMMY_CODE_B");
+		
+		String propertyCode = "DUMMY_PROPERTY";
+		
+		SampleSearchCriteria c = new SampleSearchCriteria();
+		c.withOrOperator();
+        c.withType().withCode().thatEquals(sampleTypeA.getCode());
+        
+		SampleFetchOptions fo = new SampleFetchOptions();
+		fo.withType();
+		fo.withProperties();
+		fo.sortBy().fetchedFieldsScore();
+		
+        Sample sample1 = new Sample();
+        sample1.setType(sampleTypeA);
+        sample1.setCode("S1");
+        sample1.setProperty(propertyCode, "DUMMY_S1");
+        sample1.setFetchOptions(fo);
+
+        Sample sample2 = new Sample();
+        sample2.setType(sampleTypeB);
+        sample2.setCode("S2");
+        sample2.setProperty(propertyCode, "DUMMY_S2");
+        sample2.setFetchOptions(fo);
+
+        Sample sample3 = new Sample();
+        sample3.setType(sampleTypeA);
+        sample3.setCode("S3");
+        sample3.setProperty(propertyCode, "DUMMY_S3");
+        sample3.setFetchOptions(fo);
+
+        List<Sample> samples = new ArrayList<Sample>();
+        samples.add(sample1);
+        samples.add(sample2);
+        samples.add(sample3);
+
+        Collection<Sample> results = new SortAndPage().sortAndPage(samples, c, fo);
+
+        assertEquals(results, list(sample1, sample3, sample2));
+    }
+	
     @Test
     public void testTopLevel()
     {
+    		MaterialSearchCriteria c = new MaterialSearchCriteria();
+    		
         MaterialFetchOptions fo = new MaterialFetchOptions();
         fo.sortBy().code().desc();
         fo.from(1).count(2);
@@ -48,7 +199,7 @@ public class SortAndPageTest
         materials.add(material2);
         materials.add(material3);
 
-        Collection<Material> results = new SortAndPage().sortAndPage(materials, fo);
+        Collection<Material> results = new SortAndPage().sortAndPage(materials, c, fo);
 
         assertEquals(results, list(material2, material1));
     }
@@ -56,6 +207,8 @@ public class SortAndPageTest
     @Test
     public void testSubLevel()
     {
+    		MaterialSearchCriteria c = new MaterialSearchCriteria();
+    	
         MaterialFetchOptions fo = new MaterialFetchOptions();
         fo.sortBy().code().desc();
         fo.withTags().from(1).count(1);
@@ -81,7 +234,7 @@ public class SortAndPageTest
         materials.add(material1);
         materials.add(material2);
 
-        List<Material> results = new SortAndPage().sortAndPage(materials, fo);
+        List<Material> results = new SortAndPage().sortAndPage(materials, c, fo);
 
         assertEquals(results, list(material2, material1));
         assertEquals(results.get(0).getTags(), set(tag3));
@@ -91,6 +244,8 @@ public class SortAndPageTest
     @Test
     public void testSubLevelThroughSingleRelation()
     {
+    		MaterialSearchCriteria c = new MaterialSearchCriteria();
+    	
         MaterialFetchOptions fo = new MaterialFetchOptions();
         fo.sortBy().code().desc();
         fo.withTags().from(1).count(1);
@@ -149,7 +304,7 @@ public class SortAndPageTest
         materials.add(material1);
         materials.add(material2);
 
-        List<Material> results = new SortAndPage().sortAndPage(materials, fo);
+        List<Material> results = new SortAndPage().sortAndPage(materials, c, fo);
 
         assertEquals(results, list(material2, material1));
 
@@ -168,6 +323,8 @@ public class SortAndPageTest
     @Test
     public void testSortByMultipleFields()
     {
+    		MaterialSearchCriteria c = new MaterialSearchCriteria();
+    	
         MaterialFetchOptions fo = new MaterialFetchOptions();
         fo.sortBy().code().desc();
         fo.sortBy().registrationDate().asc();
@@ -192,7 +349,7 @@ public class SortAndPageTest
         materials.add(material2);
         materials.add(material3);
 
-        List<Material> results = new SortAndPage().sortAndPage(materials, fo);
+        List<Material> results = new SortAndPage().sortAndPage(materials, c, fo);
 
         assertEquals(results.get(0), material3);
         assertEquals(results.get(1), material2);
@@ -202,6 +359,8 @@ public class SortAndPageTest
     @Test
     public void testSamePageMultipleTimes()
     {
+    		MaterialSearchCriteria c = new MaterialSearchCriteria();
+    	
         Tag tag1 = new Tag();
         tag1.setCode("T1");
         Tag tag2 = new Tag();
@@ -227,7 +386,7 @@ public class SortAndPageTest
         materials.add(material1);
         materials.add(material2);
 
-        List<Material> results = new SortAndPage().sortAndPage(materials, fo);
+        List<Material> results = new SortAndPage().sortAndPage(materials, c, fo);
 
         assertEquals(results, list(material2));
         assertEquals(results.get(0).getTags(), set(tag3));
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/EntityWithPropertiesSortOptions.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/EntityWithPropertiesSortOptions.java
index 8084b9e430a493eca9d214b9ea94ca3a269c63b3..ba1a9d6fce9200223cf2c14e8f1438378cfc3b30 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/EntityWithPropertiesSortOptions.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/EntityWithPropertiesSortOptions.java
@@ -16,6 +16,9 @@
 
 package ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions;
 
+import java.util.HashMap;
+import java.util.Map;
+
 import com.fasterxml.jackson.annotation.JsonIgnore;
 
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.ICodeHolder;
@@ -34,13 +37,31 @@ public class EntityWithPropertiesSortOptions<OBJECT extends ICodeHolder & IPermI
 {
 
     private static final long serialVersionUID = 1L;
-
+    
+    @JsonIgnore
+    public static final String FETCHED_FIELDS_SCORE = "FETCHED_FIELDS_SCORE";
+    
     @JsonIgnore
     public static final String TYPE = "TYPE";
 
     @JsonIgnore
     public static final String PROPERTY = "PROPERTY";
-
+    
+    public SortOrder fetchedFieldsScore() {
+    		Map<SortParameter, String> parameters = new HashMap<>();
+    		parameters.put(SortParameter.FULL_CODE_BOOST, 	"1000000");
+    		parameters.put(SortParameter.PARTIAL_CODE_BOOST,  "100000");
+    		parameters.put(SortParameter.FULL_PROPERTY_BOOST,  "10000");
+    		parameters.put(SortParameter.FULL_TYPE_BOOST, 	   "1000");
+    		parameters.put(SortParameter.PARTIAL_PROPERTY_BOOST, "100");
+		return getOrCreateSortingWithParameters(FETCHED_FIELDS_SCORE, parameters);
+    }
+    
+    public SortOrder getFetchedFieldsScore()
+    {
+        return getSorting(FETCHED_FIELDS_SCORE);
+    }
+    
     public SortOrder type()
     {
         return getOrCreateSorting(TYPE);
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/SortOptions.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/SortOptions.java
index 9859cfa1d08140fdfeb14d6dcfa3eec6a16571f8..9c24777923d40bc8120a1ef2d767ede57012a776 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/SortOptions.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/SortOptions.java
@@ -20,6 +20,7 @@ import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 
 import org.apache.commons.lang.StringUtils;
 
@@ -37,12 +38,17 @@ public abstract class SortOptions<OBJECT> implements Serializable
     private List<Sorting> sortings = new LinkedList<>();
 
     protected SortOrder getOrCreateSorting(String field)
+    {
+    		return getOrCreateSortingWithParameters(field, null);
+    }
+    
+    protected SortOrder getOrCreateSortingWithParameters(String field, Map<SortParameter, String> parameters)
     {
         SortOrder order = getSorting(field);
         if (order == null)
         {
             order = new SortOrder();
-            sortings.add(new Sorting(field, order));
+            sortings.add(new Sorting(field, order, parameters));
         }
         return order;
     }
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/SortParameter.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/SortParameter.java
new file mode 100644
index 0000000000000000000000000000000000000000..232fb2678e665aaa58036d85646fb7a7e2b5ced0
--- /dev/null
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/SortParameter.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2015 ETH Zuerich, CISD
+ *
+ * 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 ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions;
+
+import ch.systemsx.cisd.base.annotation.JsonObject;
+
+/**
+ * @author juanf
+ */
+@JsonObject("as.dto.common.fetchoptions.SortParameter")
+public enum SortParameter
+{
+
+    FULL_CODE_BOOST, PARTIAL_CODE_BOOST, FULL_TYPE_BOOST, FULL_PROPERTY_BOOST, PARTIAL_PROPERTY_BOOST
+
+}
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/Sorting.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/Sorting.java
index 4f819b261acb16fe341ddadf27bcf567392149cd..11b10ccff584584854bc8fd5ea82bb4c29281843 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/Sorting.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/fetchoptions/Sorting.java
@@ -17,6 +17,7 @@
 package ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions;
 
 import java.io.Serializable;
+import java.util.Map;
 
 import ch.systemsx.cisd.base.annotation.JsonObject;
 
@@ -32,16 +33,24 @@ public class Sorting implements Serializable
     private String field;
 
     private SortOrder order;
-
+    
+    private Map<SortParameter, String> parameters;
+    
     @SuppressWarnings("unused")
     private Sorting()
     {
     }
 
     public Sorting(String field, SortOrder order)
+    {
+        this(field, order, null);
+    }
+    
+    public Sorting(String field, SortOrder order, Map<SortParameter, String> parameters)
     {
         this.field = field;
         this.order = order;
+        this.parameters = parameters;
     }
 
     public String getField()
@@ -53,6 +62,10 @@ public class Sorting implements Serializable
     {
         return order;
     }
+    
+    public Map<SortParameter, String> getParameters() {
+    		return parameters;
+    }
 
     @Override
     public String toString()
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/eln-lims-api/script.py b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/eln-lims-api/script.py
index 23d486ee42c65a7da2523da9d92fdd9f306d9015..cb427d19f409c5b860b418b9ecbd6a382c138044 100644
--- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/eln-lims-api/script.py
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/dss/reporting-plugins/eln-lims-api/script.py
@@ -362,9 +362,23 @@ def getFeaturesFromFeatureVector(tr, parameters, tableBuilder):
 
 def getDiskSpace(tr, parameters, tableBuilder):
 	storerootDir = getConfigParameterAsString("storeroot-dir")
-	df = subprocess.check_output(["df", '-h', storerootDir])
-	diskSpaceValues = extractDiskSpaceValues(df)
-	return getJsonForData([diskSpaceValues])
+	diskSpaceValues = []
+	diskSpaceValues.append(getDiskSpaceForDirectory(storerootDir))
+	# The storeroot-dir might contain symlinks to different volumes.
+	# So we want to resolve them and show all relevant mount points.
+	findLinks = subprocess.check_output(["find", storerootDir, "-type", "l"])
+	mountPoints = []
+	for symlink in findLinks.splitlines():
+		linkPath = os.path.realpath(symlink)
+		if os.path.exists(linkPath):
+			diskSpaceForDir = getDiskSpaceForDirectory(linkPath)
+			if diskSpaceForDir not in diskSpaceValues:
+				diskSpaceValues.append(diskSpaceForDir)
+	return getJsonForData(diskSpaceValues)
+
+def getDiskSpaceForDirectory(dir):
+	df = subprocess.check_output(["df", '-h', dir])
+	return extractDiskSpaceValues(df)
 
 def extractDiskSpaceValues(df):
 	values = {}