diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/ListSamplesOriginalDataProvider.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/ListSamplesOriginalDataProvider.java
index c7ecf7a1b7d2557f9b4429a6814b8d639f819c2c..cb9d71fc0d766c218c41b05fa3ba690e1051707f 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/ListSamplesOriginalDataProvider.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/ListSamplesOriginalDataProvider.java
@@ -1,10 +1,12 @@
 package ch.systemsx.cisd.openbis.generic.client.web.server;
 
+import java.util.Collections;
 import java.util.List;
 
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListSampleDisplayCriteria;
 import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.IOriginalDataProvider;
 import ch.systemsx.cisd.openbis.generic.shared.ICommonServer;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchSubCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
 
 /**
@@ -37,7 +39,8 @@ final class ListSamplesOriginalDataProvider extends AbstractOriginalDataProvider
             case BROWSE:
                 return commonServer.listSamples(sessionToken, criteria.getBrowseCriteria());
             case SEARCH:
-                return commonServer.searchForSamples(sessionToken, criteria.getSearchCriteria());
+                return commonServer.searchForSamples(sessionToken, criteria.getSearchCriteria(),
+                        Collections.<DetailedSearchSubCriteria> emptyList());
         }
         return null; // not possible
     }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/SampleProvider.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/SampleProvider.java
index 767c6ae91b1c26ca907b668346863956b5f7c9c0..20e39ca47a86d98581ccde8ab3352c5ccc029681 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/SampleProvider.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/SampleProvider.java
@@ -34,6 +34,7 @@ import static ch.systemsx.cisd.openbis.generic.client.web.client.dto.SampleGridC
 import static ch.systemsx.cisd.openbis.generic.client.web.client.dto.SampleGridColumnIDs.SPACE;
 import static ch.systemsx.cisd.openbis.generic.client.web.client.dto.SampleGridColumnIDs.SUBCODE;
 
+import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 
@@ -43,6 +44,7 @@ import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListSampleDisplayC
 import ch.systemsx.cisd.openbis.generic.shared.ICommonServer;
 import ch.systemsx.cisd.openbis.generic.shared.basic.SimpleYesNoRenderer;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseInstance;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchSubCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Experiment;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleType;
@@ -50,17 +52,18 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.TypedTableModel;
 import ch.systemsx.cisd.openbis.generic.shared.util.TypedTableModelBuilder;
 
 /**
- * 
- *
  * @author Franz-Josef Elmer
  */
 public class SampleProvider extends AbstractCommonTableModelProvider<Sample>
 {
     private static final String PROPERTIES_GROUP = "property-";
+
     private static final int MAX_PARENTS = 4;
+
     private final ListSampleDisplayCriteria2 criteria;
 
-    public SampleProvider(ICommonServer commonServer, String sessionToken, ListSampleDisplayCriteria2 criteria)
+    public SampleProvider(ICommonServer commonServer, String sessionToken,
+            ListSampleDisplayCriteria2 criteria)
     {
         super(commonServer, sessionToken);
         this.criteria = criteria;
@@ -95,11 +98,14 @@ public class SampleProvider extends AbstractCommonTableModelProvider<Sample>
             builder.column(CODE).addString(sample.getCode());
             builder.column(SUBCODE).addString(sample.getSubCode());
             builder.column(DATABASE_INSTANCE).addString(getDatabaseInstance(sample).getCode());
-            builder.column(SPACE).addString(sample.getSpace() == null ? "" : sample.getSpace().getCode());
+            builder.column(SPACE).addString(
+                    sample.getSpace() == null ? "" : sample.getSpace().getCode());
             builder.column(SAMPLE_IDENTIFIER).addString(sample.getIdentifier());
             builder.column(SAMPLE_TYPE).addString(sample.getSampleType().getCode());
-            builder.column(IS_INSTANCE_SAMPLE).addString(SimpleYesNoRenderer.render(sample.getDatabaseInstance() != null));
-            builder.column(IS_INVALID).addString(SimpleYesNoRenderer.render(sample.getInvalidation() != null));
+            builder.column(IS_INSTANCE_SAMPLE).addString(
+                    SimpleYesNoRenderer.render(sample.getDatabaseInstance() != null));
+            builder.column(IS_INVALID).addString(
+                    SimpleYesNoRenderer.render(sample.getInvalidation() != null));
             builder.column(REGISTRATOR).addPerson(sample.getRegistrator());
             builder.column(REGISTRATION_DATE).addDate(sample.getRegistrationDate());
             builder.column(EXPERIMENT).addString(getExperimentCode(sample));
@@ -122,13 +128,15 @@ public class SampleProvider extends AbstractCommonTableModelProvider<Sample>
     protected TableMap<String, SampleType> getSampleTypes()
     {
         List<SampleType> sampleTypes = commonServer.listSampleTypes(sessionToken);
-        TableMap<String, SampleType> sampleTypMap = new TableMap<String, SampleType>(sampleTypes, new IKeyExtractor<String, SampleType>()
-            {
-                public String getKey(SampleType e)
-                {
-                    return e.getCode();
-                }
-            });
+        TableMap<String, SampleType> sampleTypMap =
+                new TableMap<String, SampleType>(sampleTypes,
+                        new IKeyExtractor<String, SampleType>()
+                            {
+                                public String getKey(SampleType e)
+                                {
+                                    return e.getCode();
+                                }
+                            });
         return sampleTypMap;
     }
 
@@ -160,7 +168,7 @@ public class SampleProvider extends AbstractCommonTableModelProvider<Sample>
         Sample container = sample.getContainer();
         return container == null ? "" : container.getIdentifier();
     }
-    
+
     private String getProjectCode(Sample sample)
     {
         Experiment experiment = sample.getExperiment();
@@ -172,13 +180,13 @@ public class SampleProvider extends AbstractCommonTableModelProvider<Sample>
         Experiment experiment = sample.getExperiment();
         return experiment == null ? "" : experiment.getCode();
     }
-    
+
     private String getExperimentIdentifier(Sample sample)
     {
         Experiment experiment = sample.getExperiment();
         return experiment == null ? "" : experiment.getIdentifier();
     }
-    
+
     private DatabaseInstance getDatabaseInstance(Sample sample)
     {
         DatabaseInstance databaseInstance = sample.getDatabaseInstance();
@@ -196,7 +204,8 @@ public class SampleProvider extends AbstractCommonTableModelProvider<Sample>
             case BROWSE:
                 return commonServer.listSamples(sessionToken, criteria.getBrowseCriteria());
             case SEARCH:
-                return commonServer.searchForSamples(sessionToken, criteria.getSearchCriteria());
+                return commonServer.searchForSamples(sessionToken, criteria.getSearchCriteria(),
+                        Collections.<DetailedSearchSubCriteria> emptyList());
         }
         return null; // not possible
     }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
index 1a4d3fc1c0a17824a44e16f52620677a1465afa3..945e7b6b2a0522e642da3160ff77cf2cb1baabbd 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServer.java
@@ -96,7 +96,9 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataStoreServiceKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatastoreServiceDescription;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DeletedDataSet;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchAssociationCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchSubCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DynamicPropertyEvaluationInfo;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityType;
@@ -478,10 +480,23 @@ public final class CommonServer extends AbstractCommonServer<ICommonServerForInt
         final Session session = getSession(sessionToken);
         try
         {
-            IHibernateSearchDAO searchDAO = getDAOFactory().getHibernateSearchDAO();
             final Collection<Long> sampleIds =
-                    searchDAO.searchForEntityIds(criteria,
-                            DtoConverters.convertEntityKind(EntityKind.SAMPLE));
+                    findSampleIds(criteria, Collections.<DetailedSearchSubCriteria> emptyList());
+            final ISampleLister sampleLister = businessObjectFactory.createSampleLister(session);
+            return sampleLister.list(new ListOrSearchSampleCriteria(sampleIds));
+        } catch (final DataAccessException ex)
+        {
+            throw createUserFailureException(ex);
+        }
+    }
+
+    public List<Sample> searchForSamples(String sessionToken, DetailedSearchCriteria criteria,
+            List<DetailedSearchSubCriteria> subCriterias)
+    {
+        final Session session = getSession(sessionToken);
+        try
+        {
+            final Collection<Long> sampleIds = findSampleIds(criteria, subCriterias);
             final ISampleLister sampleLister = businessObjectFactory.createSampleLister(session);
             return sampleLister.list(new ListOrSearchSampleCriteria(sampleIds));
         } catch (final DataAccessException ex)
@@ -490,6 +505,45 @@ public final class CommonServer extends AbstractCommonServer<ICommonServerForInt
         }
     }
 
