diff --git a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/DataSet.java b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/DataSet.java
index 336ddb82b03db5defc3a51049d0e1ade9fae5051..413e7c02cdc1810eabf89c9cffc21806196508d5 100644
--- a/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/DataSet.java
+++ b/datastore_server/source/java/ch/systemsx/cisd/openbis/dss/client/api/v1/DataSet.java
@@ -23,6 +23,8 @@ import java.util.Date;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import ch.systemsx.cisd.common.api.retry.Retry;
 import ch.systemsx.cisd.common.api.retry.RetryCaller;
@@ -188,6 +190,66 @@ public class DataSet
         return containedDataSets;
     }
 
+    /**
+     * Returns the primary data set. For a non-container data set, this is itself. For a container
+     * data set, this is the one contained data set that is considered primary.
+     * 
+     * @return The data set that is considered primary, or null if the primary data set cannot be
+     *         determined.
+     */
+    @Retry
+    public DataSet getPrimaryDataSetOrNull()
+    {
+        if (false == isContainerDataSet())
+        {
+            return this;
+        }
+
+        // Find the contained data set that follows the specified naming convention
+        Pattern containerDataSetTypePattern = Pattern.compile("(.*)_CONTAINER(.*)");
+        // See if the container follows the pattern
+        Matcher matcher = containerDataSetTypePattern.matcher(getDataSetTypeCode());
+        if (false == matcher.matches())
+        {
+            // We do not know how to figure out what the primary data set might be
+            return null;
+        }
+
+        // The primary data set type is the same as the container with the "_CONTAINER" removed
+        String primaryDataSetType = matcher.group(1);
+        if (null == primaryDataSetType)
+        {
+            primaryDataSetType = matcher.group(2);
+        } else
+        {
+            if (null != matcher.group(2))
+            {
+                primaryDataSetType = primaryDataSetType + matcher.group(2);
+            }
+        }
+        if (null == primaryDataSetType)
+        {
+            return null;
+        }
+
+        List<DataSet> contained = getContainedDataSets();
+        List<DataSet> matchedDataSets = new ArrayList<DataSet>();
+        for (DataSet ds : contained)
+        {
+            if (primaryDataSetType.equals(ds.getDataSetTypeCode()))
+            {
+                matchedDataSets.add(ds);
+            }
+        }
+
+        // Return the single match, or null otherwise
+        if (1 == matchedDataSets.size())
+        {
+            return matchedDataSets.get(0);
+        }
+        return null;
+    }
+
     /**
      * @see ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet#equals(java.lang.Object)
      */
