From 565a09866a98b9087ebe7bdaac41f25121d35619 Mon Sep 17 00:00:00 2001
From: vkovtun <viktor.kovtun@id.ethz.ch>
Date: Tue, 7 Feb 2023 12:38:24 +0100
Subject: [PATCH] SSDM-12986 Implemented condition translator for vocabulary
 property.

---
 .../v3/search/mapper/CriteriaMapper.java      |   5 +
 ...laryPropertySearchConditionTranslator.java | 116 ++++++++++++++++++
 .../asapi/v3/AbstractSearchPropertyTest.java  |  89 +++++++++++---
 .../search/AbstractEntitySearchCriteria.java  |   4 +-
 ...lledVocabularyPropertySearchCriteria.java} |  13 +-
 5 files changed, 205 insertions(+), 22 deletions(-)
 create mode 100644 openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/search/translator/condition/ControlledVocabularyPropertySearchConditionTranslator.java
 rename openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/search/{VocabularyPropertySearchCriteria.java => ControlledVocabularyPropertySearchCriteria.java} (69%)

diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/search/mapper/CriteriaMapper.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/search/mapper/CriteriaMapper.java
index c84f5a55155..6b736bd0c90 100644
--- a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/search/mapper/CriteriaMapper.java
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/search/mapper/CriteriaMapper.java
@@ -70,6 +70,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.StrictlyStringProp
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.StringFieldSearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.StringPropertySearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.TextAttributeSearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ControlledVocabularyPropertySearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.search.ArchivingRequestedSearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.search.CompleteSearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.dataset.search.ContentCopySearchCriteria;
@@ -145,6 +146,7 @@ import ch.ethz.sis.openbis.generic.server.asapi.v3.search.translator.condition.B
 import ch.ethz.sis.openbis.generic.server.asapi.v3.search.translator.condition.CodeSearchConditionTranslator;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.search.translator.condition.CollectionFieldSearchConditionTranslator;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.search.translator.condition.CompleteSearchConditionTranslator;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.search.translator.condition.ControlledVocabularyPropertySearchConditionTranslator;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.search.translator.condition.DataSetKindSearchConditionTranslator;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.search.translator.condition.DateFieldSearchConditionTranslator;
 import ch.ethz.sis.openbis.generic.server.asapi.v3.search.translator.condition.EmailSearchConditionTranslator;
@@ -245,6 +247,8 @@ public class CriteriaMapper
         CRITERIA_TO_CONDITION_TRANSLATOR_MAP.put(CodesSearchCriteria.class, collectionFieldSearchConditionTranslator);
         CRITERIA_TO_CONDITION_TRANSLATOR_MAP.put(CollectionFieldSearchCriteria.class,
                 collectionFieldSearchConditionTranslator);
+        CRITERIA_TO_CONDITION_TRANSLATOR_MAP.put(ControlledVocabularyPropertySearchCriteria.class,
+                new ControlledVocabularyPropertySearchConditionTranslator());
         CRITERIA_TO_CONDITION_TRANSLATOR_MAP.put(CompleteSearchCriteria.class, new CompleteSearchConditionTranslator());
         CRITERIA_TO_CONDITION_TRANSLATOR_MAP.put(DataSetKindSearchCriteria.class,
                 new DataSetKindSearchConditionTranslator());
@@ -285,6 +289,7 @@ public class CriteriaMapper
                 booleanFieldSearchConditionTranslator);
         CRITERIA_TO_CONDITION_TRANSLATOR_MAP.put(RegistrationDateSearchCriteria.class,
                 dateFieldSearchConditionTranslator);
+//        CRITERIA_TO_CONDITION_TRANSLATOR_MAP.put(SamplePropertySearchCriteria.class, );
         CRITERIA_TO_CONDITION_TRANSLATOR_MAP.put(ShareIdSearchCriteria.class, stringFieldSearchConditionTranslator);
         CRITERIA_TO_CONDITION_TRANSLATOR_MAP.put(SizeSearchCriteria.class, numberFieldSearchConditionTranslator);
         CRITERIA_TO_CONDITION_TRANSLATOR_MAP.put(SpeedHintSearchCriteria.class, numberFieldSearchConditionTranslator);
