From dd7b59644ba0f95bb2bb2c4a0ba70dccfd847332 Mon Sep 17 00:00:00 2001
From: buczekp <buczekp>
Date: Tue, 22 Mar 2011 09:32:48 +0000
Subject: [PATCH] [LMS-2148] fixed search with container; added search with
 children

SVN: 20434
---
 ...riaToDetailedSearchCriteriaTranslator.java |   6 +-
 .../business/DetailedSearchManager.java       | 123 +++++++++++++-----
 .../bo/samplelister/ISampleLister.java        |   6 +-
 .../bo/samplelister/SampleLister.java         |   4 +-
 .../search/detailed/IndexFieldNameHelper.java |   7 +-
 .../shared/api/v1/dto/SearchSubCriteria.java  |   5 +
 .../api/v1/dto/SearchableEntityKind.java      |   2 +-
 .../basic/dto/AssociatedEntityKind.java       |   4 +-
 .../openbis/generic/shared/dto/SamplePE.java  |   4 +-
 .../api/v1/GeneralInformationServiceTest.java |  40 ++++++
 .../api/v1/GeneralInformationServiceTest.java |  31 +++++
 11 files changed, 187 insertions(+), 45 deletions(-)

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 7023576eb94..5cdde53f652 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
@@ -59,6 +59,7 @@ class SearchCriteriaToDetailedSearchCriteriaTranslator
         translators.put(SearchableEntityKind.EXPERIMENT, new ExperimentAttributeTranslator());
         translators.put(SearchableEntityKind.SAMPLE, new SampleAttributeTranslator());
         translators.put(SearchableEntityKind.SAMPLE_PARENT, new SampleAttributeTranslator());
+        translators.put(SearchableEntityKind.SAMPLE_CHILD, new SampleAttributeTranslator());
         translators.put(SearchableEntityKind.SAMPLE_CONTAINER, new SampleAttributeTranslator());
     }
 
@@ -74,7 +75,8 @@ class SearchCriteriaToDetailedSearchCriteriaTranslator
                 attribute, entityKind));
     }
 
-    private static AssociatedEntityKind convertToAssociatedEntityKind(SearchableEntityKind entityKind)
+    private static AssociatedEntityKind convertToAssociatedEntityKind(
+            SearchableEntityKind entityKind)
     {
         switch (entityKind)
         {
@@ -86,6 +88,8 @@ class SearchCriteriaToDetailedSearchCriteriaTranslator
                 return AssociatedEntityKind.SAMPLE_CONTAINER;
             case SAMPLE_PARENT:
                 return AssociatedEntityKind.SAMPLE_PARENT;
+            case SAMPLE_CHILD:
+                return AssociatedEntityKind.SAMPLE_CHILD;
         }
         return null; // can't happen
     }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/DetailedSearchManager.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/DetailedSearchManager.java
