diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RemoveTagCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RemoveTagCommand.java index 157546c37e6..cfb2d366f69 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RemoveTagCommand.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/RemoveTagCommand.java @@ -27,7 +27,7 @@ protected void executeCommand() { tagsDirector.removeTag(getTag().getTagId()); String[] IDsArray = tagIdAndChildrenIds.split("[,]", -1); for (String id : IDsArray) { - id = id.replace("'", ""); + id = id.replace("\"", ""); tagDao.remove(new Guid(id)); } setSucceeded(true); diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/SearchQuery.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/SearchQuery.java index 03b78127e87..cbd3d0ee1d6 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/SearchQuery.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/SearchQuery.java @@ -639,7 +639,7 @@ private QueryData initQueryData(boolean useCache) { // statically, therefore , in order to reflect changes in the parent tree // we should not rely on the cached query in such case and have to build the // query from scratch. - if (!containsStaticInValues(data.getQuery())) { + if (!containsStaticValues(data.getQuery())) { queriesCache.put(searchKey, data); } } @@ -671,8 +671,8 @@ protected String getDefaultAuthz() { return AuthenticationProfileRepository.getInstance().getProfiles().get(0).getName(); } - private static boolean containsStaticInValues(String query) { - final String MATCH_IN_TAG_ID_CLAUSE = "with_tags.tag_id in"; - return query.toLowerCase().contains(MATCH_IN_TAG_ID_CLAUSE); + private static boolean containsStaticValues(String query) { + final String MATCH_TAG_NAMES_CONDITION = "with_tags.tag_names &&"; + return query.toLowerCase().contains(MATCH_TAG_NAMES_CONDITION); } } diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/TagsDirector.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/TagsDirector.java index ac7881ed4a0..ae0df97a96b 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/TagsDirector.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/TagsDirector.java @@ -5,6 +5,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Pattern; +import java.util.stream.Collectors; import javax.annotation.PostConstruct; import javax.inject.Inject; @@ -248,11 +249,21 @@ public String getTagNamesAndChildrenNamesByRegExp(String tagNameRegExp) { return sb.toString(); } + @Override + public String getTagsNamesByRegExp(String tagNameRegExp) { + tagNameRegExp = tagNameRegExp.replace("*", ".*"); + final Pattern tagNamePattern = Pattern.compile(String.format("^%1$s$", tagNameRegExp)); + + return tagsMapByName.keySet().stream() + .filter(name -> tagNamePattern.matcher(name).matches()) + .collect(Collectors.joining(", ")); + } + private static void recursiveGetTagsAndChildrenByRegExp(String tagNameRegExp, StringBuilder sb, Tags tag, TagReturnValueIndicator indicator) { if (tag.getChildren() != null && !tag.getChildren().isEmpty()) { tagNameRegExp = BACKSLASH_REMOVER.matcher(tagNameRegExp).replaceAll(""); + Pattern tagNamePattern = Pattern.compile(tagNameRegExp); for (Tags child : tag.getChildren()) { - Pattern tagNamePattern = Pattern.compile(tagNameRegExp); if (tagNamePattern.matcher(child.getTagName()).find()) { // the tag matches the regular expression -> add it and all its // children @@ -280,7 +291,7 @@ private static void recursiveGetTagsAndChildrenByRegExp(String tagNameRegExp, St private static StringBuilder getTagIdAndChildrenIds(Tags tag) { StringBuilder builder = new StringBuilder(); - builder.append("'").append(tag.getTagId()).append("'"); + builder.append('"').append(tag.getTagId()).append('"'); for (Tags child : tag.getChildren()) { builder.append(",").append(getTagIdAndChildrenIds(child)); @@ -290,10 +301,10 @@ private static StringBuilder getTagIdAndChildrenIds(Tags tag) { private static StringBuilder getTagNameAndChildrenNames(Tags tag) { StringBuilder builder = new StringBuilder(); - builder.append("'").append(tag.getTagName()).append("'"); + builder.append('"').append(tag.getTagName()).append('"'); for (Tags child : tag.getChildren()) { - builder.append("," + getTagNameAndChildrenNames(child)); + builder.append(",").append(getTagNameAndChildrenNames(child)); } return builder; } diff --git a/backend/manager/modules/bll/src/test/java/org/ovirt/engine/core/bll/TagsDirectorTest.java b/backend/manager/modules/bll/src/test/java/org/ovirt/engine/core/bll/TagsDirectorTest.java index f54fb76b71b..3d137cb552c 100644 --- a/backend/manager/modules/bll/src/test/java/org/ovirt/engine/core/bll/TagsDirectorTest.java +++ b/backend/manager/modules/bll/src/test/java/org/ovirt/engine/core/bll/TagsDirectorTest.java @@ -247,10 +247,10 @@ public void testGetTagIdAndChildrenIds() { tag2.setParentId(tag.getParentId()); tagsDirector.addTag(tag); String idsStr = tagsDirector.getTagIdAndChildrenIds(tag.getTagId()); - String[] ids = idsStr.split("[,]"); + String[] ids = idsStr.split(","); assertEquals(2, ids.length); - assertEquals(ids[0], "'" + tag.getTagId().toString() + "'"); - assertEquals(ids[1], "'" + tag2.getTagId().toString() + "'"); + assertEquals('"' + tag.getTagId().toString() + '"', ids[0]); + assertEquals('"' + tag2.getTagId().toString() + '"', ids[1]); } /** diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/interfaces/ITagsHandler.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/interfaces/ITagsHandler.java index 785c7c99d1f..3611791f012 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/interfaces/ITagsHandler.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/interfaces/ITagsHandler.java @@ -12,4 +12,6 @@ public interface ITagsHandler { String getTagNameAndChildrenNames(Guid tagId); String getTagNamesAndChildrenNamesByRegExp(String tagNameRegExp); + + String getTagsNamesByRegExp(String tagNameRegExp); } diff --git a/backend/manager/modules/searchbackend/src/main/java/org/ovirt/engine/core/searchbackend/BaseConditionFieldAutoCompleter.java b/backend/manager/modules/searchbackend/src/main/java/org/ovirt/engine/core/searchbackend/BaseConditionFieldAutoCompleter.java index 53cee36b85b..8c980280781 100644 --- a/backend/manager/modules/searchbackend/src/main/java/org/ovirt/engine/core/searchbackend/BaseConditionFieldAutoCompleter.java +++ b/backend/manager/modules/searchbackend/src/main/java/org/ovirt/engine/core/searchbackend/BaseConditionFieldAutoCompleter.java @@ -13,14 +13,12 @@ import java.util.stream.Collectors; import org.ovirt.engine.core.common.businessentities.DateEnumForSearch; -import org.ovirt.engine.core.common.businessentities.Tags; import org.ovirt.engine.core.common.config.Config; import org.ovirt.engine.core.common.config.ConfigValues; import org.ovirt.engine.core.common.interfaces.ITagsHandler; import org.ovirt.engine.core.common.utils.Pair; import org.ovirt.engine.core.compat.DateTime; import org.ovirt.engine.core.compat.DayOfWeek; -import org.ovirt.engine.core.compat.Guid; import org.ovirt.engine.core.compat.IntegerCompat; import org.ovirt.engine.core.compat.Regex; import org.ovirt.engine.core.compat.StringFormat; @@ -32,6 +30,13 @@ */ public class BaseConditionFieldAutoCompleter extends BaseAutoCompleter implements IConditionFieldAutoCompleter { + /** + * Stores sql expression mappers for free text search. + * key: column type + * value: sql expression mapper + */ + private static final Map, FreeSearchMapper> freeTextSearchMappers; + protected final Map> validationDict = new HashMap<>(); private final Map> typeDict = new HashMap<>(); protected final Map columnNameDict = new HashMap<>(); @@ -43,6 +48,16 @@ public class BaseConditionFieldAutoCompleter extends BaseAutoCompleter implement protected final List notFreeTextSearchableFieldsList = new ArrayList<>(); protected Set verbsWithMultipleValues = new HashSet<>(); + static { + freeTextSearchMappers = new HashMap<>(); + freeTextSearchMappers.put(String.class, (table, column, relation, value) -> + StringFormat.format(" %1$s.%2$s %3$s %4$s", table, column, relation, value)); + freeTextSearchMappers.put(String[].class, (table, column, relation, value) -> + StringFormat.format( + " (select bool_or(is_match_rows) from (select unnest(%1$s.%2$s) %3$s %4$s as is_match_rows))", + table, column, relation, value)); + } + /** * Gets the LIKE clause syntax for non case-sensitive search * @@ -169,8 +184,11 @@ public final String buildFreeTextConditionSql(String tableName, } return columnNameDict.entrySet().stream().sorted(Map.Entry.comparingByValue()) - .filter(e -> typeDict.get(e.getKey()) == String.class && !notFreeTextSearchableFieldsList.contains(e.getKey())) - .map(e -> StringFormat.format(" %1$s.%2$s %3$s %4$s", tableName, e.getValue(), rel, val)) + .filter(e -> + freeTextSearchMappers.containsKey(typeDict.get(e.getKey())) && + !notFreeTextSearchableFieldsList.contains(e.getKey())) + .map(e -> + freeTextSearchMappers.get(typeDict.get(e.getKey())).map(tableName, e.getValue(), rel, val)) .distinct() .collect(Collectors.joining(" OR ", " ( ", " ) ")); } @@ -282,30 +300,39 @@ public void formatValue(String fieldName, Pair pair, boolean cas } else if ("TAG".equals(fieldName)) { pair.setSecond(pair.getSecond().startsWith("N'") ? pair.getSecond().substring(2) : pair.getSecond()); - if (pair.getFirst() != null && pair.getFirst().equals("=")) { - pair.setFirst("IN"); - pair.setSecond(StringHelper.trim(pair.getSecond(), '\'')); - Tags tag = tagsHandler.getTagByTagName(pair.getSecond()); - if (tag != null) { - pair.setSecond( - StringFormat.format("(%1$s)", tagsHandler.getTagNameAndChildrenNames(tag.getTagId()))); - } else { - pair.setSecond(StringFormat.format("('%1$s')", Guid.Empty)); - } - } else if (pair.getFirst() != null && (pair.getFirst().equals("LIKE") || pair.getFirst().equals("ILIKE"))) { - pair.setFirst("IN"); - pair.setSecond(StringHelper.trim(pair.getSecond(), '\'').replace("%", "*")); - String IDs = tagsHandler.getTagNamesAndChildrenNamesByRegExp(pair.getSecond()); - if (StringHelper.isNullOrEmpty(IDs)) { - pair.setSecond(StringFormat.format("('%1$s')", Guid.Empty)); - } else { - pair.setSecond(StringFormat.format("(%1$s)", IDs)); - } + pair.setSecond(StringHelper.trim(pair.getSecond(), '\'').replace("%", "*")); + + String tags = getMatchingTagsForRelation(pair.getSecond(), pair.getFirst()); + if (StringHelper.isNullOrEmpty(tags)) { + pair.setSecond("'{}'"); + } else { + pair.setSecond(StringFormat.format("'{%1$s}'", tags)); } } } + /** + * Retrieves tags that match the specified regular expression based on the given relation. + * The method utilises operations according to {@link StringConditionRelationAutoCompleter}, + * which is used in the operation defining methods of other classes, + * such as {@link VmConditionFieldAutoCompleter#getFieldRelationshipAutoCompleter(String)}, for tags. + * + * @param tagSearchString A regular expression string used to search for tags. + * @param relation Relation for tag matching. + * Supported values: "=" for matches and "!=" for excludes. + * @return A comma-separated string of matching tag names. If no tags match, an empty string is returned. + * + * @see StringConditionRelationAutoCompleter + */ + private String getMatchingTagsForRelation(String tagSearchString, String relation) { + if ("=".equals(relation)) { + return tagsHandler.getTagNamesAndChildrenNamesByRegExp(tagSearchString); + } else { + return tagsHandler.getTagsNamesByRegExp(tagSearchString); + } + } + // private static final String DATE_FORMAT = "MMM dd,yyyy"; private static DateTime dealWithDateEnum(String value) { DateTime formatedValue = new DateTime(); @@ -362,8 +389,16 @@ public String buildConditionSql(String objName, String fieldName, String customi tableName, getDbFieldName(fieldName)); } + String formatString; - if (isOperatorNegative(pair.getFirst())) { + if (String[].class == getTypeDictionary().get(fieldName)) { + if ("=".equals(pair.getFirst())) { + formatString = " (%1$s.%2$s IS NOT NULL AND (%1$s.%2$s %3$s %4$s)) "; + } else { + formatString = " (%1$s.%2$s IS NULL OR NOT (%1$s.%2$s %3$s %4$s)) "; + } + pair.setFirst("&&"); + } else if (isOperatorNegative(pair.getFirst())) { formatString = " (%1$s.%2$s IS NULL OR %1$s.%2$s %3$s %4$s) "; } else { formatString = " %1$s.%2$s %3$s %4$s "; @@ -389,4 +424,14 @@ private static boolean isOperatorNegative(String operator) { public String getWildcard(String fieldName) { return verbsWithMultipleValues.contains(fieldName) ? ".*" : "%"; } + + /** + * Declares free text search mapping function. + */ + private interface FreeSearchMapper { + + String map(String table, String column, String relation, String value); + + } + } diff --git a/backend/manager/modules/searchbackend/src/main/java/org/ovirt/engine/core/searchbackend/VdcUserConditionFieldAutoCompleter.java b/backend/manager/modules/searchbackend/src/main/java/org/ovirt/engine/core/searchbackend/VdcUserConditionFieldAutoCompleter.java index 3246b0514d3..ddac57f420f 100644 --- a/backend/manager/modules/searchbackend/src/main/java/org/ovirt/engine/core/searchbackend/VdcUserConditionFieldAutoCompleter.java +++ b/backend/manager/modules/searchbackend/src/main/java/org/ovirt/engine/core/searchbackend/VdcUserConditionFieldAutoCompleter.java @@ -43,7 +43,7 @@ public VdcUserConditionFieldAutoCompleter() { getTypeDictionary().put(LOGIN, String.class); getTypeDictionary().put(DIRECTORY, String.class); getTypeDictionary().put(DEPARTMENT, String.class); - getTypeDictionary().put(TAG, String.class); + getTypeDictionary().put(TAG, String[].class); getTypeDictionary().put(POOL, String.class); getTypeDictionary().put(TYPE, UserOrGroup.class); @@ -55,7 +55,7 @@ public VdcUserConditionFieldAutoCompleter() { columnNameDict.put(LOGIN, "username"); columnNameDict.put(DIRECTORY, "domain"); columnNameDict.put(DEPARTMENT, "department"); - columnNameDict.put(TAG, "tag_name"); + columnNameDict.put(TAG, "tag_names"); columnNameDict.put(POOL, "vm_pool_name"); columnNameDict.put(TYPE, "user_group"); // Building the validation dict diff --git a/backend/manager/modules/searchbackend/src/main/java/org/ovirt/engine/core/searchbackend/VdsConditionFieldAutoCompleter.java b/backend/manager/modules/searchbackend/src/main/java/org/ovirt/engine/core/searchbackend/VdsConditionFieldAutoCompleter.java index 5fa2fb05c5f..df9201183c3 100644 --- a/backend/manager/modules/searchbackend/src/main/java/org/ovirt/engine/core/searchbackend/VdsConditionFieldAutoCompleter.java +++ b/backend/manager/modules/searchbackend/src/main/java/org/ovirt/engine/core/searchbackend/VdsConditionFieldAutoCompleter.java @@ -87,7 +87,7 @@ public VdsConditionFieldAutoCompleter() { getTypeDictionary().put(ACTIVE_VMS, Integer.class); getTypeDictionary().put(MIGRATING_VMS, Integer.class); getTypeDictionary().put(COMMITTED_MEM, Integer.class); - getTypeDictionary().put(TAG, String.class); + getTypeDictionary().put(TAG, String[].class); getTypeDictionary().put(TYPE, VDSNiceType.class); getTypeDictionary().put(DATACENTER, String.class); getTypeDictionary().put("ID", UUID.class); @@ -115,7 +115,7 @@ public VdsConditionFieldAutoCompleter() { columnNameDict.put(ACTIVE_VMS, "vm_active"); columnNameDict.put(MIGRATING_VMS, "vm_migrating"); columnNameDict.put(COMMITTED_MEM, "mem_commited"); - columnNameDict.put(TAG, "tag_name"); + columnNameDict.put(TAG, "tag_names"); columnNameDict.put(TYPE, "vds_type"); columnNameDict.put(DATACENTER, "storage_pool_name"); columnNameDict.put("ID", "vds_id"); diff --git a/backend/manager/modules/searchbackend/src/main/java/org/ovirt/engine/core/searchbackend/VmConditionFieldAutoCompleter.java b/backend/manager/modules/searchbackend/src/main/java/org/ovirt/engine/core/searchbackend/VmConditionFieldAutoCompleter.java index 5084a7e7aec..b86e98178fb 100644 --- a/backend/manager/modules/searchbackend/src/main/java/org/ovirt/engine/core/searchbackend/VmConditionFieldAutoCompleter.java +++ b/backend/manager/modules/searchbackend/src/main/java/org/ovirt/engine/core/searchbackend/VmConditionFieldAutoCompleter.java @@ -117,7 +117,7 @@ public VmConditionFieldAutoCompleter() { getTypeDictionary().put(CLUSTER, String.class); getTypeDictionary().put(POOL, String.class); getTypeDictionary().put(LOGGEDINUSER, String.class); - getTypeDictionary().put(TAG, String.class); + getTypeDictionary().put(TAG, String[].class); getTypeDictionary().put(DATACENTER, String.class); getTypeDictionary().put(TYPE, VmType.class); getTypeDictionary().put(QUOTA, String.class); @@ -155,7 +155,7 @@ public VmConditionFieldAutoCompleter() { columnNameDict.put(CLUSTER, "cluster_name"); columnNameDict.put(POOL, "vm_pool_name"); columnNameDict.put(LOGGEDINUSER, "guest_cur_user_name"); - columnNameDict.put(TAG, "tag_name"); + columnNameDict.put(TAG, "tag_names"); columnNameDict.put(DATACENTER, "storage_pool_name"); columnNameDict.put(TYPE, "vm_type"); columnNameDict.put(QUOTA, "quota_name"); diff --git a/backend/manager/modules/searchbackend/src/test/java/org/ovirt/engine/core/searchbackend/SyntaxCheckerTest.java b/backend/manager/modules/searchbackend/src/test/java/org/ovirt/engine/core/searchbackend/SyntaxCheckerTest.java index f28a108a778..c1f8576d654 100644 --- a/backend/manager/modules/searchbackend/src/test/java/org/ovirt/engine/core/searchbackend/SyntaxCheckerTest.java +++ b/backend/manager/modules/searchbackend/src/test/java/org/ovirt/engine/core/searchbackend/SyntaxCheckerTest.java @@ -23,8 +23,8 @@ @ExtendWith(MockConfigExtension.class) public class SyntaxCheckerTest { - private static final String TAG_NAME = "'tag1'"; - private static final String TAG_NAME_WITH_CHILDREN = "'tag1','all'"; + private static final String TAG_NAME = "\"tag1\""; + private static final String TAG_NAME_WITH_CHILDREN = "\"tag1\",\"all\""; public static Stream> mockConfiguration() { return Stream.of( @@ -47,6 +47,8 @@ public void setup() { when(BaseConditionFieldAutoCompleter.tagsHandler.getTagByTagName(any())).thenReturn(tags); when(BaseConditionFieldAutoCompleter.tagsHandler.getTagNamesAndChildrenNamesByRegExp(any())) .thenReturn(TAG_NAME_WITH_CHILDREN); + when(BaseConditionFieldAutoCompleter.tagsHandler.getTagNameAndChildrenNames(any())) + .thenReturn(TAG_NAME_WITH_CHILDREN); } /** @@ -157,9 +159,9 @@ public void testHost() { testValidSql("Host: EVENT.severity=error and CPU_USAGE > 80 sortby cpu_usage desc", "SELECT * FROM ((SELECT distinct vds.* FROM vds LEFT OUTER JOIN audit_log ON vds.vds_id=audit_log.vds_id WHERE ( audit_log.severity = '2' AND vds.usage_cpu_percent > 80 )) ORDER BY usage_cpu_percent DESC NULLS LAST,vds_name ASC ) as T1 OFFSET (1 -1) LIMIT 0"); testValidSql("Host: EVENT.severity=error and tag=tag1 sortby cpu_usage desc", - "SELECT * FROM (SELECT * FROM vds WHERE ( vds_id IN (SELECT distinct vds_with_tags.vds_id FROM vds_with_tags LEFT OUTER JOIN audit_log ON vds_with_tags.vds_id=audit_log.vds_id WHERE ( audit_log.severity = '2' AND vds_with_tags.tag_name IN ('tag1','all') ))) ORDER BY usage_cpu_percent DESC NULLS LAST,vds_name ASC ) as T1 OFFSET (1 -1) LIMIT 0"); + "SELECT * FROM (SELECT * FROM vds WHERE ( vds_id IN (SELECT distinct vds_with_tags.vds_id FROM vds_with_tags LEFT OUTER JOIN audit_log ON vds_with_tags.vds_id=audit_log.vds_id WHERE ( audit_log.severity = '2' AND (vds_with_tags.tag_names IS NOT NULL AND (vds_with_tags.tag_names && '{\"tag1\",\"all\"}')) ))) ORDER BY usage_cpu_percent DESC NULLS LAST,vds_name ASC ) as T1 OFFSET (1 -1) LIMIT 0"); testValidSql("Host: tag=\"tag1\"", - "SELECT * FROM (SELECT * FROM vds WHERE ( vds_id IN (SELECT distinct vds_with_tags.vds_id FROM vds_with_tags WHERE vds_with_tags.tag_name IN ('tag1','all') )) ORDER BY vds_name ASC ) as T1 OFFSET (1 -1) LIMIT 0"); + "SELECT * FROM (SELECT * FROM vds WHERE ( vds_id IN (SELECT distinct vds_with_tags.vds_id FROM vds_with_tags WHERE (vds_with_tags.tag_names IS NOT NULL AND (vds_with_tags.tag_names && '{\"tag1\",\"all\"}')) )) ORDER BY vds_name ASC ) as T1 OFFSET (1 -1) LIMIT 0"); // Before: 22ms // "SELECT * FROM (SELECT * FROM vds WHERE ( vds_id IN (SELECT vds_with_tags.vds_id FROM vds_with_tags LEFT OUTER JOIN vms_with_tags ON vds_with_tags.vds_id=vms_with_tags.run_on_vds WHERE vms_with_tags.vm_name LIKE 'vm1' )) ORDER BY vds_name ASC ) as T1 OFFSET (1 -1) LIMIT 0" // Current: 11ms @@ -195,17 +197,17 @@ public void testVm() { testValidSql("Vm: user.name = user1", "SELECT * FROM ((SELECT distinct vms.* FROM vms LEFT OUTER JOIN vdc_users_with_tags ON vms.vm_guid=vdc_users_with_tags.vm_guid WHERE vdc_users_with_tags.name LIKE user1 ) ORDER BY vm_name ASC ) as T1 OFFSET (1 -1) LIMIT 0"); testValidSql("Vm: user.name = \"user1\" and user.tag=\"tag1\"", - "SELECT * FROM (SELECT * FROM vms WHERE ( vm_guid IN (SELECT distinct vms_with_tags.vm_guid FROM vms_with_tags LEFT OUTER JOIN vdc_users_with_tags ON vms_with_tags.vm_guid=vdc_users_with_tags.vm_guid WHERE ( vdc_users_with_tags.name LIKE user1 AND vdc_users_with_tags.tag_name IN ('tag1','all') ))) ORDER BY vm_name ASC ) as T1 OFFSET (1 -1) LIMIT 0"); + "SELECT * FROM (SELECT * FROM vms WHERE ( vm_guid IN (SELECT distinct vms_with_tags.vm_guid FROM vms_with_tags LEFT OUTER JOIN vdc_users_with_tags ON vms_with_tags.vm_guid=vdc_users_with_tags.vm_guid WHERE ( vdc_users_with_tags.name LIKE user1 AND (vdc_users_with_tags.tag_names IS NOT NULL AND (vdc_users_with_tags.tag_names && '{\"tag1\",\"all\"}')) ))) ORDER BY vm_name ASC ) as T1 OFFSET (1 -1) LIMIT 0"); // Used to validate that searching values not in fields search all fields testValidSql("Vm: mac=00:1a:4a:d4:53:94", - "SELECT * FROM (SELECT * FROM vms WHERE ( vm_guid IN (SELECT distinct vms_with_tags.vm_guid FROM vms_with_tags WHERE ( vms_with_tags.cluster_compatibility_version LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.cluster_name LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.custom_cpu_name LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.custom_emulated_machine LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.description LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.free_text_comment LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.guest_cur_user_name LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.namespace LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.quota_name LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.run_on_vds_name LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.storage_pool_name LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.tag_name LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.vm_fqdn LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.vm_host LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.vm_ip LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.vm_name LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.vm_pool_name LIKE '%mac=00:1a:4a:d4:53:94%' ) )) ORDER BY vm_name ASC ) as T1 OFFSET (1 -1) LIMIT 0"); + "SELECT * FROM (SELECT * FROM vms WHERE ( vm_guid IN (SELECT distinct vms_with_tags.vm_guid FROM vms_with_tags WHERE ( vms_with_tags.cluster_compatibility_version LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.cluster_name LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.custom_cpu_name LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.custom_emulated_machine LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.description LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.free_text_comment LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.guest_cur_user_name LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.namespace LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.quota_name LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.run_on_vds_name LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.storage_pool_name LIKE '%mac=00:1a:4a:d4:53:94%' OR (select bool_or(is_match_rows) from (select unnest(vms_with_tags.tag_names) LIKE '%mac=00:1a:4a:d4:53:94%' as is_match_rows)) OR vms_with_tags.vm_fqdn LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.vm_host LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.vm_ip LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.vm_name LIKE '%mac=00:1a:4a:d4:53:94%' OR vms_with_tags.vm_pool_name LIKE '%mac=00:1a:4a:d4:53:94%' ) )) ORDER BY vm_name ASC ) as T1 OFFSET (1 -1) LIMIT 0"); // Testing that in case that function is used in the ORDER BY clause then it is converted with a computed field testValidSql("Vms: SORTBY IP DESC", "SELECT * FROM ((SELECT vms.* FROM vms ) ORDER BY vm_ip_inet_array DESC NULLS LAST,vm_name ASC ) as T1 OFFSET (1 -1) LIMIT 0"); // Testing searching Vms with cluster and Host properties testValidSql("Vms: Cluster = L0_Group_3 and Host = f17-h29*", - "SELECT * FROM (SELECT * FROM vms WHERE ( vm_guid IN (SELECT distinct vms_with_tags.vm_guid FROM vms_with_tags LEFT OUTER JOIN vds_with_tags ON vms_with_tags.run_on_vds=vds_with_tags.vds_id WHERE ( vms_with_tags.cluster_name LIKE L0\\_Group\\_3 AND ( vds_with_tags.cluster_name LIKE '%f17-h29%%' OR vds_with_tags.cpu_model LIKE '%f17-h29%%' OR vds_with_tags.free_text_comment LIKE '%f17-h29%%' OR vds_with_tags.host_name LIKE '%f17-h29%%' OR vds_with_tags.software_version LIKE '%f17-h29%%' OR vds_with_tags.storage_pool_name LIKE '%f17-h29%%' OR vds_with_tags.tag_name LIKE '%f17-h29%%' OR vds_with_tags.vds_name LIKE '%f17-h29%%' ) ))) ORDER BY vm_name ASC ) as T1 OFFSET (1 -1) LIMIT 0"); + "SELECT * FROM (SELECT * FROM vms WHERE ( vm_guid IN (SELECT distinct vms_with_tags.vm_guid FROM vms_with_tags LEFT OUTER JOIN vds_with_tags ON vms_with_tags.run_on_vds=vds_with_tags.vds_id WHERE ( vms_with_tags.cluster_name LIKE L0\\_Group\\_3 AND ( vds_with_tags.cluster_name LIKE '%f17-h29%%' OR vds_with_tags.cpu_model LIKE '%f17-h29%%' OR vds_with_tags.free_text_comment LIKE '%f17-h29%%' OR vds_with_tags.host_name LIKE '%f17-h29%%' OR vds_with_tags.software_version LIKE '%f17-h29%%' OR vds_with_tags.storage_pool_name LIKE '%f17-h29%%' OR (select bool_or(is_match_rows) from (select unnest(vds_with_tags.tag_names) LIKE '%f17-h29%%' as is_match_rows)) OR vds_with_tags.vds_name LIKE '%f17-h29%%' ) ))) ORDER BY vm_name ASC ) as T1 OFFSET (1 -1) LIMIT 0"); } @@ -268,7 +270,7 @@ public void testTemplateUsersUserName() { @Test public void testTemplateUsersAnyField() { testValidSql("Templates: Users = *", - "SELECT * FROM (SELECT * FROM vm_templates_view WHERE ( vmt_guid IN (SELECT distinct vm_templates_storage_domain.vmt_guid FROM vm_templates_storage_domain LEFT OUTER JOIN vms_with_tags ON vm_templates_storage_domain.vmt_guid=vms_with_tags.vmt_guid LEFT OUTER JOIN vdc_users_with_tags ON vms_with_tags.vm_guid=vdc_users_with_tags.vm_guid WHERE ( vdc_users_with_tags.department LIKE '%%%' OR vdc_users_with_tags.domain LIKE '%%%' OR vdc_users_with_tags.name LIKE '%%%' OR vdc_users_with_tags.surname LIKE '%%%' OR vdc_users_with_tags.tag_name LIKE '%%%' OR vdc_users_with_tags.user_and_domain LIKE '%%%' OR vdc_users_with_tags.username LIKE '%%%' OR vdc_users_with_tags.vm_pool_name LIKE '%%%' ) )) ORDER BY name ASC ) as T1 OFFSET (1 -1) LIMIT 0"); + "SELECT * FROM (SELECT * FROM vm_templates_view WHERE ( vmt_guid IN (SELECT distinct vm_templates_storage_domain.vmt_guid FROM vm_templates_storage_domain LEFT OUTER JOIN vms_with_tags ON vm_templates_storage_domain.vmt_guid=vms_with_tags.vmt_guid LEFT OUTER JOIN vdc_users_with_tags ON vms_with_tags.vm_guid=vdc_users_with_tags.vm_guid WHERE ( vdc_users_with_tags.department LIKE '%%%' OR vdc_users_with_tags.domain LIKE '%%%' OR vdc_users_with_tags.name LIKE '%%%' OR vdc_users_with_tags.surname LIKE '%%%' OR (select bool_or(is_match_rows) from (select unnest(vdc_users_with_tags.tag_names) LIKE '%%%' as is_match_rows)) OR vdc_users_with_tags.user_and_domain LIKE '%%%' OR vdc_users_with_tags.username LIKE '%%%' OR vdc_users_with_tags.vm_pool_name LIKE '%%%' ) )) ORDER BY name ASC ) as T1 OFFSET (1 -1) LIMIT 0"); } @Test @@ -295,7 +297,7 @@ public void testUsersTypeUser() { @Test public void testUsersWithTags() { testValidSql("Users:type=user tag=foo", - "SELECT * FROM (SELECT * FROM vdc_users WHERE ( user_id IN (SELECT distinct vdc_users_with_tags.user_id FROM vdc_users_with_tags WHERE vdc_users_with_tags.user_group = user AND vdc_users_with_tags.tag_name IN ('tag1','all') )) ORDER BY name ASC ) as T1 OFFSET (1 -1) LIMIT 0"); + "SELECT * FROM (SELECT * FROM vdc_users WHERE ( user_id IN (SELECT distinct vdc_users_with_tags.user_id FROM vdc_users_with_tags WHERE vdc_users_with_tags.user_group = user AND (vdc_users_with_tags.tag_names IS NOT NULL AND (vdc_users_with_tags.tag_names && '{\"tag1\",\"all\"}')) )) ORDER BY name ASC ) as T1 OFFSET (1 -1) LIMIT 0"); } @Test @@ -501,7 +503,7 @@ public void testImageTypes() { @Test public void testVmWithTags() { testValidSql("VMs:tag=all", - "SELECT * FROM (SELECT * FROM vms WHERE ( vm_guid IN (SELECT distinct vms_with_tags.vm_guid FROM vms_with_tags WHERE vms_with_tags.tag_name IN ('tag1','all') )) ORDER BY vm_name ASC ) as T1 OFFSET (1 -1) LIMIT 0"); + "SELECT * FROM (SELECT * FROM vms WHERE ( vm_guid IN (SELECT distinct vms_with_tags.vm_guid FROM vms_with_tags WHERE (vms_with_tags.tag_names IS NOT NULL AND (vms_with_tags.tag_names && '{\"tag1\",\"all\"}')) )) ORDER BY vm_name ASC ) as T1 OFFSET (1 -1) LIMIT 0"); } @Test diff --git a/packaging/dbscripts/create_views.sql b/packaging/dbscripts/create_views.sql index 0b2f5e56d2d..eccff11b934 100644 --- a/packaging/dbscripts/create_views.sql +++ b/packaging/dbscripts/create_views.sql @@ -1618,8 +1618,8 @@ SELECT DISTINCT vms.vm_name, vms.cluster_cpu_name, vms.cluster_cpu_flags, vms.cluster_cpu_verb, - tags_vm_map_view.tag_name, - tags_vm_map_view.tag_id, + (select array_agg(tag_name::text) from tags_vm_map_view where tags_vm_map_view.vm_id = vms.vm_guid) as tag_names, + (select array_agg(tag_id) from tags_vm_map_view where tags_vm_map_view.vm_id = vms.vm_guid) as tag_ids, vms.default_display_type, vms.iso_path, vms.origin, @@ -1725,8 +1725,6 @@ SELECT DISTINCT vms.vm_name, vms.current_numa_pinning, vms.parallel_migrations FROM vms -LEFT JOIN tags_vm_map_view - ON vms.vm_guid = tags_vm_map_view.vm_id LEFT JOIN vm_device ON vm_device.vm_id = vms.vm_guid LEFT JOIN images @@ -1993,8 +1991,8 @@ SELECT cluster.cluster_id, cluster.count_threads_as_cores, storage_pool.id AS storage_pool_id, storage_pool.name AS storage_pool_name, - tags_vds_map_view.tag_name, - tags_vds_map_view.tag_id, + (select array_agg(tag_name::text) from tags_vds_map_view where vds_static.vds_id = tags_vds_map_view.vds_id) as tag_names, + (select array_agg(tag_id) from tags_vds_map_view where vds_static.vds_id = tags_vds_map_view.vds_id) as tag_ids, vds_dynamic.reserved_mem, vds_dynamic.guest_overhead, vds_dynamic.rpm_version, @@ -2112,8 +2110,6 @@ INNER JOIN vds_statistics ON vds_static.vds_id = vds_statistics.vds_id LEFT JOIN storage_pool ON cluster.storage_pool_id = storage_pool.id -LEFT JOIN tags_vds_map_view - ON vds_static.vds_id = tags_vds_map_view.vds_id LEFT JOIN vds_spm_id_map ON vds_static.vds_id = vds_spm_id_map.vds_id LEFT JOIN storage_pool_iso_map @@ -2180,8 +2176,8 @@ SELECT users_1.user_group AS user_group, users_1.email AS email, users_1.note AS note, users_1.vm_admin AS vm_admin, - tags_user_map_view_1.tag_name AS tag_name, - tags_user_map_view_1.tag_id AS tag_id, + (select array_agg(tag_name::text) from tags_user_map_view where tags_user_map_view.user_id = users_1.user_id) as tag_names, + (select array_agg(tag_id) from tags_user_map_view where tags_user_map_view.user_id = users_1.user_id) as tag_ids, users_1.last_admin_check_status AS last_admin_check_status, users_1.external_id AS external_id, users_1.namespace AS namespace, @@ -2193,8 +2189,6 @@ LEFT JOIN permissions ON users_1.user_id = permissions.ad_element_id LEFT JOIN tags ON tags.type = 1 -LEFT JOIN tags_user_map_view AS tags_user_map_view_1 - ON users_1.user_id = tags_user_map_view_1.user_id LEFT JOIN roles AS roles1 ON roles1.id = permissions.role_id WHERE (users_1.user_group = 'user') @@ -2214,8 +2208,8 @@ SELECT users_2.user_group AS user_group, users_2.email AS email, users_2.note AS note, users_2.vm_admin AS vm_admin, - tags_user_group_map_view.tag_name AS tag_name, - tags_user_group_map_view.tag_id AS tag_id, + (select array_agg(tag_name::text) from tags_user_group_map_view where users_2.user_id = tags_user_group_map_view.group_id) as tag_names, + (select array_agg(tag_id) from tags_user_group_map_view where users_2.user_id = tags_user_group_map_view.group_id) as tag_ids, users_2.last_admin_check_status AS last_admin_check_status, users_2.external_id AS external_id, users_2.namespace AS namespace, @@ -2227,8 +2221,6 @@ LEFT JOIN permissions AS permissions_1 ON users_2.user_id = permissions_1.ad_element_id LEFT JOIN tags AS tags_1 ON tags_1.type = 1 -LEFT JOIN tags_user_group_map_view - ON users_2.user_id = tags_user_group_map_view.group_id LEFT JOIN roles AS roles2 ON roles2.id = permissions_1.role_id WHERE (users_2.user_group = 'group');