+    private Collection<Long> findSampleIds(DetailedSearchCriteria criteria,
+            List<DetailedSearchSubCriteria> subCriterias)
+    {
+        final IHibernateSearchDAO searchDAO = getDAOFactory().getHibernateSearchDAO();
+        // for now we connect all sub criteria with logical AND
+        List<DetailedSearchAssociationCriteria> associations =
+                new ArrayList<DetailedSearchAssociationCriteria>();
+        for (DetailedSearchSubCriteria subCriteria : subCriterias)
+        {
+            associations.add(findAssociatedEntities(subCriteria));
+        }
+        final Collection<Long> sampleIds =
+                searchDAO.searchForEntityIds(criteria,
+                        DtoConverters.convertEntityKind(EntityKind.SAMPLE), associations);
+        return sampleIds;
+    }
+
+    private DetailedSearchAssociationCriteria findAssociatedEntities(
+            DetailedSearchSubCriteria subCriteria)
+    {
+        final IHibernateSearchDAO searchDAO = getDAOFactory().getHibernateSearchDAO();
+        // for now we don't support sub criteria of sub criteria
+        List<DetailedSearchAssociationCriteria> associations = Collections.emptyList();
+
+        final Collection<Long> associatedIds =
+                searchDAO.searchForEntityIds(subCriteria.getCriteria(),
+                        convertToEntityKind(subCriteria.getTargetEntityKind()), associations);
+
+        return new DetailedSearchAssociationCriteria(subCriteria.getTargetEntityKind(),
+                associatedIds);
+    }
+
+    private static final ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind convertToEntityKind(
+            final SearchableEntity searchableEntity)
+    {
+        return ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind
+                .valueOf(searchableEntity.name());
+    }
+
     public final List<ExternalData> listSampleExternalData(final String sessionToken,
             final TechId sampleId, final boolean showOnlyDirectlyConnected)
     {
@@ -852,7 +906,8 @@ public final class CommonServer extends AbstractCommonServer<ICommonServerForInt
 
             final Collection<Long> datasetIds =
                     searchDAO.searchForEntityIds(criteria,
-                            DtoConverters.convertEntityKind(EntityKind.DATA_SET));
+                            DtoConverters.convertEntityKind(EntityKind.DATA_SET),
+                            Collections.<DetailedSearchAssociationCriteria> emptyList());
             final IDatasetLister datasetLister = createDatasetLister(session);
             return datasetLister.listByDatasetIds(datasetIds);
         } catch (final DataAccessException ex)
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServerLogger.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServerLogger.java
index c6308a167e392b917ef5767949c8c99c09fca134..0072b84d272ccc8940dfb6289c343ee4dc4bd5ad 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServerLogger.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/CommonServerLogger.java
@@ -41,6 +41,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataStoreServiceKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatastoreServiceDescription;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchSubCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DynamicPropertyEvaluationInfo;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityType;
@@ -444,7 +445,15 @@ final class CommonServerLogger extends AbstractServerLogger implements ICommonSe
 
     public List<Sample> searchForSamples(String sessionToken, DetailedSearchCriteria criteria)
     {
-        logAccess(sessionToken, "search_for_samples");
+        logAccess(sessionToken, "search_for_samples", "criteria(%s)", criteria);
+        return null;
+    }
+
+    public List<Sample> searchForSamples(String sessionToken, DetailedSearchCriteria criteria,
+            List<DetailedSearchSubCriteria> subCriterias)
+    {
+        logAccess(sessionToken, "search_for_samples", "criteria(%s) subCriterias(%s)", criteria,
+                subCriterias);
         return null;
     }
 
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationService.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationService.java
index f0f39a3a9c60c64dd03e68a51c18396579ae6f49..e4c799f70a501df03a65f5270208ac4d755ac0a1 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationService.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationService.java
@@ -48,10 +48,13 @@ import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Project;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Role;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Sample;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchSubCriteria;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchableEntityKind;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SpaceWithProjectsAndRoleAssignments;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetRelatedEntities;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchSubCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExperimentType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExternalData;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ListSampleCriteria;
@@ -232,13 +235,22 @@ public class GeneralInformationService extends AbstractServer<IGeneralInformatio
     public List<Sample> searchForSamples(String sessionToken, SearchCriteria searchCriteria)
     {
         checkSession(sessionToken);
+
         DetailedSearchCriteria detailedSearchCriteria =
-                new SearchCriteriaToDetailedSearchCriteriaTranslator(
-                        searchCriteria,
-                        new SearchCriteriaToDetailedSearchCriteriaTranslator.SampleAttributeTranslator())
-                        .convertToDetailedSearchCriteria();
+                SearchCriteriaToDetailedSearchCriteriaTranslator.convertToDetailedSearchCriteria(
+                        SearchableEntityKind.SAMPLE, searchCriteria);
+        List<DetailedSearchSubCriteria> detailedSearchSubCriterias =
+                new ArrayList<DetailedSearchSubCriteria>();
+        for (SearchSubCriteria subCriteria : searchCriteria.getSubCriterias())
+        {
+            DetailedSearchSubCriteria detailedSearchSubCriteria =
+                    SearchCriteriaToDetailedSearchCriteriaTranslator
+                            .convertToDetailedSearchSubCriteria(subCriteria);
+            detailedSearchSubCriterias.add(detailedSearchSubCriteria);
+        }
         List<ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample> privateSamples =
-                commonServer.searchForSamples(sessionToken, detailedSearchCriteria);
+                commonServer.searchForSamples(sessionToken, detailedSearchCriteria,
+                        detailedSearchSubCriterias);
         ArrayList<Sample> samples = new ArrayList<Sample>();
         for (ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample privateSample : privateSamples)
         {
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/SearchCriteriaToDetailedSearchCriteriaTranslator.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/SearchCriteriaToDetailedSearchCriteriaTranslator.java
index c418dcb061ef4c79a52f7b10c208c901f76dc064..55cb3842e38907424906a086f9bcf3cb74f1e17d 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/SearchCriteriaToDetailedSearchCriteriaTranslator.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/api/v1/SearchCriteriaToDetailedSearchCriteriaTranslator.java
@@ -17,19 +17,26 @@
 package ch.systemsx.cisd.openbis.generic.server.api.v1;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.AttributeMatchClause;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.MatchClause;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.MatchClauseAttribute;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.PropertyMatchClause;
-import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.MatchClause;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchSubCriteria;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchableEntityKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriterion;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchField;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchSubCriteria;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExperimentAttributeSearchFieldKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.IAttributeSearchFieldKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleAttributeSearchFieldKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SearchCriteriaConnection;
+import ch.systemsx.cisd.openbis.generic.shared.dto.SearchableEntity;
 
 /**
  * Converts {@link SearchCriteria} objects to {@link DetailedSearchCriteria} objects.
@@ -41,6 +48,42 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SearchCriteriaConnectio
  */
 class SearchCriteriaToDetailedSearchCriteriaTranslator
 {
+    private final static String INVALID_SEARCH_ATTRIBUTE_TEMPLATE =
+            "%s is not a valid search attribute for %s";
+
+    private static Map<SearchableEntityKind, IMatchClauseAttributeTranslator> translators =
+            new HashMap<SearchableEntityKind, IMatchClauseAttributeTranslator>();
+
+    static
+    {
+        translators.put(SearchableEntityKind.SAMPLE, new SampleAttributeTranslator());
+        translators.put(SearchableEntityKind.EXPERIMENT, new ExperimentAttributeTranslator());
+    }
+
+    private static IMatchClauseAttributeTranslator translatorFor(SearchableEntityKind entityKind)
+    {
+        return translators.get(entityKind);
+    }
+
+    private static void throwInvalidSearchAttributeException(MatchClauseAttribute attribute,
+            SearchableEntityKind entityKind) throws UnsupportedOperationException
+    {
+        throw new UnsupportedOperationException(String.format(INVALID_SEARCH_ATTRIBUTE_TEMPLATE,
+                attribute, entityKind));
+    }
+
+    private static SearchableEntity translateEntityKind(SearchableEntityKind entityKind)
+    {
+        switch (entityKind)
+        {
+            case SAMPLE:
+                return SearchableEntity.SAMPLE;
+            case EXPERIMENT:
+                return SearchableEntity.EXPERIMENT;
+        }
+        return null; // can't happen
+    }
+
     private final SearchCriteria searchCriteria;
 
     private final IMatchClauseAttributeTranslator attributeTranslator;
@@ -64,7 +107,7 @@ class SearchCriteriaToDetailedSearchCriteriaTranslator
         public IAttributeSearchFieldKind convertMatchClauseAttributeToAttributeSearchFieldKind(
                 MatchClauseAttribute attribute)
         {
-            IAttributeSearchFieldKind ans;
+            final IAttributeSearchFieldKind ans;
             switch (attribute)
             {
                 case CODE:
@@ -73,16 +116,71 @@ class SearchCriteriaToDetailedSearchCriteriaTranslator
                 case TYPE:
                     ans = SampleAttributeSearchFieldKind.SAMPLE_TYPE;
                     break;
+                case SPACE:
+                    ans = SampleAttributeSearchFieldKind.SPACE;
+                    break;
                 default:
-                    // Should never get here
-                    ans = null;
+                    throwInvalidSearchAttributeException(attribute, SearchableEntityKind.SAMPLE);
+                    ans = null; // for Eclipse
+            }
+            return ans;
+        }
+
+    }
+
+    public static class ExperimentAttributeTranslator implements IMatchClauseAttributeTranslator
+    {
+        public IAttributeSearchFieldKind convertMatchClauseAttributeToAttributeSearchFieldKind(
+                MatchClauseAttribute attribute)
+        {
+            final IAttributeSearchFieldKind ans;
+            switch (attribute)
+            {
+                case CODE:
+                    ans = ExperimentAttributeSearchFieldKind.CODE;
+                    break;
+                case TYPE:
+                    ans = ExperimentAttributeSearchFieldKind.EXPERIMENT_TYPE;
+                    break;
+                case SPACE:
+                    ans = ExperimentAttributeSearchFieldKind.PROJECT_SPACE;
+                    break;
+                case PROJECT:
+                    ans = ExperimentAttributeSearchFieldKind.PROJECT;
                     break;
+                default:
+                    throwInvalidSearchAttributeException(attribute, SearchableEntityKind.SAMPLE);
+                    ans = null; // for Eclipse
             }
             return ans;
         }
 
     }
 
+    public static DetailedSearchCriteria convertToDetailedSearchCriteria(
+            SearchableEntityKind entityKind, SearchCriteria criteria)
+    {
+        return new SearchCriteriaToDetailedSearchCriteriaTranslator(criteria, entityKind)
+                .convertToDetailedSearchCriteria();
+    }
+
+    public static DetailedSearchSubCriteria convertToDetailedSearchSubCriteria(
+            SearchSubCriteria subCriteria)
+    {
+        final SearchCriteria criteria = subCriteria.getCriteria();
+        final SearchableEntityKind targetEntityKind = subCriteria.getTargetEntityKind();
+        final DetailedSearchCriteria detailedSearchCriteria =
+                convertToDetailedSearchCriteria(targetEntityKind, criteria);
+        return new DetailedSearchSubCriteria(translateEntityKind(targetEntityKind),
+                detailedSearchCriteria);
+    }
+
+    public SearchCriteriaToDetailedSearchCriteriaTranslator(SearchCriteria searchCriteria,
+            SearchableEntityKind entityKind)
+    {
+        this(searchCriteria, translatorFor(entityKind));
+    }
+
     public SearchCriteriaToDetailedSearchCriteriaTranslator(SearchCriteria searchCriteria,
             IMatchClauseAttributeTranslator attributeTranslator)
     {
@@ -119,8 +217,8 @@ class SearchCriteriaToDetailedSearchCriteriaTranslator
             MatchClause matchClause)
     {
         DetailedSearchCriterion detailedSearchCriterion =
-                new DetailedSearchCriterion(extractDetailedSearchField(matchClause), matchClause
-                        .getDesiredValue());
+                new DetailedSearchCriterion(extractDetailedSearchField(matchClause),
+                        matchClause.getDesiredValue());
         return detailedSearchCriterion;
     }
 
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IHibernateSearchDAO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IHibernateSearchDAO.java
index afd801ea7fb69d6da8c411784e3fc6151ff26366..ae92be25b8b47b8d073ed37eea8d940bb2ec2019 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IHibernateSearchDAO.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/IHibernateSearchDAO.java
@@ -21,6 +21,7 @@ import java.util.List;
 import org.springframework.dao.DataAccessException;
 
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.db.HibernateSearchDataProvider;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchAssociationCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MatchingEntity;
 import ch.systemsx.cisd.openbis.generic.shared.dto.SearchableEntity;
@@ -38,12 +39,14 @@ public interface IHibernateSearchDAO
      * using all the indexed fields.
      * 
      * @param searchTerm could be something like "<code>C11 AND System User</code>".
-     * @param maxSize Maximum number of entries. 
+     * @param maxSize Maximum number of entries.
      */
     public List<MatchingEntity> searchEntitiesByTerm(final SearchableEntity searchableEntity,
             final String searchTerm, final HibernateSearchDataProvider dataProvider,
-            boolean useWildcardSearchMode, int alreadyFoundEntities, int maxSize) throws DataAccessException;
+            boolean useWildcardSearchMode, int alreadyFoundEntities, int maxSize)
+            throws DataAccessException;
 
     /** search for entity ids using the specified criteria */
-    public List<Long> searchForEntityIds(DetailedSearchCriteria criteria, EntityKind entityKind);
+    public List<Long> searchForEntityIds(DetailedSearchCriteria criteria, EntityKind entityKind,
+            List<DetailedSearchAssociationCriteria> associationCriterias);
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/HibernateSearchDAO.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/HibernateSearchDAO.java
index 9e57a893bdc76f95ac4fb6a75c9460c4f482ccc0..6af50dd4f3cdd124bd0b0fe01cd7cb70ca1f325c 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/HibernateSearchDAO.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/HibernateSearchDAO.java
@@ -61,6 +61,7 @@ import ch.systemsx.cisd.openbis.generic.server.dataaccess.IHibernateSearchDAO;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.db.search.HibernateSearchContext;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.db.search.LuceneQueryBuilder;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.BasicEntityType;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchAssociationCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MatchingEntity;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Person;
@@ -119,7 +120,8 @@ final class HibernateSearchDAO extends HibernateDaoSupport implements IHibernate
                                 throws HibernateException, SQLException
                         {
                             return doSearchEntitiesByTerm(session, searchableEntity, searchTerm,
-                                    dataProvider, useWildcardSearchMode, alreadyFoundEntities, maxSize);
+                                    dataProvider, useWildcardSearchMode, alreadyFoundEntities,
+                                    maxSize);
                         }
                     }));
         if (operationLog.isDebugEnabled())
