diff --git a/api-openbis-javascript/src/v3/imaging/dto/ImagingDataSetMultiExport.js b/api-openbis-javascript/src/v3/imaging/dto/ImagingDataSetMultiExport.js
index 55a2801e0f59081afd8aa7114b379795e73ba6aa..4478070ef38e24f72e5c2849d5f94e1f33b83cee 100644
--- a/api-openbis-javascript/src/v3/imaging/dto/ImagingDataSetMultiExport.js
+++ b/api-openbis-javascript/src/v3/imaging/dto/ImagingDataSetMultiExport.js
@@ -5,7 +5,8 @@ define([ "stjs", "util/Exceptions" ], function(stjs, exceptions) {
 		prototype['@type'] = 'imaging.dto.ImagingDataSetMultiExport';
 		constructor.serialVersionUID = 1;
 		prototype.permId = null;
-        prototype.index = null;
+        prototype.imageIndex = null;
+        prototype.previewIndex = null;
 		prototype.config = null;
 		prototype.metadata = null;
 
@@ -15,11 +16,17 @@ define([ "stjs", "util/Exceptions" ], function(stjs, exceptions) {
 		prototype.setPermId = function(permId) {
 			this.permId = permId;
 		};
-		prototype.getIndex = function() {
-            return this.index;
+		prototype.getImageIndex = function() {
+            return this.imageIndex;
         };
-        prototype.setIndex = function(index) {
-            this.index = index;
+        prototype.setImageIndex = function(imageIndex) {
+            this.imageIndex = imageIndex;
+        };
+        prototype.getPreviewIndex = function() {
+            return this.previewIndex;
+        };
+        prototype.setPreviewIndex = function(previewIndex) {
+            this.previewIndex = previewIndex;
         };
 		prototype.getConfig = function() {
 			return this.config;
diff --git a/docs/software-developer-documentation/apis/img/semantic-annotations-classic-nomenclature.png b/docs/software-developer-documentation/apis/img/semantic-annotations-classic-nomenclature.png
new file mode 100644
index 0000000000000000000000000000000000000000..a5b2957c5d4fd1aa985cd45b6951bfc44d116cc5
Binary files /dev/null and b/docs/software-developer-documentation/apis/img/semantic-annotations-classic-nomenclature.png differ
diff --git a/docs/software-developer-documentation/apis/img/semantic-annotations-modern-nomenclature.png b/docs/software-developer-documentation/apis/img/semantic-annotations-modern-nomenclature.png
new file mode 100644
index 0000000000000000000000000000000000000000..77de5765c4b13efab8f86531336b9c11892cf1b7
Binary files /dev/null and b/docs/software-developer-documentation/apis/img/semantic-annotations-modern-nomenclature.png differ
diff --git a/docs/software-developer-documentation/apis/img/semantic-annotations-openbis-nomenclature.png b/docs/software-developer-documentation/apis/img/semantic-annotations-openbis-nomenclature.png
new file mode 100644
index 0000000000000000000000000000000000000000..1b547ea5452fedff0360d079dd3a226e371ea8f4
Binary files /dev/null and b/docs/software-developer-documentation/apis/img/semantic-annotations-openbis-nomenclature.png differ
diff --git a/docs/software-developer-documentation/apis/index.rst b/docs/software-developer-documentation/apis/index.rst
index 68618580d1776b255f02bf798a1185929cb52620..d845a0f6157d0f0f90c6388bcd0b325ec7e1b737 100644
--- a/docs/software-developer-documentation/apis/index.rst
+++ b/docs/software-developer-documentation/apis/index.rst
@@ -7,4 +7,5 @@ APIS
    java-javascript-v3-api
    python-v3-api
    matlab-v3-api
-   personal-access-tokens
\ No newline at end of file
+   personal-access-tokens
+   semantic-annotations
\ No newline at end of file
diff --git a/docs/software-developer-documentation/apis/semantic-annotations.md b/docs/software-developer-documentation/apis/semantic-annotations.md
new file mode 100644
index 0000000000000000000000000000000000000000..d4f023dec839005d959a9740e6cdd288f142c126
--- /dev/null
+++ b/docs/software-developer-documentation/apis/semantic-annotations.md
@@ -0,0 +1,486 @@
+# Semantic Annotations
+
+- Introduction
+    - Classic Nomenclature
+    - Modern Nomenclature 
+    - openBIS Nomenclature 
+- openBIS Implementation : Java Examples
+    - Use Case 1 : Annotating a Semantic Class corresponds to Annotating an openBIS Type
+    - Use Case 2 : Annotating a Semantic Class Property corresponds to Annotating an openBIS Property Assignment
+    - Use Case 3 : Annotating a Semantic Property corresponds to Annotating an openBIS Property
+    - Search Based on Semantic Annotations
+    - Helper Class - Semantic API Extensions
+
+## Introduction
+Semantic annotations were created as a means of standardisation to provide interoperability between systems.
+Different systems can then name the same kinds of data differently, but providing the same semantic annotations systems could theoretically interoperate.
+One of the biggest hurdles to overcome when trying to understand semantic annotations is the nomenclature. There is a plethora of information often using different terms.
+We will now introduce the main three nomenclatures to help the reader to familiarise with them.
+
+Classic Nomenclature
+This is typically found in literature.
+
+![Semantic Annotations Classic Nomenclature](img/semantic-annotations-classic-nomenclature.png "Semantic Annotations Classic Nomenclature")
+
+For reference: http://www.linkeddatatools.com/semantic-web-basics
+
+Modern Nomenclature 
+This is typically found in standards.
+
+![Semantic Annotations Modern Nomenclature](img/semantic-annotations-modern-nomenclature.png "Semantic Annotations Modern Nomenclature")
+
+For reference: https://www.biomedit.ch/rdf/sphn-ontology/sphn/2022/2
+
+openBIS Nomenclature 
+This is typically found in systems or programming languages.
+
+![Semantic Annotations openBIS Nomenclature](img/semantic-annotations-openbis-nomenclature.png "Semantic Annotations openBIS Nomenclature")
+
+openBIS Implementation : Java Examples
+
+## Use Case 1 : Annotating a Semantic Class corresponds to Annotating an openBIS Type
+openBIS allows you to annotate Types using semantic annotation URIs through the use of its API using SemanticAnnotationCreation. 
+In the Example below:
+The openBIS Sample Type ADMINISTRATIVE_GENDER is annotated with the semantic class AdministrativeGender.
+package ch.ethz.sis.pat;
+ 
+```java
+import ch.ethz.sis.openbis.generic.asapi.v3.IApplicationServerApi;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.SearchResult;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.EntityKind;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.semanticannotation.create.SemanticAnnotationCreation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.semanticannotation.id.SemanticAnnotationPermId;
+import ch.systemsx.cisd.common.spring.HttpInvokerUtils;
+ 
+import java.util.List;
+import java.util.Map;
+ 
+public class MainSemantic {
+ 
+    private static final String URL = "https://openbis-sis-ci-sprint.ethz.ch/openbis/openbis" + IApplicationServerApi.SERVICE_URL;
+    private static final int TIMEOUT = 10000;
+ 
+    private static final String USER = "admin";
+    private static final String PASSWORD = "changeit";
+ 
+    public static void main(String[] args) {
+        IApplicationServerApi v3 = HttpInvokerUtils.createServiceStub(IApplicationServerApi.class, URL, TIMEOUT);
+        String sessionToken = v3.login(USER, PASSWORD);
+ 
+        System.out.println("sessionToken: " + sessionToken);
+ 
+        // Creating semantic annotations using helper methods
+        SemanticAnnotationCreation administrative_gender = ApplicationServerSemanticAPIExtensions.getSemanticSubjectCreation(
+                EntityKind.SAMPLE,
+                "ADMINISTRATIVE_GENDER",
+                "https://biomedit.ch/rdf/sphn-ontology/sphn",
+                "https://biomedit.ch/rdf/sphn-ontology/sphn/2022/2",
+                "https://biomedit.ch/rdf/sphn-ontology/sphn#AdministrativeGender");
+ 
+        List<SemanticAnnotationPermId> annotations = v3.createSemanticAnnotations(sessionToken, List.of(administrative_gender));
+        System.out.println("created annotations: " + annotations);
+ 
+        v3.logout(sessionToken);
+    }
+}
+```
+
+## Use Case 2 : Annotating a Semantic Class Property corresponds to Annotating an openBIS Property Assignment
+openBIS allows you to annotate Property Assignments using semantic annotation URIs through the use of its API using SemanticAnnotationCreation. 
+In the Example below:
+The openBIS Property Type IDENTIFIER of the Type ADMINISTRATIVE_GENDER is annotated with the semantic property hasIdentifier.
+package ch.ethz.sis.pat;
+ 
+```java
+import ch.ethz.sis.openbis.generic.asapi.v3.IApplicationServerApi;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.SearchResult;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.EntityKind;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.semanticannotation.create.SemanticAnnotationCreation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.semanticannotation.id.SemanticAnnotationPermId;
+import ch.systemsx.cisd.common.spring.HttpInvokerUtils;
+ 
+import java.util.List;
+import java.util.Map;
+ 
+public class MainSemantic {
+ 
+    private static final String URL = "https://openbis-sis-ci-sprint.ethz.ch/openbis/openbis" + IApplicationServerApi.SERVICE_URL;
+    private static final int TIMEOUT = 10000;
+ 
+    private static final String USER = "admin";
+    private static final String PASSWORD = "changeit";
+ 
+    public static void main(String[] args) {
+        IApplicationServerApi v3 = HttpInvokerUtils.createServiceStub(IApplicationServerApi.class, URL, TIMEOUT);
+        String sessionToken = v3.login(USER, PASSWORD);
+ 
+        System.out.println("sessionToken: " + sessionToken);         
+        SemanticAnnotationCreation  identifier = ApplicationServerSemanticAPIExtensions.getSemanticPredicateWithSubjectCreation(
+                EntityKind.SAMPLE,
+                "ADMINISTRATIVE_GENDER",
+                "identifier",
+                "https://biomedit.ch/rdf/sphn-ontology/sphn",
+                "https://biomedit.ch/rdf/sphn-ontology/sphn/2022/2",
+                "https://biomedit.ch/rdf/sphn-ontology/sphn#hasIdentifier");         
+        List<SemanticAnnotationPermId> annotations = v3.createSemanticAnnotations(sessionToken, List.of(identifier));
+        System.out.println("created annotations: " + annotations);
+ 
+        v3.logout(sessionToken);
+    }
+}
+```
+
+## Use Case 3 : Annotating a Semantic Property corresponds to Annotating an openBIS Property
+Even if less common, sometimes we can be under the need of annotating a predicate without a subject.
+openBIS allows you to annotate Property Types without Types using semantic annotation URIs through the use of its API using SemanticAnnotationCreation. 
+In the Example below:
+The openBIS Property Type IDENTIFIER is annotated with the semantic property hasIdentifier.
+package ch.ethz.sis.pat;
+
+```java
+import ch.ethz.sis.openbis.generic.asapi.v3.IApplicationServerApi;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.SearchResult;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.EntityKind;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.semanticannotation.create.SemanticAnnotationCreation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.semanticannotation.id.SemanticAnnotationPermId;
+import ch.systemsx.cisd.common.spring.HttpInvokerUtils;
+ 
+import java.util.List;
+import java.util.Map;
+ 
+public class MainSemantic {
+ 
+    private static final String URL = "https://openbis-sis-ci-sprint.ethz.ch/openbis/openbis" + IApplicationServerApi.SERVICE_URL;
+    private static final int TIMEOUT = 10000;
+ 
+    private static final String USER = "admin";
+    private static final String PASSWORD = "changeit";
+ 
+    public static void main(String[] args) {
+        IApplicationServerApi v3 = HttpInvokerUtils.createServiceStub(IApplicationServerApi.class, URL, TIMEOUT);
+        String sessionToken = v3.login(USER, PASSWORD);
+ 
+        System.out.println("sessionToken: " + sessionToken);         
+        SemanticAnnotationCreation  identifierOnly = ApplicationServerSemanticAPIExtensions.getSemanticPredicateCreation(
+                "identifier",
+                "https://biomedit.ch/rdf/sphn-ontology/sphn",
+                "https://biomedit.ch/rdf/sphn-ontology/sphn/2022/2",
+                "https://biomedit.ch/rdf/sphn-ontology/sphn#hasIdentifier");      
+        List<SemanticAnnotationPermId> annotations = v3.createSemanticAnnotations(sessionToken, List.of(identifierOnly));
+        System.out.println("created annotations: " + annotations);
+ 
+        v3.logout(sessionToken);
+    }
+}
+```
+
+## Search Based on Semantic Annotations
+openBIS search doesn't directly allow a search based on semantic annotations. This is currently a two steps process.
+On the next example we reduce this two step process to a single call for what we believe is the most common use case: Search for subject and predicate of a well known ontology. 
+We build this example on top of the previous example. We would like now to make a search based on the previously created subject (AdministrativeGender) and predicate (hasIdentifier) to finally obtain Samples having the identifier "12345678".
+The reader will appreciate that this search can be done without having previous knowledge of how this semantic annotations map to openBIS types and property types. See example below.
+package ch.ethz.sis.pat;
+
+```java
+import ch.ethz.sis.openbis.generic.asapi.v3.IApplicationServerApi;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.SearchResult;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.EntityKind;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.semanticannotation.create.SemanticAnnotationCreation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.semanticannotation.id.SemanticAnnotationPermId;
+import ch.systemsx.cisd.common.spring.HttpInvokerUtils;
+ 
+import java.util.List;
+import java.util.Map;
+ 
+public class MainSemantic {
+ 
+    private static final String URL = "https://openbis-sis-ci-sprint.ethz.ch/openbis/openbis" + IApplicationServerApi.SERVICE_URL;
+    private static final int TIMEOUT = 10000;
+ 
+    private static final String USER = "admin";
+    private static final String PASSWORD = "changeit";
+ 
+    public static void main(String[] args) {
+        IApplicationServerApi v3 = HttpInvokerUtils.createServiceStub(IApplicationServerApi.class, URL, TIMEOUT);
+        String sessionToken = v3.login(USER, PASSWORD);
+ 
+        System.out.println("sessionToken: " + sessionToken);
+ 
+        // Searching semantic annotations using helper methods
+        SearchResult searchResult = ApplicationServerSemanticAPIExtensions.searchEntityWithSemanticAnnotations(v3, sessionToken,
+                EntityKind.SAMPLE,
+                "https://biomedit.ch/rdf/sphn-ontology/sphn#AdministrativeGender",
+                Map.of("https://biomedit.ch/rdf/sphn-ontology/sphn#hasIdentifier", "12345678"),
+                0,
+                Integer.MAX_VALUE
+        );
+ 
+        System.out.println("Found Entities: " + searchResult.getTotalCount());
+        v3.logout(sessionToken);
+    }
+}
+```
+
+## Helper Class - Semantic API Extensions
+To facilitate previous straightforward examples we have created the next utility class:
+package ch.ethz.sis.pat;
+ 
+```java
+import ch.ethz.sis.openbis.generic.asapi.v3.IApplicationServerApi;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.fetchoptions.FetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.AbstractEntitySearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.SearchResult;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.fetchoptions.DataSetFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.search.DataSetSearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.EntityKind;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.entitytype.id.EntityTypePermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.fetchoptions.ExperimentFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.experiment.search.ExperimentSearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.fetchoptions.PropertyAssignmentFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.id.PropertyAssignmentPermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.property.id.PropertyTypePermId;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.fetchoptions.SampleFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.sample.search.SampleSearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.semanticannotation.SemanticAnnotation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.semanticannotation.create.SemanticAnnotationCreation;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.semanticannotation.fetchoptions.SemanticAnnotationFetchOptions;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.semanticannotation.search.SemanticAnnotationSearchCriteria;
+import ch.systemsx.cisd.common.exceptions.UserFailureException;
+ 
+import java.util.Map;
+ 
+public abstract class ApplicationServerSemanticAPIExtensions {
+ 
+    /**
+     * This utility method provides a simplified API to create subject semantic annotations
+     *
+     */
+    public static SemanticAnnotationCreation getSemanticSubjectCreation(    EntityKind subjectEntityKind,
+                                                                            String subjectClass,
+                                                                             String subjectClassOntologyId,
+                                                                             String subjectClassOntologyVersion,
+                                                                             String subjectClassId) {
+        SemanticAnnotationCreation semanticAnnotationCreation = new SemanticAnnotationCreation();
+        // Subject: Type matching an ontology class
+        semanticAnnotationCreation.setEntityTypeId(new EntityTypePermId(subjectClass, subjectEntityKind));
+        // Ontology URL
+        semanticAnnotationCreation.setPredicateOntologyId(subjectClassOntologyId);
+        semanticAnnotationCreation.setDescriptorOntologyId(subjectClassOntologyId);
+        // Ontology Version URL
+        semanticAnnotationCreation.setPredicateOntologyVersion(subjectClassOntologyVersion);
+        semanticAnnotationCreation.setDescriptorOntologyVersion(subjectClassOntologyVersion);
+        // Ontology Class URL
+        semanticAnnotationCreation.setPredicateAccessionId(subjectClassId);
+        semanticAnnotationCreation.setDescriptorAccessionId(subjectClassId);
+        return semanticAnnotationCreation;
+    }
+ 
+    /**
+     * This utility method provides a simplified API to create predicate semantic annotations
+     *
+     */
+    public static SemanticAnnotationCreation getSemanticPredicateWithSubjectCreation( EntityKind subjectEntityKind,
+                                                                            String subjectClass,
+                                                                            String predicateProperty,
+                                                                            String predicatePropertyOntologyId,
+                                                                            String predicatePropertyOntologyVersion,
+                                                                            String predicatePropertyId) {
+        SemanticAnnotationCreation semanticAnnotationCreation = new SemanticAnnotationCreation();
+        // Subject: Type matching an ontology class
+        // Predicate: Property matching an ontology class property
+        semanticAnnotationCreation.setPropertyAssignmentId(new PropertyAssignmentPermId(
+                new EntityTypePermId(subjectClass, subjectEntityKind),
+                new PropertyTypePermId(predicateProperty)));
+        // Ontology URL
+        semanticAnnotationCreation.setPredicateOntologyId(predicatePropertyOntologyId);
+        // Ontology Version URL
+        semanticAnnotationCreation.setPredicateOntologyVersion(predicatePropertyOntologyVersion);
+        // Ontology Property URL
+        semanticAnnotationCreation.setPredicateAccessionId(predicatePropertyId);
+        return semanticAnnotationCreation;
+    }
+ 
+    /**
+     * This utility method provides a simplified API to create predicate semantic annotations
+     *
+     */
+    public static SemanticAnnotationCreation getSemanticPredicateCreation( String predicateProperty,
+                                                                                      String predicatePropertyOntologyId,
+                                                                                      String predicatePropertyOntologyVersion,
+                                                                                      String predicatePropertyId) {
+        SemanticAnnotationCreation semanticAnnotationCreation = new SemanticAnnotationCreation();
+        // Predicate: Property matching an ontology class property
+        semanticAnnotationCreation.setPropertyTypeId(new PropertyTypePermId(predicateProperty));
+        // Ontology URL
+        semanticAnnotationCreation.setPredicateOntologyId(predicatePropertyOntologyId);
+        // Ontology Version URL
+        semanticAnnotationCreation.setPredicateOntologyVersion(predicatePropertyOntologyVersion);
+        // Ontology Property URL
+        semanticAnnotationCreation.setPredicateAccessionId(predicatePropertyId);
+        return semanticAnnotationCreation;
+    }
+ 
+    /**
+     * This utility method provides a simplified API to search based on semantic subjects and predicates
+     *
+     * @throws UserFailureException in case of any problems
+     */
+    public static SearchResult searchEntityWithSemanticAnnotations(IApplicationServerApi v3,
+                                                                           String sessionToken,
+                                                                           EntityKind entityKind,
+                                                                           String subjectClassIDOrNull,
+                                                                           Map<String, String> predicatePropertyIDsOrNull,
+                                                                           Integer fromOrNull,
+                                                                           Integer countOrNull) {
+        if (entityKind == null) {
+            throw new UserFailureException("entityKind cannot be null");
+        }
+        if (entityKind == EntityKind.DATA_SET) {
+            throw new UserFailureException("EntityKind.DATA_SET is not supported");
+        }
+ 
+        //
+        // Part 1 : Translate semantic classes and properties into openBIS types and property types
+        //
+ 
+        SemanticAnnotationSearchCriteria semanticCriteria = new SemanticAnnotationSearchCriteria();
+        semanticCriteria.withOrOperator();
+ 
+        SemanticAnnotationFetchOptions semanticFetchOptions = new SemanticAnnotationFetchOptions();
+ 
+        // Request and collect subjects
+        if (subjectClassIDOrNull != null) {
+            semanticCriteria.withPredicateAccessionId().thatEquals(subjectClassIDOrNull);
+        }
+        semanticFetchOptions.withEntityType();
+ 
+        // Request and collect predicates
+        if (predicatePropertyIDsOrNull != null) {
+            for (String predicate : predicatePropertyIDsOrNull.keySet()) {
+                semanticCriteria.withPredicateAccessionId().thatEquals(predicate);
+            }
+        }
+        PropertyAssignmentFetchOptions propertyAssignmentFetchOptions = semanticFetchOptions.withPropertyAssignment();
+        propertyAssignmentFetchOptions.withPropertyType();
+        propertyAssignmentFetchOptions.withEntityType();
+ 
+        SearchResult<SemanticAnnotation> semanticAnnotationSearchResult = v3.searchSemanticAnnotations(sessionToken, new SemanticAnnotationSearchCriteria(), semanticFetchOptions);
+ 
+        //
+        // Part 2 : Create openBIS search matching semantic results
+        //
+ 
+        AbstractEntitySearchCriteria criteria = getEntitySearchCriteria(entityKind);
+        criteria.withAndOperator();
+ 
+        // Set Subject
+        String entityTypeCode = null;
+        for (SemanticAnnotation semanticAnnotation:semanticAnnotationSearchResult.getObjects()) {
+            if (semanticAnnotation.getEntityType() != null) {
+                EntityTypePermId permId = (EntityTypePermId) semanticAnnotation.getEntityType().getPermId();
+                if (permId.getEntityKind() == entityKind) {
+                    entityTypeCode = semanticAnnotation.getEntityType().getCode();
+                    setWithTypeThatEquals(entityKind, criteria, entityTypeCode);
+                }
+            }
+        }
+ 
+        if (entityTypeCode == null) {
+            throw new UserFailureException("Entity Type matching Subject not found.");
+        }
+ 
+        // Set Predicates matching the Subject
+        if (predicatePropertyIDsOrNull != null) {
+            int predicatesFound = 0;
+            for (SemanticAnnotation semanticAnnotation : semanticAnnotationSearchResult.getObjects()) {
+                if (semanticAnnotation.getPropertyAssignment() != null &&
+                        semanticAnnotation.getPropertyAssignment().getEntityType().getCode().equals(entityTypeCode)) {
+                    EntityTypePermId permId = (EntityTypePermId) semanticAnnotation.getPropertyAssignment().getEntityType().getPermId();
+                    if (permId.getEntityKind() == entityKind) {
+                        String value = predicatePropertyIDsOrNull.get(semanticAnnotation.getPredicateAccessionId());
+                        criteria.withProperty(semanticAnnotation.getPropertyAssignment().getPropertyType().getCode()).thatEquals(value);
+                        predicatesFound++;
+                    }
+                }
+            }
+ 
+            if (predicatesFound != predicatePropertyIDsOrNull.size()) {
+                throw new UserFailureException("Property Types matching Predicates not found.");
+            }
+        }
+ 
+        FetchOptions fetchOptions = getEntityFetchOptions(entityKind);
+        if (fromOrNull != null) {
+            fetchOptions.from(fromOrNull);
+        }
+        if (countOrNull != null) {
+            fetchOptions.count(countOrNull);
+        }
+ 
+        SearchResult searchResult = getSearchResult(v3, sessionToken, entityKind, criteria, fetchOptions);
+        return searchResult;
+    }
+ 
+    private static void setWithTypeThatEquals(EntityKind entityKind, AbstractEntitySearchCriteria criteria, String entityTypeCode) {
+        switch (entityKind) {
+            case EXPERIMENT:
+                ((ExperimentSearchCriteria) criteria).withType().withCode().thatEquals(entityTypeCode);
+                break;
+            case SAMPLE:
+                ((SampleSearchCriteria) criteria).withType().withCode().thatEquals(entityTypeCode);
+                break;
+            case DATA_SET:
+                ((DataSetSearchCriteria) criteria).withType().withCode().thatEquals(entityTypeCode);
+                break;
+        }
+    }
+ 
+    private static AbstractEntitySearchCriteria getEntitySearchCriteria(EntityKind entityKind) {
+        AbstractEntitySearchCriteria criteria = null;
+        switch (entityKind) {
+            case EXPERIMENT:
+                criteria = new ExperimentSearchCriteria();
+                break;
+            case SAMPLE:
+                criteria = new SampleSearchCriteria();
+                break;
+            case DATA_SET:
+                criteria = new DataSetSearchCriteria();
+                break;
+        }
+        return criteria;
+    }
+ 
+    private static FetchOptions getEntityFetchOptions(EntityKind entityKind) {
+        FetchOptions fetchOptions = null;
+        switch (entityKind) {
+            case EXPERIMENT:
+                fetchOptions = new ExperimentFetchOptions();
+                break;
+            case SAMPLE:
+                fetchOptions = new SampleFetchOptions();
+                break;
+            case DATA_SET:
+                fetchOptions = new DataSetFetchOptions();
+                break;
+        }
+        return fetchOptions;
+    }
+ 
+    private static SearchResult getSearchResult(IApplicationServerApi v3, String sessionToken, EntityKind entityKind, AbstractEntitySearchCriteria criteria, FetchOptions fetchOptions) {
+        SearchResult searchResult = null;
+        switch (entityKind) {
+            case EXPERIMENT:
+                searchResult = v3.searchExperiments(sessionToken, (ExperimentSearchCriteria) criteria, (ExperimentFetchOptions) fetchOptions);
+                break;
+            case SAMPLE:
+                searchResult = v3.searchSamples(sessionToken, (SampleSearchCriteria) criteria, (SampleFetchOptions) fetchOptions);
+                break;
+            case DATA_SET:
+                searchResult = v3.searchDataSets(sessionToken, (DataSetSearchCriteria) criteria, (DataSetFetchOptions) fetchOptions);
+                break;
+        }
+        return searchResult;
+    }
+}
+```
\ No newline at end of file