@@ -195,7 +257,17 @@ public class DataSet
     @Retry
     public boolean equals(Object obj)
     {
-        return getMetadata().equals(obj);
+        if (obj == this)
+        {
+            return true;
+        }
+        if (obj instanceof DataSet == false)
+        {
+            return false;
+        }
+
+        DataSet other = (DataSet) obj;
+        return getMetadata().equals(other.getMetadata());
     }
 
     /**
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/datastoreserver/systemtests/OpenbisServiceFacadeTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/datastoreserver/systemtests/OpenbisServiceFacadeTest.java
index f3e4beb94319a717d1a5ed47f773b787367ab7b6..639f5aa7d4298d47d8b2dd194c2180f2bd75966d 100644
--- a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/datastoreserver/systemtests/OpenbisServiceFacadeTest.java
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/datastoreserver/systemtests/OpenbisServiceFacadeTest.java
@@ -160,6 +160,10 @@ public class OpenbisServiceFacadeTest extends SystemTestCase
 
         List<DataSet> contained = ds.getContainedDataSets();
         assertEquals(0, contained.size());
+
+        // The primary data set for a normal (non-container) data set is itself
+        assertNotNull(ds.getPrimaryDataSetOrNull());
+        assertEquals(ds, ds.getPrimaryDataSetOrNull());
     }
 
     private static String fileInfoString(String startPath, String pathInListing, long length)
diff --git a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/client/api/v1/impl/OpenbisServiceFacadeTest.java b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/client/api/v1/impl/OpenbisServiceFacadeTest.java
index 2bfdecb9bd2294b805e957ea3d2e609ca04b0bad..a33fb345ef6c0197d2d539211e2dbeb21cb5d3f3 100644
--- a/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/client/api/v1/impl/OpenbisServiceFacadeTest.java
+++ b/datastore_server/sourceTest/java/ch/systemsx/cisd/openbis/dss/client/api/v1/impl/OpenbisServiceFacadeTest.java
@@ -268,8 +268,13 @@ public class OpenbisServiceFacadeTest extends AssertJUnit
     {
         final List<String> codes = unmodifiableList("DATA-SET-1", "DATA-SET-2");
         final List<ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet> dataSets =
-                unmodifiableList(createDataSet("DATA-SET-1", experimentIdentifier("E1"), null),
-                        createDataSet("DATA-SET-2", experimentIdentifier("E2"), null));
+                unmodifiableList(
+                        createDataSet("DATA-SET-1", experimentIdentifier("E1"), null),
+                        createDataSet("DATA-SET-2", experimentIdentifier("E2"), null,
+                                "data-set-alternative-type"));
+        final ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet containerDataSet =
+                createContainerDataSet("DATA-SET-CONTAINER-1", experimentIdentifier("E1"), null,
+                        dataSets);
 
         final RecordingMatcher<SearchCriteria> criteriaMatcher =
                 new RecordingMatcher<SearchCriteria>();
@@ -279,6 +284,9 @@ public class OpenbisServiceFacadeTest extends AssertJUnit
                     one(service).searchForDataSets(with(equal(SESSION_TOKEN)),
                             with(criteriaMatcher));
                     will(returnValue(dataSets));
+                    one(service).searchForDataSets(with(equal(SESSION_TOKEN)),
+                            with(criteriaMatcher));
+                    will(returnValue(unmodifiableList(containerDataSet)));
                 }
             });
 
@@ -286,12 +294,21 @@ public class OpenbisServiceFacadeTest extends AssertJUnit
         // dataSets and result do not have the same type, but should have the same string
         // representation.
         assertEquals(dataSets.toString(), result.toString());
-        SearchCriteria criteria = criteriaMatcher.recordedObject();
+        // A normal data set is its own primary data set
+        assertEquals(result.get(0), result.get(0).getPrimaryDataSetOrNull());
+        SearchCriteria criteria = criteriaMatcher.getRecordedObjects().get(0);
         assertNotNull(criteria);
         assertEquals(SearchOperator.MATCH_ANY_CLAUSES, criteria.getOperator());
         assertEquals(codes.size(), criteria.getMatchClauses().size());
         assertMatchClauseForCode("DATA-SET-1", criteria.getMatchClauses().get(0));
         assertMatchClauseForCode("DATA-SET-2", criteria.getMatchClauses().get(1));
+
+        // Try to get the container data set now
+        DataSet container = openbisFacade.getDataSet("DATA-SET-CONTAINER-1");
+        DataSet primaryDataSet = container.getPrimaryDataSetOrNull();
+        assertNotNull(primaryDataSet);
+        assertEquals("DATA-SET-1", primaryDataSet.getCode());
+
     }
 
     @Test
@@ -537,7 +554,30 @@ public class OpenbisServiceFacadeTest extends AssertJUnit
     }
 
     private ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet createDataSet(String code,
-            String exprimentId, String sampleIdOrNull)
+            String experimentId, String sampleIdOrNull)
+    {
+        return createDataSet(code, experimentId, sampleIdOrNull, "data-set-type");
+    }
+
+    private ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet createDataSet(String code,
+            String experimentId, String sampleIdOrNull, String dataSetType)
+    {
+        EntityRegistrationDetailsInitializer initializer =
+                new EntityRegistrationDetailsInitializer();
+        EntityRegistrationDetails registrationDetails = new EntityRegistrationDetails(initializer);
+
+        DataSetInitializer init = new DataSetInitializer();
+        init.setCode(code);
+        init.setDataSetTypeCode(dataSetType);
+        init.setExperimentIdentifier(experimentId);
+        init.setSampleIdentifierOrNull(sampleIdOrNull);
+        init.setRegistrationDetails(registrationDetails);
+        return new ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet(init);
+    }
+
+    private ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet createContainerDataSet(
+            String code, String exprimentId, String sampleIdOrNull,
+            List<ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet> containedDataSets)
     {
         EntityRegistrationDetailsInitializer initializer =
                 new EntityRegistrationDetailsInitializer();
@@ -545,10 +585,12 @@ public class OpenbisServiceFacadeTest extends AssertJUnit
 
         DataSetInitializer init = new DataSetInitializer();
         init.setCode(code);
-        init.setDataSetTypeCode("data-set-type");
+        init.setDataSetTypeCode("data-set_CONTAINER-type");
         init.setExperimentIdentifier(exprimentId);
         init.setSampleIdentifierOrNull(sampleIdOrNull);
         init.setRegistrationDetails(registrationDetails);
+        init.setContainerDataSet(true);
+        init.setContainedDataSets(containedDataSets);
         return new ch.systemsx.cisd.openbis.generic.shared.api.v1.dto.DataSet(init);
     }