@@ -212,7 +214,7 @@ final class HibernateSearchDAO extends HibernateDaoSupport implements IHibernate
     // detailed search
 
     public List<Long> searchForEntityIds(final DetailedSearchCriteria criteria,
-            final EntityKind entityKind)
+            final EntityKind entityKind, final List<DetailedSearchAssociationCriteria> associations)
     {
         final List<Long> list =
                 AbstractDAO.cast((List<?>) getHibernateTemplate().execute(new HibernateCallback()
@@ -220,7 +222,7 @@ final class HibernateSearchDAO extends HibernateDaoSupport implements IHibernate
                         public final Object doInHibernate(final Session session)
                                 throws HibernateException, SQLException
                         {
-                            return searchForEntityIds(session, criteria, entityKind);
+                            return searchForEntityIds(session, criteria, entityKind, associations);
                         }
                     }));
         if (operationLog.isDebugEnabled())
@@ -238,9 +240,11 @@ final class HibernateSearchDAO extends HibernateDaoSupport implements IHibernate
      * Takes data only from Lucene index without hitting DB.
      */
     private List<Long> searchForEntityIds(Session session, DetailedSearchCriteria searchCriteria,
-            EntityKind entityKind)
+            EntityKind entityKind, List<DetailedSearchAssociationCriteria> associations)
     {
-        Query query = LuceneQueryBuilder.createDetailedSearchQuery(searchCriteria, entityKind);
+        Query query =
+                LuceneQueryBuilder.createDetailedSearchQuery(searchCriteria, associations,
+                        entityKind);
         final FullTextSession fullTextSession = Search.getFullTextSession(session);
         final FullTextQuery hibernateQuery =
                 fullTextSession.createFullTextQuery(query, entityKind.getEntityClass());
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/search/LuceneQueryBuilder.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/search/LuceneQueryBuilder.java
index 64459ba08ebe7391000d8997537c319a276db3e7..c0192ec7218bfd122c719151346f6aa7e6b366bb 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/search/LuceneQueryBuilder.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/search/LuceneQueryBuilder.java
@@ -30,6 +30,7 @@ import org.apache.lucene.search.Query;
 
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.db.search.detailed.DetailedQueryBuilder;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchAssociationCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind;
 import ch.systemsx.cisd.openbis.generic.shared.translator.DtoConverters;
@@ -49,12 +50,14 @@ public class LuceneQueryBuilder
 
     private static final char SPACE = ' ';
 
-    /** @throws UserFailureException when some search patterns are incorrect */
+    /**
+     * @throws UserFailureException when some search patterns are incorrect
+     */
     public static Query createDetailedSearchQuery(DetailedSearchCriteria searchCriteria,
-            EntityKind entityKind)
+            List<DetailedSearchAssociationCriteria> associations, EntityKind entityKind)
     {
         return DetailedQueryBuilder.createQuery(searchCriteria,
-                DtoConverters.convertEntityKind(entityKind));
+                DtoConverters.convertEntityKind(entityKind), associations);
     }
 
     private static final char FIELD_SEPARATOR = ':';
@@ -179,6 +182,19 @@ public class LuceneQueryBuilder
         return resultQuery;
     }
 
+    // creates a query where given field matches any of the given patterns
+    public static Query parseQuery(final String fieldName, final List<String> searchPatterns,
+            Analyzer analyzer) throws UserFailureException
+    {
+        BooleanQuery resultQuery = new BooleanQuery();
+        for (String searchPattern : searchPatterns)
+        {
+            Query query = parseQuery(fieldName, searchPattern, analyzer);
+            resultQuery.add(query, Occur.SHOULD);
+        }
+        return resultQuery;
+    }
+
     private static Query parseQuery(final String searchPattern, String wholeQuery,
             final QueryParser parser)
     {
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/search/detailed/DetailedQueryBuilder.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/search/detailed/DetailedQueryBuilder.java
index 8ead8f710589abe056dde3a11984434324371dfb..3ce6140bb0b60b76249db5c332e8c0c8bc3f6337 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/search/detailed/DetailedQueryBuilder.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/search/detailed/DetailedQueryBuilder.java
@@ -23,9 +23,9 @@ import java.util.List;
 
 import org.apache.log4j.Logger;
 import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.search.BooleanClause.Occur;
 import org.apache.lucene.search.BooleanQuery;
 import org.apache.lucene.search.Query;
-import org.apache.lucene.search.BooleanClause.Occur;
 
 import ch.systemsx.cisd.common.exceptions.InternalErr;
 import ch.systemsx.cisd.common.exceptions.UserFailureException;
@@ -33,6 +33,7 @@ import ch.systemsx.cisd.common.logging.LogCategory;
 import ch.systemsx.cisd.common.logging.LogFactory;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.db.search.LuceneQueryBuilder;
 import ch.systemsx.cisd.openbis.generic.shared.basic.AttributeSearchFieldKindProvider;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchAssociationCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriterion;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchField;
@@ -49,15 +50,18 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.hibernate.SearchFieldConstant
  */
 public class DetailedQueryBuilder
 {
-    private final static Logger operationLog =
-            LogFactory.getLogger(LogCategory.OPERATION, DetailedQueryBuilder.class);
-
-    /** @throws UserFailureException when some search patterns are incorrect */
-    public static Query createQuery(DetailedSearchCriteria searchCriteria, EntityKind entityKind)
-            throws UserFailureException
+    private final static Logger operationLog = LogFactory.getLogger(LogCategory.OPERATION,
+            DetailedQueryBuilder.class);
+
+    /**
+     * @param associations // TODO move to DetailedSearchCriteria
+     * @throws UserFailureException when some search patterns are incorrect
+     */
+    public static Query createQuery(DetailedSearchCriteria searchCriteria, EntityKind entityKind,
+            List<DetailedSearchAssociationCriteria> associations) throws UserFailureException
     {
         final DetailedQueryBuilder builder = new DetailedQueryBuilder(entityKind);
-        final Query resultQuery = builder.createQuery(searchCriteria);
+        final Query resultQuery = builder.createQuery(searchCriteria, associations);
         operationLog.debug("Lucene detailed query: " + resultQuery.toString());
         return resultQuery;
     }
@@ -73,7 +77,8 @@ public class DetailedQueryBuilder
         this.entityKind = entityKind;
     }
 
-    private Query createQuery(DetailedSearchCriteria searchCriteria)
+    private Query createQuery(DetailedSearchCriteria searchCriteria,
+            List<DetailedSearchAssociationCriteria> associations)
     {
         boolean useWildcardSearchMode = searchCriteria.isUseWildcardSearchMode();
         List<DetailedSearchCriterion> criteria = searchCriteria.getCriteria();
@@ -89,9 +94,26 @@ public class DetailedQueryBuilder
             Query luceneQuery = LuceneQueryBuilder.parseQuery(fieldNames, searchPattern, analyzer);
             resultQuery.add(luceneQuery, occureCondition);
         }
+        for (DetailedSearchAssociationCriteria association : associations)
+        {
+            String fieldName = getIndexFieldName(association);
+            List<String> searchPatterns = extractAssociationPatterns(association);
+            Query luceneQuery = LuceneQueryBuilder.parseQuery(fieldName, searchPatterns, analyzer);
+            resultQuery.add(luceneQuery, occureCondition);
+        }
         return resultQuery;
     }
 