index f2bca9d4b3a..f3f69c066e5 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/DetailedSearchManager.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/DetailedSearchManager.java
@@ -59,60 +59,110 @@ public class DetailedSearchManager
             List<DetailedSearchSubCriteria> subCriterias) throws DataAccessException
     {
         final DetailedSearchCriteria parentCriteria = new DetailedSearchCriteria();
+        final DetailedSearchCriteria childCriteria = new DetailedSearchCriteria();
         final List<DetailedSearchSubCriteria> otherSubCriterias =
                 new ArrayList<DetailedSearchSubCriteria>();
-        groupSampleSubCriteria(subCriterias, parentCriteria, otherSubCriterias);
+        groupSampleSubCriteria(subCriterias, parentCriteria, childCriteria, otherSubCriterias);
 
         final List<Long> mainSampleIds = findSampleIds(criteria, otherSubCriterias);
 
-        final Set<Long> filteredSampleIds;
-        if (parentCriteria.isEmpty())
-        {
-            filteredSampleIds = new HashSet<Long>(mainSampleIds);
-        } else
+        final Set<Long> filteredSampleIds = new HashSet<Long>();
+        if (false == parentCriteria.isEmpty())
         {
             final List<Long> parentSampleIds =
                     findSampleIds(parentCriteria,
                             Collections.<DetailedSearchSubCriteria> emptyList());
-
-            filteredSampleIds = new HashSet<Long>();
-
             if (mainSampleIds.size() > parentSampleIds.size())
             {
-                // search for connections
-                Map<Long, Set<Long>> parentToChildIds =
-                        sampleLister.listChildrenIds(parentSampleIds);
-                for (Set<Long> childrenIds : parentToChildIds.values())
-                {
-                    filteredSampleIds.addAll(childrenIds);
-                }
-                // filter main parents
-                filteredSampleIds.retainAll(mainSampleIds);
+                listParentsChildrenAndFilterChildren(mainSampleIds, parentSampleIds,
+                        filteredSampleIds);
+            } else
+            {
+                listChildrensParentsAndFilterChildren(mainSampleIds, parentSampleIds,
+                        filteredSampleIds);
+            }
+        } else if (false == childCriteria.isEmpty())
+        {
+            final List<Long> childSampleIds =
+                    findSampleIds(childCriteria,
+                            Collections.<DetailedSearchSubCriteria> emptyList());
+            if (mainSampleIds.size() > childSampleIds.size())
+            {
+                listChildrensParentsAndFilterParents(childSampleIds, mainSampleIds,
+                        filteredSampleIds);
             } else
             {
-                // search for connections
-                Map<Long, Set<Long>> childToParentIds = sampleLister.listParentIds(mainSampleIds);
-
-                // filter main parents
-                for (Entry<Long, Set<Long>> entry : childToParentIds.entrySet())
-                {
-                    Long childId = entry.getKey();
-                    Set<Long> parentIds = entry.getValue();
-                    parentIds.retainAll(parentSampleIds);
-                    if (parentIds.isEmpty() == false)
-                    {
-                        filteredSampleIds.add(childId);
-                    }
-                }
+                listParentsChildrenAndFilterParents(childSampleIds, mainSampleIds,
+                        filteredSampleIds);
             }
+
+        } else
+        {
+            filteredSampleIds.addAll(mainSampleIds);
         }
         return sampleLister.list(new ListOrSearchSampleCriteria(filteredSampleIds));
     }
 