diff --git a/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/search/translator/condition/ControlledVocabularyPropertySearchConditionTranslator.java b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/search/translator/condition/ControlledVocabularyPropertySearchConditionTranslator.java
new file mode 100644
index 00000000000..4bb65920cc9
--- /dev/null
+++ b/openbis/source/java/ch/ethz/sis/openbis/generic/server/asapi/v3/search/translator/condition/ControlledVocabularyPropertySearchConditionTranslator.java
@@ -0,0 +1,116 @@
+/*
+ * 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.ethz.sis.openbis.generic.server.asapi.v3.search.translator.condition;
+
+import static ch.ethz.sis.openbis.generic.server.asapi.v3.search.translator.SQLLexemes.AND;
+import static ch.ethz.sis.openbis.generic.server.asapi.v3.search.translator.SQLLexemes.EQ;
+import static ch.ethz.sis.openbis.generic.server.asapi.v3.search.translator.SQLLexemes.FROM;
+import static ch.ethz.sis.openbis.generic.server.asapi.v3.search.translator.SQLLexemes.IN;
+import static ch.ethz.sis.openbis.generic.server.asapi.v3.search.translator.SQLLexemes.LP;
+import static ch.ethz.sis.openbis.generic.server.asapi.v3.search.translator.SQLLexemes.PERIOD;
+import static ch.ethz.sis.openbis.generic.server.asapi.v3.search.translator.SQLLexemes.QU;
+import static ch.ethz.sis.openbis.generic.server.asapi.v3.search.translator.SQLLexemes.RP;
+import static ch.ethz.sis.openbis.generic.server.asapi.v3.search.translator.SQLLexemes.SELECT;
+import static ch.ethz.sis.openbis.generic.server.asapi.v3.search.translator.SQLLexemes.SP;
+import static ch.ethz.sis.openbis.generic.server.asapi.v3.search.translator.SQLLexemes.WHERE;
+import static ch.systemsx.cisd.openbis.generic.shared.dto.ColumnNames.ID_COLUMN;
+import static ch.systemsx.cisd.openbis.generic.shared.dto.ColumnNames.VOCABULARY_TERM_COLUMN;
+import static ch.systemsx.cisd.openbis.generic.shared.dto.TableNames.CONTROLLED_VOCABULARY_TERM_TABLE;
+
+import java.util.List;
+import java.util.Map;
+
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.AbstractFieldSearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ControlledVocabularyPropertySearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.SearchFieldType;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.search.mapper.TableMapper;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.search.translator.condition.utils.JoinInformation;
+import ch.ethz.sis.openbis.generic.server.asapi.v3.search.translator.condition.utils.TranslatorUtils;
+
+public class ControlledVocabularyPropertySearchConditionTranslator
+        implements IConditionTranslator<ControlledVocabularyPropertySearchCriteria>
+{
+
+    @Override
+    public Map<String, JoinInformation> getJoinInformationMap(
+            final ControlledVocabularyPropertySearchCriteria criterion, final TableMapper tableMapper,
+            final IAliasFactory aliasFactory)
+    {
+        if (criterion.getFieldType() == SearchFieldType.PROPERTY)
+        {
+            return TranslatorUtils.getPropertyJoinInformationMap(tableMapper, aliasFactory);
+        } else {
+            throw new IllegalArgumentException();
+        }
+    }
+
+    @Override
+    public void translate(final ControlledVocabularyPropertySearchCriteria criterion, final TableMapper tableMapper,
+            final List<Object> args, final StringBuilder sqlBuilder, final Map<String, JoinInformation> aliases,
+            final Map<String, String> dataTypeByPropertyCode)
+    {
+        switch (criterion.getFieldType())
+        {
+            case PROPERTY:
+            {
+                doTranslate(criterion, tableMapper, args, sqlBuilder, aliases);
+                break;
+            }
+
+            case ANY_FIELD:
+            case ANY_PROPERTY:
+            case ATTRIBUTE:
+            {
+                throw new IllegalArgumentException("Field type " + criterion.getFieldType() + " is not supported");
+            }
+        }
+    }
+
+    static void doTranslate(final AbstractFieldSearchCriteria<String> criterion,
+            final TableMapper tableMapper,
+            final List<Object> args, final StringBuilder sqlBuilder, final Map<String, JoinInformation> aliases)
+    {
+        final String value = criterion.getFieldValue();
+        final String valuesTableAlias = aliases.get(tableMapper.getValuesTable()).getSubTableAlias();
+
+        TranslatorUtils.appendPropertiesExist(sqlBuilder, valuesTableAlias);
+        sqlBuilder.append(SP).append(AND).append(SP).append(LP);
+
+        appendControlledVocabularyTermIdSubselectConstraint(args, sqlBuilder, value, valuesTableAlias);
+
+        sqlBuilder.append(RP);
+    }
+
+    private static void appendControlledVocabularyTermIdSubselectConstraint(final List<Object> args,
+            final StringBuilder sqlBuilder, final String value, final String propertyTableAlias)
+    {
+        sqlBuilder.append(propertyTableAlias).append(PERIOD).append(VOCABULARY_TERM_COLUMN)
+                .append(SP).append(IN).append(SP);
+        sqlBuilder.append(LP);
+
+        sqlBuilder.append(SELECT).append(SP).append(ID_COLUMN).append(SP)
+                .append(FROM).append(SP).append(CONTROLLED_VOCABULARY_TERM_TABLE).append(SP)
+                .append(WHERE).append(SP);
+        sqlBuilder.append(ch.systemsx.cisd.openbis.generic.shared.dto.ColumnNames.CODE_COLUMN);
+
+        sqlBuilder.append(SP).append(EQ).append(SP).append(QU);
+        args.add(value);
+
+        sqlBuilder.append(RP);
+    }
+
+}
diff --git a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractSearchPropertyTest.java b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractSearchPropertyTest.java
index 45d3ad7ea38..e0c24b899e7 100644
--- a/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractSearchPropertyTest.java
+++ b/openbis/sourceTest/java/ch/ethz/sis/openbis/systemtest/asapi/v3/AbstractSearchPropertyTest.java
@@ -37,6 +37,7 @@ import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.id.ObjectPermId;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.interfaces.IPermIdHolder;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.AbstractEntitySearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.BooleanFieldSearchCriteria;
+import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.ControlledVocabularyPropertySearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.DateFieldSearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.NumberFieldSearchCriteria;
 import ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search.StringFieldSearchCriteria;
@@ -857,10 +858,43 @@ public abstract class AbstractSearchPropertyTest extends AbstractTest
         };
     }
 
-    @Test(dataProvider = "withControlledVocabularyAsStringPropertyExamples")
+    @Test(dataProvider = "withControlledVocabularyPropertyExamples")
     public void testWithControlledVocabularyProperty(final String value, final String queryString, final boolean found)
     {
+        // Given
+        final String sessionToken = v3api.login(TEST_USER, PASSWORD);
+
+        final VocabularyTermCreation vocabularyTermCreation1 = new VocabularyTermCreation();
+        vocabularyTermCreation1.setCode("WINTER");
+        final VocabularyTermCreation vocabularyTermCreation2 = new VocabularyTermCreation();
+        vocabularyTermCreation2.setCode("SPRING");
+        final VocabularyTermCreation vocabularyTermCreation3 = new VocabularyTermCreation();
+        vocabularyTermCreation3.setCode("SUMMER");
+        final VocabularyTermCreation vocabularyTermCreation4 = new VocabularyTermCreation();
+        vocabularyTermCreation4.setCode("AUTUMN");
+
+        final VocabularyCreation vocabularyCreation = new VocabularyCreation();
+        vocabularyCreation.setCode("SEASONS");
+        vocabularyCreation.setTerms(Arrays.asList(vocabularyTermCreation1, vocabularyTermCreation2,
+                vocabularyTermCreation3, vocabularyTermCreation4));
+        final VocabularyPermId vocabularyPermId =
+                v3api.createVocabularies(sessionToken, Collections.singletonList(vocabularyCreation)).get(0);
+
+        final PropertyTypePermId propertyTypeId = createAPropertyType(sessionToken, DataType.CONTROLLEDVOCABULARY,
+                vocabularyPermId);
+        final ObjectPermId entityPermId = createEntity(sessionToken, propertyTypeId, value);
+        final AbstractEntitySearchCriteria<?> searchCriteria = createSearchCriteria();
+        new VocabularyQueryInjector(searchCriteria, propertyTypeId).buildCriteria(queryString);
 
+        // When
+        final List<? extends IPermIdHolder> entities = search(sessionToken, searchCriteria);
+
+        // Then
+        assertEquals(entities.size(), found ? 1 : 0);
+        if (found)
+        {
+            assertEquals(entities.get(0).getPermId().toString(), entityPermId.getPermId());
+        }
     }
 
     @Test
@@ -879,21 +913,21 @@ public abstract class AbstractSearchPropertyTest extends AbstractTest
         assertEquals(entities.size(), 1);
     }
 
-//    @Test
-//    public void testSearchWithSampleProperty()
-//    {
-//        final String sessionToken = v3api.login(TEST_USER, PASSWORD);
-//        final PropertyTypePermId propertyTypeId = createASamplePropertyType(sessionToken, null);
-//
-//        createEntity(sessionToken, propertyTypeId, "/CISD/CL1");
-//
-//        final AbstractEntitySearchCriteria<?> searchCriteria = createSearchCriteria();
-//        searchCriteria.withOrOperator();
-//        searchCriteria.withSampleProperty(propertyTypeId.getPermId()).thatEquals("/CISD/CL1");
-//
-//        final List<? extends IPermIdHolder> entities = search(sessionToken, searchCriteria);
-//        assertEquals(entities.size(), 1);
-//    }
+    @Test(enabled = false)
+    public void testSearchWithSampleProperty()
+    {
+        final String sessionToken = v3api.login(TEST_USER, PASSWORD);
+        final PropertyTypePermId propertyTypeId = createASamplePropertyType(sessionToken, null);
+
+        createEntity(sessionToken, propertyTypeId, "/CISD/CL1");
+
+        final AbstractEntitySearchCriteria<?> searchCriteria = createSearchCriteria();
+        searchCriteria.withOrOperator();
+        searchCriteria.withSampleProperty(propertyTypeId.getPermId()).thatEquals("/CISD/CL1");
+
+        final List<? extends IPermIdHolder> entities = search(sessionToken, searchCriteria);
+        assertEquals(entities.size(), 1);
+    }
 
     @Test
     public void testSearchWithPropertyMatchingMaterialProperty()
@@ -1477,4 +1511,27 @@ public abstract class AbstractSearchPropertyTest extends AbstractTest
             }
         }
     }
+
+    static final class VocabularyQueryInjector extends AbstractQueryInjector
+    {
+        VocabularyQueryInjector(final AbstractEntitySearchCriteria<?> searchCriteria,
+                final PropertyTypePermId propertyTypeId)
+        {
+            super(searchCriteria, propertyTypeId);
+        }
+
+        @Override
+        protected void injectQuery(final Operator operator, final String operand)
+        {
+            final ControlledVocabularyPropertySearchCriteria criteria =
+                    searchCriteria.withVocabularyProperty(propertyTypeId.getPermId());
+            if (operator == Operator.EQUAL)
+            {
+                criteria.thatEquals(operand);
+            } else
+            {
+                throw new IllegalArgumentException("Unsupported operator " + operator);
+            }
+        }
+    }
 }
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/search/AbstractEntitySearchCriteria.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/search/AbstractEntitySearchCriteria.java
index 573d390a538..abfea0bf12d 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/search/AbstractEntitySearchCriteria.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/search/AbstractEntitySearchCriteria.java
@@ -116,9 +116,9 @@ public abstract class AbstractEntitySearchCriteria<ID extends IObjectId> extends
         return with(new SamplePropertySearchCriteria(propertyName));
     }
 
-    public VocabularyPropertySearchCriteria withVocabularyProperty(final String propertyName)
+    public ControlledVocabularyPropertySearchCriteria withVocabularyProperty(final String propertyName)
     {
-        return with(new VocabularyPropertySearchCriteria(propertyName));
+        return with(new ControlledVocabularyPropertySearchCriteria(propertyName));
     }
 
     /**
diff --git a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/search/VocabularyPropertySearchCriteria.java b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/search/ControlledVocabularyPropertySearchCriteria.java
similarity index 69%
rename from openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/search/VocabularyPropertySearchCriteria.java
rename to openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/search/ControlledVocabularyPropertySearchCriteria.java
index 0d64f8123b9..2b6a8495ae1 100644
--- a/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/search/VocabularyPropertySearchCriteria.java
+++ b/openbis_api/source/java/ch/ethz/sis/openbis/generic/asapi/v3/dto/common/search/ControlledVocabularyPropertySearchCriteria.java
@@ -18,16 +18,21 @@ package ch.ethz.sis.openbis.generic.asapi.v3.dto.common.search;
 
 import ch.systemsx.cisd.base.annotation.JsonObject;
 
-// TODO: add JS criteria
-@JsonObject("as.dto.common.search.VocabularyPropertySearchCriteria")
-public class VocabularyPropertySearchCriteria extends AbstractFieldSearchCriteria<String>
+// TODO: add JS criteria!
+@JsonObject("as.dto.common.search.ControlledVocabularyPropertySearchCriteria")
+public class ControlledVocabularyPropertySearchCriteria extends AbstractFieldSearchCriteria<String>
 {
 
     private static final long serialVersionUID = 1L;
 
-    VocabularyPropertySearchCriteria(final String propertyName)
+    ControlledVocabularyPropertySearchCriteria(final String propertyName)
     {
         super(propertyName, SearchFieldType.PROPERTY);
     }
 
+    public void thatEquals(final String value)
+    {
+        setFieldValue(value);
+    }
+
 }
-- 
GitLab