+    private List<String> extractAssociationPatterns(DetailedSearchAssociationCriteria association)
+    {
+        List<String> result = new ArrayList<String>();
+        for (Long id : association.getIds())
+        {
+            result.add(id.toString());
+        }
+        return result;
+    }
+
     private Occur createOccureCondition(SearchCriteriaConnection connection)
     {
         switch (connection)
@@ -125,8 +147,14 @@ public class DetailedQueryBuilder
         }
     }
 
-    private final static EnumSet<DetailedSearchFieldKind> simpleFieldKinds =
-            EnumSet.of(DetailedSearchFieldKind.ATTRIBUTE, DetailedSearchFieldKind.PROPERTY);
+    private String getIndexFieldName(DetailedSearchAssociationCriteria association)
+    {
+        return IndexFieldNameHelper.getAssociationIndexField(entityKind,
+                association.getEntityKind());
+    }
+
+    private final static EnumSet<DetailedSearchFieldKind> simpleFieldKinds = EnumSet.of(
+            DetailedSearchFieldKind.ATTRIBUTE, DetailedSearchFieldKind.PROPERTY);
 
     private String getSimpleFieldIndexName(DetailedSearchField searchField)
     {
@@ -182,8 +210,8 @@ public class DetailedQueryBuilder
         switch (fieldKind)
         {
             case ATTRIBUTE:
-                return IndexFieldNameHelper.getAttributeIndexField(entityKind, searchField
-                        .getAttributeCode());
+                return IndexFieldNameHelper.getAttributeIndexField(entityKind,
+                        searchField.getAttributeCode());
             case PROPERTY:
                 return IndexFieldNameHelper.getPropertyIndexField(searchField.getPropertyCode());
             case ANY_PROPERTY:
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/search/detailed/IndexFieldNameHelper.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/search/detailed/IndexFieldNameHelper.java
index f28eae16e1c5ef7cf0d89523b83bccebe14ef9ec..d5587230ffbea39d77d60f788465b3602e3272b1 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/search/detailed/IndexFieldNameHelper.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/search/detailed/IndexFieldNameHelper.java
@@ -18,11 +18,13 @@ package ch.systemsx.cisd.openbis.generic.server.dataaccess.db.search.detailed;
 
 import static ch.systemsx.cisd.openbis.generic.shared.dto.hibernate.SearchFieldConstants.CODE;
 
+import ch.systemsx.cisd.common.exceptions.InternalErr;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetAttributeSearchFieldKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExperimentAttributeSearchFieldKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.MaterialAttributeSearchFieldKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleAttributeSearchFieldKind;
+import ch.systemsx.cisd.openbis.generic.shared.dto.SearchableEntity;
 import ch.systemsx.cisd.openbis.generic.shared.dto.hibernate.SearchFieldConstants;
 
 /**
@@ -32,6 +34,39 @@ import ch.systemsx.cisd.openbis.generic.shared.dto.hibernate.SearchFieldConstant
  */
 class IndexFieldNameHelper
 {
+    // associations
+
+    static String getAssociationIndexField(EntityKind entityKind, SearchableEntity associationKind)
+    {
+        switch (associationKind)
+        {
+            case EXPERIMENT:
+                if (entityKind == EntityKind.SAMPLE || entityKind == EntityKind.DATA_SET)
+                {
+                    return SearchFieldConstants.EXPERIMENT_ID;
+                }
+                throw createAssociationNotHandledException(entityKind, associationKind);
+            case SAMPLE:
+                if (entityKind == EntityKind.DATA_SET)
+                {
+                    return SearchFieldConstants.SAMPLE_ID;
+                }
+                throw createAssociationNotHandledException(entityKind, associationKind);
+            case MATERIAL:
+                throw createAssociationNotHandledException(entityKind, associationKind);
+            case DATA_SET:
+                throw createAssociationNotHandledException(entityKind, associationKind);
+        }
+        return null; // shouldn't happen
+    }
+
+    private static RuntimeException createAssociationNotHandledException(EntityKind entityKind,
+            SearchableEntity associationKind)
+    {
+        return InternalErr.error("Associations between " + entityKind + " and " + associationKind
+                + " are not supported");
+    }
+
     // properties
 
     static String getPropertyIndexField(String propertyCode)
@@ -88,7 +123,7 @@ class IndexFieldNameHelper
                 return SearchFieldConstants.PREFIX_ENTITY_TYPE + CODE;
             case PROJECT:
                 return SearchFieldConstants.PREFIX_PROJECT + CODE;
-            case PROJECT_GROUP:
+            case PROJECT_SPACE:
                 return SearchFieldConstants.PREFIX_PROJECT + SearchFieldConstants.PREFIX_SPACE
                         + CODE;
         }
@@ -116,7 +151,7 @@ class IndexFieldNameHelper
                 return CODE;
             case SAMPLE_TYPE:
                 return SearchFieldConstants.PREFIX_ENTITY_TYPE + CODE;
-            case GROUP:
+            case SPACE:
                 return SearchFieldConstants.PREFIX_SPACE + CODE;
         }
         return null; // cannot happen
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java
index 610cec0ebf6e87149b9927ce7434d33935e8129e..dae02f9b5b0ab8263014e8c4132edf8c60a8235e 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java
@@ -64,6 +64,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseModificationKind.ObjectKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatastoreServiceDescription;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchSubCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DynamicPropertyEvaluationInfo;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityType;
@@ -573,7 +574,8 @@ public interface ICommonServer extends IServer
     @Transactional(readOnly = true)
     @RolesAllowed(RoleWithHierarchy.SPACE_OBSERVER)
     @ReturnValueFilter(validatorClass = SampleValidator.class)
-    public List<Sample> searchForSamples(String sessionToken, DetailedSearchCriteria criteria);
+    public List<Sample> searchForSamples(String sessionToken, DetailedSearchCriteria criteria,
+            List<DetailedSearchSubCriteria> subCriterias);
 
     /**
      * Returns all data sets related to specified entities.
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/SearchCriteria.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/SearchCriteria.java
index 4e6a29619186c5267b2af22dd165604bf8e7a303..5223bed27838a637713822132b6d748f6ef33d2a 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/SearchCriteria.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/SearchCriteria.java
@@ -70,7 +70,12 @@ public class SearchCriteria implements Serializable
      */
     public static enum MatchClauseAttribute
     {
-        CODE, TYPE
+        // common
+        CODE, TYPE,
+        // for sample or experiment
+        SPACE,
+        // for experiment
+        PROJECT
     }
 
     /**
@@ -264,10 +269,24 @@ public class SearchCriteria implements Serializable
         MATCH_ALL_CLAUSES, MATCH_ANY_CLAUSES
     }
 
+    /**
+     * Operators for combining {@link SearchCriteria} objects.
+     * 
+     * @author Piotr Buczek
+     */
+    public static enum SubSearchCriteriaOperator
+    {
+        AND, OR
+    }
+
     private SearchOperator operator = SearchOperator.MATCH_ALL_CLAUSES;
 
     private ArrayList<MatchClause> matchClauses = new ArrayList<MatchClause>();
 
+    private SubSearchCriteriaOperator subSearchOperator = SubSearchCriteriaOperator.AND;
+
+    private ArrayList<SearchSubCriteria> subCriterias = new ArrayList<SearchSubCriteria>();
+
     /**
      * Set the operator for combining MatchClause objects.
      */
@@ -301,6 +320,31 @@ public class SearchCriteria implements Serializable
         matchClauses.add(criterion);
     }
 
+    /**
+     * Gets the operator for combining {@link SearchSubCriteria} objects. Default value is
+     * {@link SubSearchCriteriaOperator#AND}.
+     */
+    public SubSearchCriteriaOperator getSubSearchCriteriaOperator()
+    {
+        return subSearchOperator;
+    }
+
+    /**
+     * Get a list of {@link SearchSubCriteria} objects for this SearchCriteria.
+     */
+    public List<SearchSubCriteria> getSubCriterias()
+    {
+        return Collections.unmodifiableList(subCriterias);
+    }
+
+    /**
+     * Add a new sub search criteria.
+     */
+    public void addSubCriteria(SearchSubCriteria criteria)
+    {
+        subCriterias.add(criteria);
+    }
+
     @Override
     public boolean equals(Object obj)
     {
@@ -317,6 +361,8 @@ public class SearchCriteria implements Serializable
         EqualsBuilder builder = new EqualsBuilder();
         builder.append(getOperator(), other.getOperator());
         builder.append(getMatchClauses(), other.getMatchClauses());
+        builder.append(getSubSearchCriteriaOperator(), other.getSubSearchCriteriaOperator());
+        builder.append(getSubCriterias(), other.getSubCriterias());
         return builder.isEquals();
     }
 
@@ -326,6 +372,8 @@ public class SearchCriteria implements Serializable
         HashCodeBuilder builder = new HashCodeBuilder();
         builder.append(getOperator());
         builder.append(getMatchClauses());
+        builder.append(getSubSearchCriteriaOperator());
+        builder.append(getSubCriterias());
         return builder.toHashCode();
     }
 
@@ -335,6 +383,8 @@ public class SearchCriteria implements Serializable
         ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
         builder.append(getOperator());
         builder.append(getMatchClauses());