+    private void listChildrensParentsAndFilterChildren(final List<Long> allChildrenIds,
+            final List<Long> allParentIds, final Set<Long> filteredChildrenIds)
+    {
+        Map<Long, Set<Long>> childToParentIds = sampleLister.getChildToParentsIdsMap(allChildrenIds);
+        for (Entry<Long, Set<Long>> entry : childToParentIds.entrySet())
+        {
+            Long childId = entry.getKey();
+            Set<Long> parentIds = entry.getValue();
+            parentIds.retainAll(allParentIds);
+            if (parentIds.isEmpty() == false)
+            {
+                filteredChildrenIds.add(childId);
+            }
+        }
+    }
+
+    private void listParentsChildrenAndFilterParents(final List<Long> allChildrenIds,
+            final List<Long> allParentIds, final Set<Long> filteredParentIds)
+    {
+        Map<Long, Set<Long>> parentToChildIds = sampleLister.getParentToChildrenIdsMap(allParentIds);
+        for (Entry<Long, Set<Long>> entry : parentToChildIds.entrySet())
+        {
+            Long parentId = entry.getKey();
+            Set<Long> childIds = entry.getValue();
+            childIds.retainAll(allChildrenIds);
+            if (childIds.isEmpty() == false)
+            {
+                filteredParentIds.add(parentId);
+            }
+        }
+    }
+
+    private void listParentsChildrenAndFilterChildren(final List<Long> allChildrenIds,
+            final List<Long> allParentIds, final Set<Long> filteredChildrenIds)
+    {
+        Map<Long, Set<Long>> parentToChildIds = sampleLister.getParentToChildrenIdsMap(allParentIds);
+        for (Set<Long> childrenIds : parentToChildIds.values())
+        {
+            filteredChildrenIds.addAll(childrenIds);
+        }
+        filteredChildrenIds.retainAll(allChildrenIds);
+    }
+
+    private void listChildrensParentsAndFilterParents(final List<Long> allChildrenIds,
+            final List<Long> allParentIds, final Set<Long> filteredParentsIds)
+    {
+        Map<Long, Set<Long>> childToParentIds = sampleLister.getChildToParentsIdsMap(allChildrenIds);
+        for (Set<Long> parentIds : childToParentIds.values())
+        {
+            filteredParentsIds.addAll(parentIds);
+        }
+        filteredParentsIds.retainAll(allParentIds);
+    }
+
     private void groupSampleSubCriteria(List<DetailedSearchSubCriteria> allSubCriterias,
-            DetailedSearchCriteria parentCriteria, List<DetailedSearchSubCriteria> otherSubCriterias)
+            DetailedSearchCriteria parentCriteria, DetailedSearchCriteria childCriteria,
+            List<DetailedSearchSubCriteria> otherSubCriterias)
     {
         parentCriteria.setCriteria(new ArrayList<DetailedSearchCriterion>());
+        childCriteria.setCriteria(new ArrayList<DetailedSearchCriterion>());
         for (DetailedSearchSubCriteria subCriteria : allSubCriterias)
         {
             switch (subCriteria.getTargetEntityKind())
@@ -124,6 +174,13 @@ public class DetailedSearchManager
                     parentCriteria.setUseWildcardSearchMode(subCriteria.getCriteria()
                             .isUseWildcardSearchMode());
                     break;
+                case SAMPLE_CHILD:
+                    // merge all child sub criteria into one
+                    childCriteria.getCriteria().addAll(subCriteria.getCriteria().getCriteria());
+                    childCriteria.setConnection(subCriteria.getCriteria().getConnection());
+                    childCriteria.setUseWildcardSearchMode(subCriteria.getCriteria()
+                            .isUseWildcardSearchMode());
+                    break;
                 default:
                     otherSubCriterias.add(subCriteria);
             }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/samplelister/ISampleLister.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/samplelister/ISampleLister.java
index 14f10d8f3f9..cb7a2445b17 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/samplelister/ISampleLister.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/samplelister/ISampleLister.java
@@ -66,8 +66,10 @@ public interface ISampleLister
     public List<SampleRelationShipSkeleton> listSampleRelationShipsBy(
             IValidator<SampleRelationShipSkeleton> criteria);
 
-    public Map<Long, Set<Long>> listParentIds(Collection<Long> childrenIds);
+    /** Returns a map from child id to set of parents ids for specified children. */
+    public Map<Long, Set<Long>> getChildToParentsIdsMap(Collection<Long> childrenIds);
 
-    public Map<Long, Set<Long>> listChildrenIds(Collection<Long> parentIds);
+    /** Returns a map from parent id to set of children ids for specified parents. */
+    public Map<Long, Set<Long>> getParentToChildrenIdsMap(Collection<Long> parentIds);
 
 }
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/samplelister/SampleLister.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/samplelister/SampleLister.java
index 39ad734c765..63ffe527675 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/samplelister/SampleLister.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/server/business/bo/samplelister/SampleLister.java
@@ -120,7 +120,7 @@ public class SampleLister implements ISampleLister
         return result;
     }
 
-    public Map<Long, Set<Long>> listParentIds(Collection<Long> childrenIds)
+    public Map<Long, Set<Long>> getChildToParentsIdsMap(Collection<Long> childrenIds)
     {
         LongOpenHashSet ids = new LongOpenHashSet();
         for (Long id : childrenIds)
@@ -145,7 +145,7 @@ public class SampleLister implements ISampleLister
         return map;
     }
 