+        builder.append(getSubSearchCriteriaOperator());
+        builder.append(getSubCriterias());
         return builder.toString();
     }
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/SearchSubCriteria.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/SearchSubCriteria.java
new file mode 100644
index 0000000000000000000000000000000000000000..fef1b2ca87b265005da77b1b2a111e264f25a92f
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/SearchSubCriteria.java
@@ -0,0 +1,94 @@
+package ch.systemsx.cisd.openbis.generic.shared.api.v1.dto;
+
+import java.io.Serializable;
+
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.apache.commons.lang.builder.HashCodeBuilder;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang.builder.ToStringStyle;
+
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.MatchClause;
+
+/**
+ * A specification of criteria for a subquery about a connected entity.
+ * 
+ * @author Piotr Buczek
+ */
+public class SearchSubCriteria implements Serializable
+{
+    private static final long serialVersionUID = 1L;
+
+    private final SearchCriteria criteria;
+
+    private final SearchableEntityKind targetEntityKind;
+
+    public static SearchSubCriteria createSampleCriteria(SearchCriteria criteria)
+    {
+        return new SearchSubCriteria(SearchableEntityKind.SAMPLE, criteria);
+    }
+
+    public static SearchSubCriteria createExperimentCriteria(SearchCriteria criteria)
+    {
+        return new SearchSubCriteria(SearchableEntityKind.EXPERIMENT, criteria);
+    }
+
+    /**
+     * Protected constructor. Use one of the factory methods to instantiate a
+     * {@link SearchSubCriteria}.
+     * 
+     * @param targetEntityKind
+     * @param criteria
+     */
+    protected SearchSubCriteria(SearchableEntityKind targetEntityKind, SearchCriteria criteria)
+    {
+        this.targetEntityKind = targetEntityKind;
+        this.criteria = criteria;
+    }
+
+    public SearchCriteria getCriteria()
+    {
+        return criteria;
+    }
+
+    public SearchableEntityKind getTargetEntityKind()
+    {
+        return targetEntityKind;
+    }
+
+    @Override
+    public boolean equals(Object obj)
+    {
+        if (obj == this)
+        {
+            return true;
+        }
+        if (obj instanceof MatchClause == false)
+        {
+            return false;
+        }
+
+        SearchSubCriteria other = (SearchSubCriteria) obj;
+        EqualsBuilder builder = new EqualsBuilder();
+        builder.append(getTargetEntityKind(), other.getTargetEntityKind());
+        builder.append(getCriteria(), other.getCriteria());
+        return builder.isEquals();
+    }
+
+    @Override
+    public int hashCode()
+    {
+        HashCodeBuilder builder = new HashCodeBuilder();
+        builder.append(getTargetEntityKind());
+        builder.append(getCriteria());
+        return builder.toHashCode();
+    }
+
+    @Override
+    public String toString()
+    {
+        ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE);
+        builder.append(getTargetEntityKind());
+        builder.append(getCriteria());
+        return builder.toString();
+    }
+}
\ No newline at end of file
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/SearchableEntityKind.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/SearchableEntityKind.java
new file mode 100644
index 0000000000000000000000000000000000000000..0e253d3d6195debd459d78b5ae9062c82bb50050
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/SearchableEntityKind.java
@@ -0,0 +1,11 @@
+package ch.systemsx.cisd.openbis.generic.shared.api.v1.dto;
+
+/**
+ * An enum listing the different kinds of entities that are searchable.
+ * 
+ * @author Piotr Buczek
+ */
+public enum SearchableEntityKind
+{
+    SAMPLE, EXPERIMENT
+}
\ No newline at end of file
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/DetailedSearchAssociationCriteria.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/DetailedSearchAssociationCriteria.java
new file mode 100644
index 0000000000000000000000000000000000000000..c6656ff0a94933e4dfed2115837cd3954518010e
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/DetailedSearchAssociationCriteria.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2011 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.generic.shared.basic.dto;
+
+import java.util.Collection;
+
+import ch.systemsx.cisd.openbis.generic.shared.basic.ISerializable;
+import ch.systemsx.cisd.openbis.generic.shared.dto.SearchableEntity;
+
+/**
+ * Describes detailed search assiciation criteria for with specified entity kind.
+ * 
+ * @author Piotr Buczek
+ */
+public class DetailedSearchAssociationCriteria implements ISerializable
+{
+    private static final long serialVersionUID = ServiceVersionHolder.VERSION;
+
+    private SearchableEntity entityKind;
+
+    private Collection<Long> ids;
+
+    public DetailedSearchAssociationCriteria(SearchableEntity entityKind, Collection<Long> ids)
+    {
+        this.entityKind = entityKind;
+        this.ids = ids;
+    }
+
+    public Collection<Long> getIds()
+    {
+        return ids;
+    }
+
+    public SearchableEntity getEntityKind()
+    {
+        return entityKind;
+    }
+
+    @Override
+    public String toString()
+    {
+        final StringBuilder sb = new StringBuilder();
+        sb.append(entityKind + ": ");
+        sb.append(getIds());
+        return sb.toString();
+    }
+
+}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/DetailedSearchCriteria.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/DetailedSearchCriteria.java
index 43639e945c33673c27120f980a5713e1335a7b2c..a266675ba0ab7e293ef30241be79580529c1f3b4 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/DetailedSearchCriteria.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/DetailedSearchCriteria.java
@@ -85,7 +85,7 @@ public class DetailedSearchCriteria implements ISerializable
             {
                 if (sb.length() > 0)
                 {
-                    sb.append(" " + getConnection().name() + " ");
+                    sb.append(" " + getConnection().getLabel() + " ");
                 }
                 sb.append(element);
             }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/DetailedSearchSubCriteria.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/DetailedSearchSubCriteria.java
new file mode 100644
index 0000000000000000000000000000000000000000..328b4a2ad6548a9c87ba8bd0de9294d611beff94
--- /dev/null
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/DetailedSearchSubCriteria.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2011 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.generic.shared.basic.dto;
+
+import ch.systemsx.cisd.openbis.generic.shared.basic.ISerializable;
+import ch.systemsx.cisd.openbis.generic.shared.dto.SearchableEntity;
+
+/**
+ * Describes detailed search sub criteria for specified associated entity kind.
+ * 
+ * @author Piotr Buczek
+ */
+public class DetailedSearchSubCriteria implements ISerializable
+{
+    private static final long serialVersionUID = ServiceVersionHolder.VERSION;
+
+    private DetailedSearchCriteria criteria;
+
+    private SearchableEntity targetEntityKind;
+
+    public DetailedSearchSubCriteria(SearchableEntity targetEntityKind,
+            DetailedSearchCriteria criteria)
+    {
+        this.targetEntityKind = targetEntityKind;
+        this.criteria = criteria;
+    }
+
+    public DetailedSearchCriteria getCriteria()
+    {
+        return criteria;
+    }
+
+    public SearchableEntity getTargetEntityKind()
+    {
+        return targetEntityKind;
+    }
+
+    @Override
+    public String toString()
+    {
+        final StringBuilder sb = new StringBuilder();
+        sb.append(targetEntityKind + ": ");
+        sb.append(criteria);
+        return sb.toString();
+    }
+
+}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/ExperimentAttributeSearchFieldKind.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/ExperimentAttributeSearchFieldKind.java
index 1d9c9dd8130e1609a24c4140647fb078ce64c702..44f0ebed2024b24b2b35de312cfd7bd0be5d94ea 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/ExperimentAttributeSearchFieldKind.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/ExperimentAttributeSearchFieldKind.java
@@ -29,9 +29,9 @@ public enum ExperimentAttributeSearchFieldKind implements ISerializable, IAttrib
 
     EXPERIMENT_TYPE("Experiment Type"),
 
-    PROJECT("Space"),
+    PROJECT("Project"),
 
-    PROJECT_GROUP("Project Space");
+    PROJECT_SPACE("Space");
 
     private final String description;
 
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/SampleAttributeSearchFieldKind.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/SampleAttributeSearchFieldKind.java
index 0e60a892cdc60118b5b787de96f558bdee1169c7..ed844ebc6a271de20e83f153c010bec6d6206b19 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/SampleAttributeSearchFieldKind.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/SampleAttributeSearchFieldKind.java
@@ -29,7 +29,7 @@ public enum SampleAttributeSearchFieldKind implements ISerializable, IAttributeS
 
     SAMPLE_TYPE("Sample Type"),
 
-    GROUP("Space");
+    SPACE("Space");
 
     private final String description;
 
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/SearchCriteriaConnection.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/SearchCriteriaConnection.java
index 1dba1a1dc6f7a4c7dd2829a413646cf3942e55c4..2a7ac6031ad7eac408835aebac2cb8e7903f65f0 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/SearchCriteriaConnection.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/SearchCriteriaConnection.java
@@ -23,5 +23,18 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.ISerializable;
  */
 public enum SearchCriteriaConnection implements ISerializable
 {
-    MATCH_ALL, MATCH_ANY
+    MATCH_ALL("AND"), MATCH_ANY("OR");
+
+    private String label;
+
+    SearchCriteriaConnection(String label)
+    {
+        this.label = label;
+    }
+
+    public String getLabel()
+    {
+        return label;
+    }
+
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataPE.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataPE.java
index baaae5390966a9a91e1306cd58b2f2fbd2399025..408f204bf9b9ed53c239e0463c2f01ab45d0a66c 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataPE.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DataPE.java
@@ -258,6 +258,15 @@ public class DataPE extends AbstractIdAndCodeHolder<DataPE> implements
         this.sample = sample;
     }
 
+    // used only by Hibernate Search
+    @SuppressWarnings("unused")
+    @Transient
+    @Field(index = Index.UN_TOKENIZED, store = Store.YES, name = SearchFieldConstants.SAMPLE_ID)
+    private Long getSampleId()
+    {
+        return getSampleInternal() != null ? getSampleInternal().getId() : null;
+    }
+
     /**
      * Returns the date when the measurement / calculation that produced this external data set has
      * been performed.
@@ -431,6 +440,15 @@ public class DataPE extends AbstractIdAndCodeHolder<DataPE> implements
         return experiment;
     }
 
+    // used only by Hibernate Search
+    @SuppressWarnings("unused")
+    @Transient
+    @Field(index = Index.UN_TOKENIZED, store = Store.YES, name = SearchFieldConstants.EXPERIMENT_ID)
+    private Long getExperimentId()
+    {
+        return getExperimentInternal() != null ? getExperimentInternal().getId() : null;
+    }
+
     private Set<DataSetPropertyPE> properties = new HashSet<DataSetPropertyPE>();
 
     @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "entity")
@@ -517,4 +535,5 @@ public class DataPE extends AbstractIdAndCodeHolder<DataPE> implements
     {
         return code;
     }
+
 }
\ No newline at end of file
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DatasetDescription.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DatasetDescription.java
index 87f1f15ba8cbc8a57adcc46e5baf685554acea1d..fc89df467d87a6a2f3ea00d8aac3c9ffaaf26c54 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DatasetDescription.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/DatasetDescription.java
@@ -20,8 +20,6 @@ import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.List;
 
-import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ServiceVersionHolder;
-
 /**
  * Describes one dataset which should be processed by the plugin task.
  * 
@@ -29,20 +27,20 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ServiceVersionHolder;
  */
 public class DatasetDescription implements Serializable
 {
-    private static final long serialVersionUID = ServiceVersionHolder.VERSION;
+    private static final long serialVersionUID = 20L;
 
     private String datasetTypeCode;
-    
+
     private String datasetCode;
 
     private String dataSetLocation;
-    
+
     private Long dataSetSize;
 
     private String sampleCode;
-    
+
     private String sampleIdentifier;
-    
+
     private String sampleTypeCode;
 
     private String spaceCode;
@@ -52,9 +50,9 @@ public class DatasetDescription implements Serializable
     private String instanceCode;
 
     private String experimentCode;
-    
+
     private String experimentIdentifier;
-    
+
     private String experimentTypeCode;
 
     private String mainDataSetPattern;
@@ -103,7 +101,7 @@ public class DatasetDescription implements Serializable
     {
         this.dataSetLocation = dataSetLocation;
     }
-    
+
     public String getInstanceCode()
     {
         return instanceCode;
@@ -118,7 +116,7 @@ public class DatasetDescription implements Serializable
     {
         this.datasetCode = datasetCode;
     }
-    
+
     /**
      * NOTE: may be NULL
      */
@@ -131,7 +129,7 @@ public class DatasetDescription implements Serializable
     {
         this.mainDataSetPattern = mainDataSetPattern;
     }
-    
+
     /**
      * NOTE: may be NULL
      */
@@ -144,7 +142,7 @@ public class DatasetDescription implements Serializable
     {
         this.mainDataSetPath = mainDataSetPath;
     }
-    
+
     /**
      * NOTE: may be NULL
      */
@@ -157,7 +155,7 @@ public class DatasetDescription implements Serializable
     {
         this.sampleCode = sampleCode;
     }
-    
+
     public void setSampleIdentifier(String sampleIdentifier)
     {
         this.sampleIdentifier = sampleIdentifier;
@@ -187,7 +185,7 @@ public class DatasetDescription implements Serializable
     {
         this.spaceCode = groupCode;
     }
-    
+
     public String getProjectCode()
     {
         return projectCode;
@@ -197,7 +195,7 @@ public class DatasetDescription implements Serializable
     {
         this.projectCode = projectCode;
     }
-    
+
     public String getDatabaseInstanceCode()
     {
         return instanceCode;
@@ -207,7 +205,7 @@ public class DatasetDescription implements Serializable
     {
         this.instanceCode = instanceCode;
     }
-    
+
     public String getExperimentCode()
     {
         return experimentCode;
@@ -217,7 +215,7 @@ public class DatasetDescription implements Serializable
     {
         this.experimentCode = experimentCode;
     }
-    
+
     public void setExperimentIdentifier(String experimentIdentifier)
     {
         this.experimentIdentifier = experimentIdentifier;
@@ -243,7 +241,5 @@ public class DatasetDescription implements Serializable
     {
         return String.format("Dataset '%s'", datasetCode);
     }
-    
-}
-
 
+}
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/SamplePE.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/SamplePE.java
index 4936235a46c19907e3ca6df00a27e8c0a464783b..df67e93eb884139566a31ca43307de9f921c574c 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/SamplePE.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/SamplePE.java
@@ -461,6 +461,15 @@ public class SamplePE extends AttachmentHolderPE implements IIdAndCodeHolder, Co
         return experiment;
     }
 
+    // used only by Hibernate Search
+    @SuppressWarnings("unused")
+    @Transient
+    @Field(index = Index.UN_TOKENIZED, store = Store.YES, name = SearchFieldConstants.EXPERIMENT_ID)
+    private Long getExperimentId()
+    {
+        return getExperimentInternal() != null ? getExperimentInternal().getId() : null;
+    }
+
     //
     // IIdAndCodeHolder
     //
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/hibernate/SearchFieldConstants.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/hibernate/SearchFieldConstants.java
index 1314f4b0a76f28d5b45da03819eb56567ef8dee9..e62d9de685213df1483a685308e95c5524a691e7 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/hibernate/SearchFieldConstants.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/dto/hibernate/SearchFieldConstants.java
@@ -72,4 +72,8 @@ public final class SearchFieldConstants
 
     public static final String FILE_DESCRIPTION = "description";
 
+    public static final String EXPERIMENT_ID = "experiment_id";
+
+    public static final String SAMPLE_ID = "sample_id";
+
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/PropertyTranslatorUtils.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/PropertyTranslatorUtils.java
index 4cf87dfed80f6b6589ecc3cc6bf8d88912080933..e2bd3e5495278c4aa3bbc0d0d3478d08ead3d27d 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/PropertyTranslatorUtils.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/translator/PropertyTranslatorUtils.java
@@ -80,6 +80,7 @@ final class PropertyTranslatorUtils
         final ManagedEntityProperty result = new ManagedEntityProperty(basicProperty);
         try
         {
+            // TODO move this outside of translator
             ManagedPropertyEvaluator evaluator =
                     ManagedPropertyEvaluatorFactory.createManagedPropertyEvaluator(script
                             .getScript());
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/public/common-dictionary.js b/openbis/source/java/ch/systemsx/cisd/openbis/public/common-dictionary.js
index f2b9d96dec91a09b33e0b6b3f5407ee977aa73c2..453d1caa100cfac3b29ec2e78564bf2bebcf5864 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/public/common-dictionary.js
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/public/common-dictionary.js
@@ -589,8 +589,8 @@ var common = {
  //
  data_set_search: "Data Set Search",
  sample_search: "Sample Search",
- match_all: "Match all criteria",
- match_any: "Match any criteria",
+ match_all: "Match all criteria (logical AND)",
+ match_any: "Match any criteria (logical OR)",
  button_change_query : "Change Search Criteria",
   
  //
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/SampleProviderTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/SampleProviderTest.java
index c79e440cf1ef62ea9966d6dc644c1641df6fd291..aba419963b9e29d67ebb191b7d5410cf68589555 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/SampleProviderTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/client/web/server/resultset/SampleProviderTest.java
@@ -17,6 +17,7 @@
 package ch.systemsx.cisd.openbis.generic.client.web.server.resultset;
 
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 
@@ -27,6 +28,7 @@ import org.testng.annotations.Test;
 import ch.systemsx.cisd.openbis.generic.client.web.client.dto.ListSampleDisplayCriteria2;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataTypeCode;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchSubCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ListSampleCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.TableModelRowWithObject;
@@ -126,7 +128,8 @@ public class SampleProviderTest extends AbstractProviderTest
         context.checking(new Expectations()
             {
                 {
-                    one(server).searchForSamples(SESSION_TOKEN, criteria);
+                    one(server).searchForSamples(SESSION_TOKEN, criteria,
+                            Collections.<DetailedSearchSubCriteria> emptyList());
                     will(returnValue(Arrays.asList(s1.getSample(), s2.getSample())));
                 }
             });
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationServiceTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationServiceTest.java
index 8af35e94c7bf6fe41ffa94b89f948fe7ad0778a0..e8603460e09653ea044fd46e3d0513b494996394 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationServiceTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/api/v1/GeneralInformationServiceTest.java
@@ -30,6 +30,7 @@ import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import ch.rinn.restrictions.Friend;
+import ch.systemsx.cisd.common.test.RecordingMatcher;
 import ch.systemsx.cisd.openbis.generic.shared.AbstractServerTestCase;
 import ch.systemsx.cisd.openbis.generic.shared.ICommonServer;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet;
@@ -41,10 +42,12 @@ import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.Sample.SampleInitializ
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.MatchClause;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.MatchClauseAttribute;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchSubCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SpaceWithProjectsAndRoleAssignments;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetRelatedEntities;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchSubCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.ExperimentType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.RoleWithHierarchy.RoleCode;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleType;
@@ -194,21 +197,59 @@ public class GeneralInformationServiceTest extends AbstractServerTestCase
     public void testSearchForSamples()
     {
         prepareGetSession();
-        prepareSearchForSamples();
-        List<Sample> result = service.searchForSamples(SESSION_TOKEN, createSearchCriteria());
+        final RecordingMatcher<DetailedSearchCriteria> detailedSearchCriteriaMatcher =
+                RecordingMatcher.create();
+        final RecordingMatcher<List<DetailedSearchSubCriteria>> detailedSearchSubCriteriaMatcher =
+                RecordingMatcher.create();
+        prepareSearchForSamples(detailedSearchCriteriaMatcher, detailedSearchSubCriteriaMatcher);
+        List<Sample> result =
+                service.searchForSamples(SESSION_TOKEN, createSearchCriteriaForSample());
         assertEquals(1, result.size());
         Sample resultSample = result.get(0);
         assertEquals("/space/code", resultSample.getIdentifier());
+        assertEquals("ATTRIBUTE CODE: a code AND "
+                + "PROPERTY MY_PROPERTY2: a property value (with wildcards)",
+                detailedSearchCriteriaMatcher.recordedObject().toString());
+        // no subcriteria
+        assertEquals("[]", detailedSearchSubCriteriaMatcher.recordedObject().toString());
         context.assertIsSatisfied();
     }
 
-    private void prepareSearchForSamples()
+    @Test
+    public void testSearchForSamplesWithExperiment()
+    {
+        prepareGetSession();
+        final RecordingMatcher<DetailedSearchCriteria> detailedSearchCriteriaMatcher =
+                RecordingMatcher.create();
+        final RecordingMatcher<List<DetailedSearchSubCriteria>> detailedSearchSubCriteriaMatcher =
+                RecordingMatcher.create();
+        prepareSearchForSamples(detailedSearchCriteriaMatcher, detailedSearchSubCriteriaMatcher);
+        List<Sample> result =
+                service.searchForSamples(SESSION_TOKEN,
+                        createSearchCriteriaForSampleWithExperiment());
+        assertEquals(1, result.size());
+        Sample resultSample = result.get(0);
+        assertEquals("/space/code", resultSample.getIdentifier());
+        assertEquals("ATTRIBUTE CODE: a code AND "
+                + "PROPERTY MY_PROPERTY2: a property value (with wildcards)",
+                detailedSearchCriteriaMatcher.recordedObject().toString());
+        // check experiment subcriteria
+        assertEquals("[Experiment: ATTRIBUTE CODE: a code AND ATTRIBUTE PROJECT: a project AND "
+                + "PROPERTY EXP_PROPERTY: exp property value (with wildcards)]",
+                detailedSearchSubCriteriaMatcher.recordedObject().toString());
+        context.assertIsSatisfied();
+    }
+
+    private void prepareSearchForSamples(
+            final RecordingMatcher<DetailedSearchCriteria> detailedSearchCriteriaMatcher,
+            final RecordingMatcher<List<DetailedSearchSubCriteria>> detailedSearchSubCriteriaMatcher)
     {
         context.checking(new Expectations()
             {
                 {
                     one(commonServer).searchForSamples(with(SESSION_TOKEN),
-                            with(any(DetailedSearchCriteria.class)));
+                            with(detailedSearchCriteriaMatcher),
+                            with(detailedSearchSubCriteriaMatcher));
                     SampleBuilder sample =
                             new SampleBuilder("/space/code")
                                     .id(1L)
@@ -273,7 +314,8 @@ public class GeneralInformationServiceTest extends AbstractServerTestCase
             {
                 {
                     one(commonServer).listSampleTypes(SESSION_TOKEN);
-                    SampleTypeBuilder sampleType = new SampleTypeBuilder().id(1L).code("sample-type");
+                    SampleTypeBuilder sampleType =
+                            new SampleTypeBuilder().id(1L).code("sample-type");
                     will(returnValue(Collections.singletonList(sampleType.getSampleType())));
 
                     one(commonServer).listRelatedDataSets(with(SESSION_TOKEN),
@@ -389,7 +431,7 @@ public class GeneralInformationServiceTest extends AbstractServerTestCase
         context.assertIsSatisfied();
     }
 
-    private SearchCriteria createSearchCriteria()
+    private SearchCriteria createSearchCriteriaForSample()
     {
         SearchCriteria sc = new SearchCriteria();
         sc.addMatchClause(MatchClause.createAttributeMatch(MatchClauseAttribute.CODE, "a code"));
@@ -397,6 +439,24 @@ public class GeneralInformationServiceTest extends AbstractServerTestCase
         return sc;
     }
 
+    private SearchCriteria createSearchCriteriaForExperiment()
+    {
+        SearchCriteria sc = new SearchCriteria();
+        sc.addMatchClause(MatchClause.createAttributeMatch(MatchClauseAttribute.CODE, "a code"));
+        sc.addMatchClause(MatchClause.createAttributeMatch(MatchClauseAttribute.PROJECT,
+                "a project"));
+        sc.addMatchClause(MatchClause.createPropertyMatch("EXP_PROPERTY", "exp property value"));
+        return sc;
+    }
+
+    private SearchCriteria createSearchCriteriaForSampleWithExperiment()
+    {
+        SearchCriteria mainCriteria = createSearchCriteriaForSample();
+        SearchCriteria expCriteria = createSearchCriteriaForExperiment();
+        mainCriteria.addSubCriteria(SearchSubCriteria.createExperimentCriteria(expCriteria));
+        return mainCriteria;
+    }
+
     private void assertSpaceAndProjects(String expectedSpaceCode, String expectedProjects,
             SpaceWithProjectsAndRoleAssignments space)
     {
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/api/v1/SearchCriteriaToDetailedSearchCriteriaTranslatorTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/api/v1/SearchCriteriaToDetailedSearchCriteriaTranslatorTest.java
index 991d132310cfa9c5df389b20ff354065a6a649a4..70a80b71f69407b2f5064d3164b11450984ad475 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/api/v1/SearchCriteriaToDetailedSearchCriteriaTranslatorTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/api/v1/SearchCriteriaToDetailedSearchCriteriaTranslatorTest.java
@@ -17,12 +17,13 @@
 package ch.systemsx.cisd.openbis.generic.server.api.v1;
 
 import org.testng.AssertJUnit;
-import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.MatchClause;
 import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.MatchClauseAttribute;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchCriteria.SearchOperator;
+import ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.SearchableEntityKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria;
 
 /**
@@ -30,38 +31,109 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria;
  */
 public class SearchCriteriaToDetailedSearchCriteriaTranslatorTest extends AssertJUnit
 {
-    private SearchCriteria searchCriteria;
+    @Test
+    public void testBasicMatchAllCriteriaTranslator()
+    {
+        SearchCriteria criteria = createBasicSearchCriteria();
+        SearchCriteriaToDetailedSearchCriteriaTranslator translator =
+                new SearchCriteriaToDetailedSearchCriteriaTranslator(criteria,
+                        SearchableEntityKind.SAMPLE);
+
+        DetailedSearchCriteria detailedSearchCriteria =
+                translator.convertToDetailedSearchCriteria();
+        assertNotNull(detailedSearchCriteria);
 
-    private SearchCriteriaToDetailedSearchCriteriaTranslator translator;
+        // It is easier to test equality by string comparison
+        assertEquals("ATTRIBUTE CODE: a code AND " + "PROPERTY MY_PROPERTY: a property value "
+                + "(with wildcards)", detailedSearchCriteria.toString());
+    }
 
-    @BeforeMethod
-    public void setUp()
+    @Test
+    public void testBasicMatchAnyCriteriaTranslator()
     {
-        searchCriteria = createSearchCriteria();
-        translator =
-                new SearchCriteriaToDetailedSearchCriteriaTranslator(
-                        searchCriteria,
-                        new SearchCriteriaToDetailedSearchCriteriaTranslator.SampleAttributeTranslator());
+        SearchCriteria criteria = createBasicSearchCriteria();
+        criteria.setOperator(SearchOperator.MATCH_ANY_CLAUSES);
+        SearchCriteriaToDetailedSearchCriteriaTranslator translator =
+                new SearchCriteriaToDetailedSearchCriteriaTranslator(criteria,
+                        SearchableEntityKind.SAMPLE);
+
+        DetailedSearchCriteria detailedSearchCriteria =
+                translator.convertToDetailedSearchCriteria();
+        assertNotNull(detailedSearchCriteria);
+
+        assertEquals("ATTRIBUTE CODE: a code OR " + "PROPERTY MY_PROPERTY: a property value "
+                + "(with wildcards)", detailedSearchCriteria.toString());
     }
 
     @Test
-    public void testTranslator()
+    public void testFullSampleMatchAllCriteriaTranslator()
     {
+        SearchCriteria criteria = createBasicSearchCriteria();
+        criteria.addMatchClause(MatchClause.createAttributeMatch(MatchClauseAttribute.TYPE,
+                "a type"));
+        criteria.addMatchClause(MatchClause.createAttributeMatch(MatchClauseAttribute.SPACE,
+                "a space"));
+        SearchCriteriaToDetailedSearchCriteriaTranslator translator =
+                new SearchCriteriaToDetailedSearchCriteriaTranslator(criteria,
+                        SearchableEntityKind.SAMPLE);
+
         DetailedSearchCriteria detailedSearchCriteria =
                 translator.convertToDetailedSearchCriteria();
         assertNotNull(detailedSearchCriteria);
 
-        // It is easier to test equality by string comparison
-        assertEquals(
-                "ATTRIBUTE CODE: a code MATCH_ALL PROPERTY MY_PROPERTY2: a property value (with wildcards)",
-                detailedSearchCriteria.toString());
+        assertEquals("ATTRIBUTE CODE: a code AND " + "PROPERTY MY_PROPERTY: a property value AND "
+                + "ATTRIBUTE SAMPLE_TYPE: a type AND " + "ATTRIBUTE SPACE: a space "
+                + "(with wildcards)", detailedSearchCriteria.toString());
+    }
+
+    @Test
+    public void testFullExperimentMatchAllCriteriaTranslator()
+    {
+        SearchCriteria criteria = createBasicSearchCriteria();
+        criteria.addMatchClause(MatchClause.createAttributeMatch(MatchClauseAttribute.TYPE,
+                "a type"));
+        criteria.addMatchClause(MatchClause.createAttributeMatch(MatchClauseAttribute.SPACE,
+                "a space"));
+        criteria.addMatchClause(MatchClause.createAttributeMatch(MatchClauseAttribute.PROJECT,
+                "a project"));
+        SearchCriteriaToDetailedSearchCriteriaTranslator translator =
+                new SearchCriteriaToDetailedSearchCriteriaTranslator(criteria,
+                        SearchableEntityKind.EXPERIMENT);
+
+        DetailedSearchCriteria detailedSearchCriteria =
+                translator.convertToDetailedSearchCriteria();
+        assertNotNull(detailedSearchCriteria);
+
+        assertEquals("ATTRIBUTE CODE: a code AND " + "PROPERTY MY_PROPERTY: a property value AND "
+                + "ATTRIBUTE EXPERIMENT_TYPE: a type AND "
+                + "ATTRIBUTE PROJECT_SPACE: a space AND " + "ATTRIBUTE PROJECT: a project "
+                + "(with wildcards)", detailedSearchCriteria.toString());
+    }
+
+    @Test
+    public void testSampleCriteriaTranslatorFailsWithUnsupportedAttribute()
+    {
+        SearchCriteria criteria = createBasicSearchCriteria();
+        criteria.addMatchClause(MatchClause.createAttributeMatch(MatchClauseAttribute.PROJECT,
+                "a project"));
+        SearchCriteriaToDetailedSearchCriteriaTranslator translator =
+                new SearchCriteriaToDetailedSearchCriteriaTranslator(criteria,
+                        SearchableEntityKind.SAMPLE);
+        try
+        {
+            translator.convertToDetailedSearchCriteria();
+            fail("Expected UnsupportedOperationException");
+        } catch (UnsupportedOperationException ex)
+        {
+            assertEquals("PROJECT is not a valid search attribute for SAMPLE", ex.getMessage());
+        }
     }
 
-    private SearchCriteria createSearchCriteria()
+    private SearchCriteria createBasicSearchCriteria()
     {
         SearchCriteria sc = new SearchCriteria();
         sc.addMatchClause(MatchClause.createAttributeMatch(MatchClauseAttribute.CODE, "a code"));
-        sc.addMatchClause(MatchClause.createPropertyMatch("MY_PROPERTY2", "a property value"));
+        sc.addMatchClause(MatchClause.createPropertyMatch("MY_PROPERTY", "a property value"));
         return sc;
     }
 
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/HibernateSearchDAOTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/HibernateSearchDAOTest.java
index 186c613ac5cda90bc425b1cf29585d846b6e636c..079f05a8494751e82e85c7d1a179e60f25568e02 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/HibernateSearchDAOTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/server/dataaccess/db/HibernateSearchDAOTest.java
@@ -25,6 +25,7 @@ import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -47,6 +48,7 @@ import ch.systemsx.cisd.openbis.generic.server.dataaccess.db.search.FullTextInde
 import ch.systemsx.cisd.openbis.generic.server.util.TestInitializer;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataSetAttributeSearchFieldKind;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchAssociationCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriterion;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchField;
@@ -118,7 +120,8 @@ public final class HibernateSearchDAOTest extends AbstractDAOTest
         boolean fail = true;
         try
         {
-            hibernateSearchDAO.searchEntitiesByTerm(null, null, createDataProvider(), true, 0, Integer.MAX_VALUE);
+            hibernateSearchDAO.searchEntitiesByTerm(null, null, createDataProvider(), true, 0,
+                    Integer.MAX_VALUE);
         } catch (final AssertionError ex)
         {
             fail = false;
@@ -262,7 +265,8 @@ public final class HibernateSearchDAOTest extends AbstractDAOTest
         final IHibernateSearchDAO hibernateSearchDAO = daoFactory.getHibernateSearchDAO();
         List<Long> datasetIds =
                 hibernateSearchDAO.searchForEntityIds(criteria,
-                        DtoConverters.convertEntityKind(EntityKind.DATA_SET));
+                        DtoConverters.convertEntityKind(EntityKind.DATA_SET),
+                        Collections.<DetailedSearchAssociationCriteria> emptyList());
         final List<ExternalDataPE> result = new ArrayList<ExternalDataPE>();
         for (Long datasetId : datasetIds)
         {
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java.expected b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java.expected
index 610cec0ebf6e87149b9927ce7434d33935e8129e..dae02f9b5b0ab8263014e8c4132edf8c60a8235e 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java.expected
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/ICommonServer.java.expected
@@ -64,6 +64,7 @@ import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DataType;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatabaseModificationKind.ObjectKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DatastoreServiceDescription;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchSubCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DynamicPropertyEvaluationInfo;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.EntityType;
@@ -573,7 +574,8 @@ public interface ICommonServer extends IServer
     @Transactional(readOnly = true)
     @RolesAllowed(RoleWithHierarchy.SPACE_OBSERVER)
     @ReturnValueFilter(validatorClass = SampleValidator.class)
-    public List<Sample> searchForSamples(String sessionToken, DetailedSearchCriteria criteria);
+    public List<Sample> searchForSamples(String sessionToken, DetailedSearchCriteria criteria,
+            List<DetailedSearchSubCriteria> subCriterias);
 
     /**
      * Returns all data sets related to specified entities.
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/SearchCriteriaTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/SearchCriteriaTest.java
index 6b98f20d2fa22ba84d27d4c7a668a35bfac21bad..c91ab63a507b71e84f1d56ef4ab568fc3f283ba3 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/SearchCriteriaTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/generic/shared/api/v1/dto/SearchCriteriaTest.java
@@ -62,8 +62,9 @@ public class SearchCriteriaTest extends AssertJUnit
     @Test
     public void testToString()
     {
-        assertEquals(
-                "SearchCriteria[MATCH_ALL_CLAUSES,[SearchCriteria.AttributeMatchClause[ATTRIBUTE,CODE,a code], SearchCriteria.PropertyMatchClause[PROPERTY,MY_PROPERTY2,a property value]]]",
-                searchCriteria.toString());
+        assertEquals("SearchCriteria[MATCH_ALL_CLAUSES,["
+                + "SearchCriteria.AttributeMatchClause[ATTRIBUTE,CODE,a code], "
+                + "SearchCriteria.PropertyMatchClause[PROPERTY,MY_PROPERTY2,a property value]"
+                + "],AND,[]]", searchCriteria.toString());
     }
 }
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/server/resultset/BiologicalSampleProvider.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/server/resultset/BiologicalSampleProvider.java
index 9da3f00c18ead8ff2e14794d7311664a48fb837e..958dc0744938c582fe3d67906e0b98015c50282c 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/server/resultset/BiologicalSampleProvider.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/server/resultset/BiologicalSampleProvider.java
@@ -20,6 +20,7 @@ import static ch.systemsx.cisd.openbis.plugin.phosphonetx.client.web.client.dto.
 import static ch.systemsx.cisd.openbis.plugin.phosphonetx.client.web.client.dto.BiologicalSampleGridColumnIDs.REGISTRATION_DATE;
 
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
 import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.AbstractCommonTableModelProvider;
@@ -27,6 +28,7 @@ import ch.systemsx.cisd.openbis.generic.shared.ICommonServer;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriterion;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchField;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchSubCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleAttributeSearchFieldKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SearchCriteriaConnection;
@@ -61,7 +63,9 @@ public class BiologicalSampleProvider extends AbstractCommonTableModelProvider<S
         registratorCriterion.setField(DetailedSearchField.createRegistratorField());
         registratorCriterion.setValue(userName);
         criteria.setCriteria(Arrays.asList(typeCriterion, registratorCriterion));
-        List<Sample> samples = commonServer.searchForSamples(sessionToken, criteria);
+        List<Sample> samples =
+                commonServer.searchForSamples(sessionToken, criteria,
+                        Collections.<DetailedSearchSubCriteria> emptyList());
         TypedTableModelBuilder<Sample> builder = new TypedTableModelBuilder<Sample>();
         builder.addColumn(IDENTIFIER);
         builder.addColumn(REGISTRATION_DATE);
diff --git a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/server/resultset/ParentlessMsInjectionSampleProvider.java b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/server/resultset/ParentlessMsInjectionSampleProvider.java
index dd56b8727d98420f23cdc473f635ae9f07bb6ea2..89bf1e84ce25b3029364d44cccaf661b67c74d20 100644
--- a/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/server/resultset/ParentlessMsInjectionSampleProvider.java
+++ b/rtd_phosphonetx/source/java/ch/systemsx/cisd/openbis/plugin/phosphonetx/client/web/server/resultset/ParentlessMsInjectionSampleProvider.java
@@ -20,6 +20,7 @@ import static ch.systemsx.cisd.openbis.plugin.phosphonetx.client.web.client.dto.
 import static ch.systemsx.cisd.openbis.plugin.phosphonetx.client.web.client.dto.ParentlessMsInjectionSampleGridColumnIDs.REGISTRATION_DATE;
 
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
 import ch.systemsx.cisd.openbis.generic.client.web.server.resultset.AbstractCommonTableModelProvider;
@@ -27,6 +28,7 @@ import ch.systemsx.cisd.openbis.generic.shared.ICommonServer;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriterion;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchField;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchSubCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.Sample;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SampleAttributeSearchFieldKind;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.SearchCriteriaConnection;
@@ -36,7 +38,7 @@ import ch.systemsx.cisd.openbis.plugin.phosphonetx.shared.CommonConstants;
 
 /**
  * Provider of MS_INJECTION samples registered for the user.
- *
+ * 
  * @author Franz-Josef Elmer
  */
 public class ParentlessMsInjectionSampleProvider extends AbstractCommonTableModelProvider<Sample>
@@ -57,7 +59,7 @@ public class ParentlessMsInjectionSampleProvider extends AbstractCommonTableMode
         typeCriterion.setValue(CommonConstants.MS_INJECTION_SAMPLE_TYPE_CODE);
         DetailedSearchCriterion spaceCriterion = new DetailedSearchCriterion();
         spaceCriterion.setField(DetailedSearchField
-                .createAttributeField(SampleAttributeSearchFieldKind.GROUP));
+                .createAttributeField(SampleAttributeSearchFieldKind.SPACE));
         spaceCriterion.setValue(CommonConstants.MS_DATA_SPACE);
         DetailedSearchCriterion registratorCriterion = new DetailedSearchCriterion();
         String userName = commonServer.tryGetSession(sessionToken).getUserName();
@@ -65,7 +67,9 @@ public class ParentlessMsInjectionSampleProvider extends AbstractCommonTableMode
         registratorCriterion.setValue(userName);
         System.out.println("userName:"+userName);
         criteria.setCriteria(Arrays.asList(typeCriterion, spaceCriterion, registratorCriterion));
-        List<Sample> samples = commonServer.searchForSamples(sessionToken, criteria);
+        List<Sample> samples =
+                commonServer.searchForSamples(sessionToken, criteria,
+                        Collections.<DetailedSearchSubCriteria> emptyList());
         TypedTableModelBuilder<Sample> builder = new TypedTableModelBuilder<Sample>();
         builder.addColumn(IDENTIFIER).withDefaultWidth(300);
         builder.addColumn(REGISTRATION_DATE).withDefaultWidth(300);
diff --git a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/WellContentLoader.java b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/WellContentLoader.java
index f4e20455e8162f26ab8b59a7e633c05ef44010f0..c2c9485bfbb2d92df56ee38e7f6a0987d490e035 100644
--- a/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/WellContentLoader.java
+++ b/screening/source/java/ch/systemsx/cisd/openbis/plugin/screening/server/logic/WellContentLoader.java
@@ -44,6 +44,7 @@ import ch.systemsx.cisd.openbis.generic.server.business.bo.materiallister.IMater
 import ch.systemsx.cisd.openbis.generic.server.business.bo.samplelister.ISampleLister;
 import ch.systemsx.cisd.openbis.generic.server.dataaccess.IDAOFactory;
 import ch.systemsx.cisd.openbis.generic.shared.basic.TechId;
+import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchAssociationCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriteria;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchCriterion;
 import ch.systemsx.cisd.openbis.generic.shared.basic.dto.DetailedSearchField;
@@ -674,7 +675,8 @@ public class WellContentLoader
         return ArrayUtils.toPrimitive(daoFactory
                 .getHibernateSearchDAO()
                 .searchForEntityIds(criteria,
-                        ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind.MATERIAL)
+                        ch.systemsx.cisd.openbis.generic.shared.dto.properties.EntityKind.MATERIAL,
+                        Collections.<DetailedSearchAssociationCriteria> emptyList())
                 .toArray(new Long[0]));
     }