-    public Map<Long, Set<Long>> listChildrenIds(Collection<Long> parentIds)
+    public Map<Long, Set<Long>> getParentToChildrenIdsMap(Collection<Long> parentIds)
     {
         LongOpenHashSet ids = new LongOpenHashSet();
         for (Long id : parentIds)
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 c8a9f4ca752..d645c3df48a 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
@@ -19,12 +19,12 @@ 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.AssociatedEntityKind;
 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.basic.dto.AssociatedEntityKind;
 import ch.systemsx.cisd.openbis.generic.shared.dto.hibernate.SearchFieldConstants;
 
 /**
@@ -56,11 +56,12 @@ class IndexFieldNameHelper
             case SAMPLE_CONTAINER:
                 if (entityKind == EntityKind.SAMPLE)
                 {
-                    return SearchFieldConstants.SAMPLE_ID;
+                    return SearchFieldConstants.CONTAINER_ID;
                 }
                 throw createAssociationNotHandledException(entityKind, associationKind);
             case SAMPLE_PARENT:
-                // sample parent is a many-to-many connection - it is not handled by lucene index
+            case SAMPLE_CHILD:
+                // parent-child is a many-to-many connection - it is not handled by lucene index
                 throw createAssociationNotHandledException(entityKind, associationKind);
         }
         return null; // shouldn't happen
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
index ab2d31f0216..03f59d5a27d 100644
--- 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
@@ -27,6 +27,11 @@ public class SearchSubCriteria implements Serializable
         return new SearchSubCriteria(SearchableEntityKind.SAMPLE_PARENT, criteria);
     }
 
+    public static SearchSubCriteria createSampleChildCriteria(SearchCriteria criteria)
+    {
+        return new SearchSubCriteria(SearchableEntityKind.SAMPLE_CHILD, criteria);
+    }
+
     public static SearchSubCriteria createSampleContainerCriteria(SearchCriteria criteria)
     {
         return new SearchSubCriteria(SearchableEntityKind.SAMPLE_CONTAINER, criteria);
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
index 9b40de745aa..3414819537a 100644
--- 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
@@ -9,5 +9,5 @@ public enum SearchableEntityKind
 {
     SAMPLE, EXPERIMENT,
     // sample subcriteria
-    SAMPLE_CONTAINER, SAMPLE_PARENT, // SAMPLE_CHILD
+    SAMPLE_CONTAINER, SAMPLE_PARENT, SAMPLE_CHILD
 }
\ No newline at end of file
diff --git a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/AssociatedEntityKind.java b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/AssociatedEntityKind.java
index f5bea334d9e..e50c8df8b0e 100644
--- a/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/AssociatedEntityKind.java
+++ b/openbis/source/java/ch/systemsx/cisd/openbis/generic/shared/basic/dto/AssociatedEntityKind.java
@@ -32,7 +32,9 @@ public enum AssociatedEntityKind implements ISerializable
 
     SAMPLE_CONTAINER("Container", EntityKind.SAMPLE),
 
-    SAMPLE_PARENT("Parent", EntityKind.SAMPLE);
+    SAMPLE_PARENT("Parent", EntityKind.SAMPLE),
+
+    SAMPLE_CHILD("Child", EntityKind.SAMPLE);
 
     private final String description;
 
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 7a3fe63546d..567809831e1 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
@@ -382,9 +382,9 @@ public class SamplePE extends AttachmentHolderPE implements IIdAndCodeHolder, Co
     private Long getContainerId()
     {
         Long result = null;
-        if (getExperimentInternal() != null)
+        if (getContainer() != null)
         {
-            result = HibernateUtils.getId(getExperimentInternal());
+            result = HibernateUtils.getId(getContainer());
             assert result != null;
         }
         return result;
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 a7375d49a12..55f584dbe43 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
@@ -264,6 +264,30 @@ public class GeneralInformationServiceTest extends AbstractServerTestCase
         context.assertIsSatisfied();
     }
 
+    @Test
+    public void testSearchForSamplesWithChild()
+    {
+        prepareGetSession();
+        final RecordingMatcher<DetailedSearchCriteria> detailedSearchCriteriaMatcher =
+                RecordingMatcher.create();
+        final RecordingMatcher<List<DetailedSearchSubCriteria>> detailedSearchSubCriteriaMatcher =
+                RecordingMatcher.create();
+        prepareSearchForSamples(detailedSearchCriteriaMatcher, detailedSearchSubCriteriaMatcher);
+        List<Sample> result =
+                service.searchForSamples(SESSION_TOKEN, createSearchCriteriaForSampleWithChild());
+        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 parent subcriteria
+        assertEquals("[SAMPLE_CHILD: ATTRIBUTE CODE: child code AND "
+                + "PROPERTY CHILD_PROPERTY: child property value (with wildcards)]",
+                detailedSearchSubCriteriaMatcher.recordedObject().toString());
+        context.assertIsSatisfied();
+    }
+
     @Test
     public void testSearchForSamplesWithContainer()
     {
@@ -498,6 +522,14 @@ public class GeneralInformationServiceTest extends AbstractServerTestCase
         return sc;
     }
 
+    private SearchCriteria createSearchCriteriaForSampleChild()
+    {
+        SearchCriteria sc = new SearchCriteria();
+        sc.addMatchClause(MatchClause.createAttributeMatch(MatchClauseAttribute.CODE, "child code"));
+        sc.addMatchClause(MatchClause.createPropertyMatch("CHILD_PROPERTY", "child property value"));
+        return sc;
+    }
+
     private SearchCriteria createSearchCriteriaForSampleContainer()
     {
         SearchCriteria sc = new SearchCriteria();
@@ -534,6 +566,14 @@ public class GeneralInformationServiceTest extends AbstractServerTestCase
         return mainCriteria;
     }
 
+    private SearchCriteria createSearchCriteriaForSampleWithChild()
+    {
+        SearchCriteria mainCriteria = createSearchCriteriaForSample();
+        SearchCriteria childCriteria = createSearchCriteriaForSampleChild();
+        mainCriteria.addSubCriteria(SearchSubCriteria.createSampleChildCriteria(childCriteria));
+        return mainCriteria;
+    }
+
     private SearchCriteria createSearchCriteriaForSampleWithContainer()
     {
         SearchCriteria mainCriteria = createSearchCriteriaForSample();
diff --git a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/api/v1/GeneralInformationServiceTest.java b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/api/v1/GeneralInformationServiceTest.java
index d27cfd3f9a0..030b5ce6207 100644
--- a/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/api/v1/GeneralInformationServiceTest.java
+++ b/openbis/sourceTest/java/ch/systemsx/cisd/openbis/systemtest/api/v1/GeneralInformationServiceTest.java
@@ -17,6 +17,7 @@
 package ch.systemsx.cisd.openbis.systemtest.api.v1;
 
 import static org.testng.AssertJUnit.assertEquals;
+import static org.testng.AssertJUnit.assertTrue;
 import static org.testng.AssertJUnit.fail;
 
 import java.util.ArrayList;
@@ -170,6 +171,36 @@ public class GeneralInformationServiceTest extends SystemTestCase
         assertEquals(2, result.size());
     }
 
+    @Test
+    public void testSearchForSamplesByChildCode()
+    {
+        // Search for Samples with only child's code limiting the results
+        SearchCriteria sc = new SearchCriteria();
+        sc.addMatchClause(MatchClause.createAttributeMatch(MatchClauseAttribute.CODE, "*"));
+        SearchCriteria cc = new SearchCriteria();
+        cc.addMatchClause(MatchClause.createAttributeMatch(MatchClauseAttribute.CODE, "3VCP*"));
+        sc.addSubCriteria(SearchSubCriteria.createSampleChildCriteria(cc));
+        List<Sample> result = generalInformationService.searchForSamples(sessionToken, sc);
+        assertEquals(2, result.size());
+    }
+
+    @Test
+    public void testSearchForSamplesByContainerCode()
+    {
+        // Search for Samples with only container's code limiting the results
+        SearchCriteria sc = new SearchCriteria();
+        sc.addMatchClause(MatchClause.createAttributeMatch(MatchClauseAttribute.CODE, "*"));
+        SearchCriteria cc = new SearchCriteria();
+        cc.addMatchClause(MatchClause.createAttributeMatch(MatchClauseAttribute.CODE, "CL1"));
+        sc.addSubCriteria(SearchSubCriteria.createSampleContainerCriteria(cc));
+        List<Sample> result = generalInformationService.searchForSamples(sessionToken, sc);
+        assertEquals(2, result.size());
+        for (Sample s : result)
+        {
+            assertTrue(s.getCode() + "doesn't start with 'CL1:'", s.getCode().startsWith("CL1:"));
+        }
+    }
+
     @Test
     public void testSearchForSamplesByExperimentAndParentCode()
     {
-- 
GitLab