This is an automated email from the ASF dual-hosted git repository. sarath pushed a commit to branch branch-2.0 in repository https://gitbox.apache.org/repos/asf/atlas.git
The following commit(s) were added to refs/heads/branch-2.0 by this push: new 0296ad6 ATLAS-4112 : Basic Search : Attribute search of QualifiedName beginswith operator not returning results when the value ends with a digit+dot 0296ad6 is described below commit 0296ad6f4acbc00b66811655aa5729cff00769d0 Author: Pinal <pinal-shah> AuthorDate: Wed Jan 27 10:24:24 2021 +0530 ATLAS-4112 : Basic Search : Attribute search of QualifiedName beginswith operator not returning results when the value ends with a digit+dot Signed-off-by: Sarath Subramanian <sar...@apache.org> (cherry picked from commit e873515f5c9bd4cbc6eda9609034779bcddaf3a4) --- .../org/apache/atlas/type/AtlasStructType.java | 48 ++- .../apache/atlas/discovery/SearchProcessor.java | 66 ++-- .../atlas/discovery/AtlasDiscoveryServiceTest.java | 352 ++++++++++++++++++++- 3 files changed, 439 insertions(+), 27 deletions(-) diff --git a/intg/src/main/java/org/apache/atlas/type/AtlasStructType.java b/intg/src/main/java/org/apache/atlas/type/AtlasStructType.java index 0509809..d89aca2 100644 --- a/intg/src/main/java/org/apache/atlas/type/AtlasStructType.java +++ b/intg/src/main/java/org/apache/atlas/type/AtlasStructType.java @@ -960,10 +960,14 @@ public class AtlasStructType extends AtlasType { } public static String escapeIndexQueryValue(String value) { - return escapeIndexQueryValue(value, false); + return escapeIndexQueryValue(value, false, true); } public static String escapeIndexQueryValue(String value, boolean allowWildcard) { + return escapeIndexQueryValue(value, allowWildcard, true); + } + + public static String escapeIndexQueryValue(String value, boolean allowWildcard, boolean shouldQuote) { String ret = value; boolean quoteValue = false; @@ -977,7 +981,7 @@ public class AtlasStructType extends AtlasType { sb.append('\\'); } - if (!quoteValue) { + if (shouldQuote && !quoteValue) { quoteValue = shouldQuoteIndexQueryForChar(c); } @@ -987,7 +991,7 @@ public class AtlasStructType extends AtlasType { ret = sb.toString(); } else if (value != null) { for (int i = 0; i < value.length(); i++) { - if (shouldQuoteIndexQueryForChar(value.charAt(i))) { + if (shouldQuote && shouldQuoteIndexQueryForChar(value.charAt(i))) { quoteValue = true; break; @@ -1047,12 +1051,50 @@ public class AtlasStructType extends AtlasType { case ':': case '\\': case '/': + case ' ': return true; } return false; } + public static boolean hastokenizeChar(String value) { + if (value != null) { + for (int i = 0; i < value.length(); i++) { + if (hastokenizeChar(value, i)) { + return true; + } + } + } + + return false; + } + + + private static boolean hastokenizeChar(String value, int i) { + char c = value.charAt(i); + if (!Character.isLetterOrDigit(c)) { + switch (c) { + case '_': + return false; + case '.': + case ':': + case '\'': + if (i > 0 && !Character.isAlphabetic(value.charAt(i - 1))) { + return true; + } + if (i < value.length() - 1 && !Character.isAlphabetic(value.charAt(i + 1))) { + return true; + } + return false; + } + + return true; + } + + return false; + } + private static boolean shouldQuoteIndexQueryForChar(char c) { switch (c) { case '@': diff --git a/repository/src/main/java/org/apache/atlas/discovery/SearchProcessor.java b/repository/src/main/java/org/apache/atlas/discovery/SearchProcessor.java index 01daf53..275fc78 100644 --- a/repository/src/main/java/org/apache/atlas/discovery/SearchProcessor.java +++ b/repository/src/main/java/org/apache/atlas/discovery/SearchProcessor.java @@ -26,6 +26,7 @@ import org.apache.atlas.model.discovery.SearchParameters; import org.apache.atlas.model.discovery.SearchParameters.FilterCriteria; import org.apache.atlas.model.discovery.SearchParameters.FilterCriteria.Condition; import org.apache.atlas.model.typedef.AtlasBaseTypeDef; +import org.apache.atlas.model.typedef.AtlasStructDef; import org.apache.atlas.repository.Constants; import org.apache.atlas.repository.graph.GraphHelper; import org.apache.atlas.repository.graphdb.AtlasGraphQuery; @@ -459,26 +460,31 @@ public abstract class SearchProcessor { } private boolean isIndexSearchable(FilterCriteria filterCriteria, AtlasStructType structType) throws AtlasBaseException { - String qualifiedName = structType.getVertexPropertyName(filterCriteria.getAttributeName()); - Set<String> indexedKeys = context.getIndexedKeys(); - boolean ret = indexedKeys != null && indexedKeys.contains(qualifiedName); + String attributeName = filterCriteria.getAttributeName(); + String attributeValue = filterCriteria.getAttributeValue(); + AtlasType attributeType = structType.getAttributeType(attributeName); + String typeName = attributeType.getTypeName(); + String qualifiedName = structType.getVertexPropertyName(attributeName); + Set<String> indexedKeys = context.getIndexedKeys(); + boolean ret = indexedKeys != null && indexedKeys.contains(qualifiedName); + + SearchParameters.Operator operator = filterCriteria.getOperator(); + AtlasStructDef.AtlasAttributeDef.IndexType indexType = structType.getAttributeDef(attributeName).getIndexType(); if (ret) { // index exists // for string type attributes, don't use index query in the following cases: // - operation is NEQ, as it might return fewer entries due to tokenization of vertex property value // - value-to-compare has special characters - AtlasType attributeType = structType.getAttributeType(filterCriteria.getAttributeName()); - - if (AtlasBaseTypeDef.ATLAS_TYPE_STRING.equals(attributeType.getTypeName())) { - if (filterCriteria.getOperator() == SearchParameters.Operator.NEQ || filterCriteria.getOperator() == SearchParameters.Operator.NOT_CONTAINS) { + if (AtlasBaseTypeDef.ATLAS_TYPE_STRING.equals(typeName)) { + if (operator == SearchParameters.Operator.NEQ || operator == SearchParameters.Operator.NOT_CONTAINS) { if (LOG.isDebugEnabled()) { - LOG.debug("{} operator found for string attribute {}, deferring to in-memory or graph query (might cause poor performance)", filterCriteria.getOperator(), qualifiedName); + LOG.debug("{} operator found for string attribute {}, deferring to in-memory or graph query (might cause poor performance)", operator, qualifiedName); } ret = false; - } else if (hasIndexQuerySpecialChar(filterCriteria.getAttributeValue()) && !isPipeSeparatedSystemAttribute(filterCriteria.getAttributeName())) { + } else if (operator == SearchParameters.Operator.CONTAINS && AtlasAttribute.hastokenizeChar(attributeValue) && indexType == null) { // indexType = TEXT if (LOG.isDebugEnabled()) { - LOG.debug("special characters found in filter value {}, deferring to in-memory or graph query (might cause poor performance)", filterCriteria.getAttributeValue()); + LOG.debug("{} operator found for string (TEXT) attribute {} and special characters found in filter value {}, deferring to in-memory or graph query (might cause poor performance)", attributeValue); } ret = false; @@ -488,7 +494,7 @@ public abstract class SearchProcessor { if (LOG.isDebugEnabled()) { if (!ret) { - LOG.debug("Not using index query for: attribute='{}', operator='{}', value='{}'", qualifiedName, filterCriteria.getOperator(), filterCriteria.getAttributeValue()); + LOG.debug("Not using index query for: attribute='{}', operator='{}', value='{}'", qualifiedName, operator, attributeValue); } } @@ -788,14 +794,36 @@ public abstract class SearchProcessor { ret = String.format(OPERATOR_MAP.get(op), qualifiedName, rangeStartIndexQueryValue, rangeEndIndexQueryValue); } } else { - // map '__customAttributes' 'CONTAINS' operator to 'EQ' operator (solr limitation for json serialized string search) - // map '__customAttributes' value from 'key1=value1' to '\"key1\":\"value1\"' (escape special characters and surround with quotes) - String escapeIndexQueryValue = AtlasAttribute.escapeIndexQueryValue(attrVal); - if (attrName.equals(CUSTOM_ATTRIBUTES_PROPERTY_KEY) && op == SearchParameters.Operator.CONTAINS) { - ret = String.format(OPERATOR_MAP.get(op), qualifiedName, getCustomAttributeIndexQueryValue(escapeIndexQueryValue, false)); - } else { - ret = String.format(OPERATOR_MAP.get(op), qualifiedName, escapeIndexQueryValue); - } + String escapeIndexQueryValue; + boolean replaceWildcardChar = false; + + AtlasStructDef.AtlasAttributeDef def = type.getAttributeDef(attrName); + + //when wildcard search -> escape special Char, don't quote + // when tokenized characters + index field Type TEXT -> remove wildcard '*' from query + if (!isPipeSeparatedSystemAttribute(attrName) + && (op == SearchParameters.Operator.CONTAINS || op == SearchParameters.Operator.STARTS_WITH || op == SearchParameters.Operator.ENDS_WITH) + && def.getTypeName().equalsIgnoreCase(AtlasBaseTypeDef.ATLAS_TYPE_STRING)) { + + escapeIndexQueryValue = AtlasAttribute.escapeIndexQueryValue(attrVal, false, false); + if (def.getIndexType() == null && AtlasAttribute.hastokenizeChar(attrVal)) { + replaceWildcardChar = true; + } + } else { + escapeIndexQueryValue = AtlasAttribute.escapeIndexQueryValue(attrVal); + } + + String operatorStr = OPERATOR_MAP.get(op); + if (replaceWildcardChar) { + operatorStr = operatorStr.replace("*", ""); + } + + // map '__customAttributes' value from 'key1=value1' to '\"key1\":\"value1\"' (escape special characters and surround with quotes) + if (attrName.equals(CUSTOM_ATTRIBUTES_PROPERTY_KEY) && op == SearchParameters.Operator.CONTAINS) { + ret = String.format(operatorStr, qualifiedName, getCustomAttributeIndexQueryValue(escapeIndexQueryValue, false)); + } else { + ret = String.format(operatorStr, qualifiedName, escapeIndexQueryValue); + } } } } catch (AtlasBaseException ex) { diff --git a/repository/src/test/java/org/apache/atlas/discovery/AtlasDiscoveryServiceTest.java b/repository/src/test/java/org/apache/atlas/discovery/AtlasDiscoveryServiceTest.java index 0da60d3..9846d43 100644 --- a/repository/src/test/java/org/apache/atlas/discovery/AtlasDiscoveryServiceTest.java +++ b/repository/src/test/java/org/apache/atlas/discovery/AtlasDiscoveryServiceTest.java @@ -18,20 +18,21 @@ package org.apache.atlas.discovery; import org.apache.atlas.ApplicationProperties; +import org.apache.atlas.AtlasClient; import org.apache.atlas.BasicTestSetup; import org.apache.atlas.TestModules; import org.apache.atlas.exception.AtlasBaseException; +import org.apache.atlas.model.discovery.AtlasSearchResult; import org.apache.atlas.model.discovery.SearchParameters; import org.apache.atlas.model.instance.AtlasClassification; +import org.apache.atlas.model.instance.AtlasEntity; import org.apache.atlas.model.instance.AtlasEntityHeader; import org.apache.atlas.model.instance.EntityMutationResponse; import org.apache.atlas.repository.graph.AtlasGraphProvider; +import org.apache.atlas.repository.store.graph.v2.AtlasEntityStream; import org.apache.commons.collections.CollectionUtils; import org.testng.Assert; -import org.testng.annotations.AfterClass; -import org.testng.annotations.BeforeClass; -import org.testng.annotations.Guice; -import org.testng.annotations.Test; +import org.testng.annotations.*; import javax.inject.Inject; import java.util.Arrays; @@ -39,7 +40,8 @@ import java.util.HashMap; import java.util.List; import static org.apache.atlas.model.discovery.SearchParameters.*; -import static org.testng.Assert.assertEquals; +import static org.testng.Assert.*; +import static org.testng.Assert.assertNotNull; @Guice(modules = TestModules.TestOnlyModule.class) public class AtlasDiscoveryServiceTest extends BasicTestSetup { @@ -54,6 +56,7 @@ public class AtlasDiscoveryServiceTest extends BasicTestSetup { ApplicationProperties.get().setProperty(ApplicationProperties.ENABLE_FREETEXT_SEARCH_CONF, true); setupTestData(); createDimensionalTaggedEntity("sales"); + createSpecialCharTestEntities(); } /* TermSearchProcessor(TSP), @@ -322,6 +325,345 @@ public class AtlasDiscoveryServiceTest extends BasicTestSetup { assertEquals(entityHeaders.size(), 4); } + String spChar1 = "default.test_dot_name"; + String spChar2 = "default.test_dot_name@db.test_db"; + String spChar3 = "default.test_dot_name_12.col1@db1"; + String spChar4 = "default_.test_dot_name"; + + String spChar5 = "default.test_colon_name:test_db"; + String spChar6 = "default.test_colon_name:-test_db"; + String spChar7 = "crn:def:default:name-76778-87e7-23@test"; + String spChar8 = "default.:test_db_name"; + + String spChar9 = "default.customer's_name"; + String spChar10 = "default.customers'_data_name"; + + String spChar11 = "search_with space@name"; + String spChar12 = "search_with space 123@name"; + + //SearchProcessor.isIndexQuerySpecialChar + String spChar13 = "search_with_special-char#having$and%inthename=attr"; + String spChar14 = "search_with_specialChar!name"; + String spChar15 = "search_with_star*in_name"; + String spChar16 = "search_with_star5.*5_inname"; + String spChar17 = "search_quest?n_name"; + + String spChar18 = "/warehouse/tablespace/external/hive/name/hortonia_bank"; + String spChar19 = "/warehouse/tablespace/external/hive/name/exis_bank"; + + + @DataProvider(name = "specialCharSearchContains") + private Object[][] specialCharSearchContains() { + return new Object[][]{ + {"name",Operator.CONTAINS,"test_dot",4}, + {"name",Operator.CONTAINS,"test_dot_name_",1}, + {"name",Operator.CONTAINS,"test_colon_name",2}, + {"name",Operator.CONTAINS,"def:default:name",1}, + {"name",Operator.CONTAINS,"space 12",1}, + {"name",Operator.CONTAINS,"with space",2}, + {"name",Operator.CONTAINS,"Char!name",1}, + {"name",Operator.CONTAINS,"with_star",2}, + {"name",Operator.CONTAINS,"/external/hive/name/",2}, + + {"name",Operator.CONTAINS,"test_dot_name@db",1}, + {"name",Operator.CONTAINS,"name@db",1}, + {"name",Operator.CONTAINS,"def:default:name-",1}, + {"name",Operator.CONTAINS,"star*in",1}, + {"name",Operator.CONTAINS,"Char!na",1}, + {"name",Operator.CONTAINS,"ith spac",2}, + {"name",Operator.CONTAINS,"778-87",1}, + + {"qualifiedName",Operator.CONTAINS,"test_dot",4}, + {"qualifiedName",Operator.CONTAINS,"test_dot_qf_",1}, + {"qualifiedName",Operator.CONTAINS,"test_colon_qf",2}, + {"qualifiedName",Operator.CONTAINS,"def:default:qf",1}, + {"qualifiedName",Operator.CONTAINS,"space 12",1}, + {"qualifiedName",Operator.CONTAINS,"with space",2}, + {"qualifiedName",Operator.CONTAINS,"Char!qf",1}, + {"qualifiedName",Operator.CONTAINS,"with_star",2}, + {"qualifiedName",Operator.CONTAINS,"/external/hive/qf/",2}, + + {"qualifiedName",Operator.CONTAINS,"test_dot_qf@db",1}, + {"qualifiedName",Operator.CONTAINS,"qf@db",1}, + {"qualifiedName",Operator.CONTAINS,"def:default:qf-",1}, + {"qualifiedName",Operator.CONTAINS,"star*in",1}, + {"qualifiedName",Operator.CONTAINS,"Char!q",1}, + {"qualifiedName",Operator.CONTAINS,"ith spac",2}, + {"qualifiedName",Operator.CONTAINS,"778-87",1}, + }; + } + + @DataProvider(name = "specialCharSearchName") + private Object[][] specialCharSearchName() { + return new Object[][]{ + + {"name",Operator.STARTS_WITH,"default.test_dot_",3}, + + {"name",Operator.STARTS_WITH,"default.test_dot_n...@db.test",1}, + {"name",Operator.STARTS_WITH,"default.test_dot_name@db.",1}, + {"name",Operator.STARTS_WITH,"default.test_dot_name",3}, + {"name",Operator.ENDS_WITH,"test_db",3}, + + {"name",Operator.STARTS_WITH,"default.test_dot_name_12.col1@db",1}, + {"name",Operator.STARTS_WITH,"default.test_dot_name_12.col1@",1}, + {"name",Operator.STARTS_WITH,"default.test_dot_name_12.col1",1}, + {"name",Operator.STARTS_WITH,"default.test_dot_name_12.col",1}, + {"name",Operator.STARTS_WITH,"default.test_dot_name_12.",1}, + {"name",Operator.STARTS_WITH,"default.test_dot_name_12",1}, + {"name",Operator.ENDS_WITH,"col1@db1",1}, + + {"name",Operator.STARTS_WITH,"default_.test_dot",1}, + {"name",Operator.ENDS_WITH,"test_dot_name",2}, + + {"name",Operator.STARTS_WITH,"default.test_colon_name:test_",1}, + + {"name",Operator.STARTS_WITH,"default.test_colon_name:-test_",1}, + {"name",Operator.STARTS_WITH,"default.test_colon_name:-",1}, + {"name",Operator.STARTS_WITH,"default.test_colon",2}, + + {"name",Operator.STARTS_WITH,"crn:def:default:name-76778-87e7-23@",1}, + {"name",Operator.STARTS_WITH,"crn:def:default:name-76778-87e7-",1}, + {"name",Operator.STARTS_WITH,"crn:def:default:",1}, + + {"name",Operator.STARTS_WITH,"default.:test_db",1}, + {"name",Operator.ENDS_WITH,"test_db_name",1}, + + {"name",Operator.STARTS_WITH,"default.customer's",1}, + {"name",Operator.ENDS_WITH,"mer's_name",1}, + + {"name",Operator.STARTS_WITH,"default.customers'_data",1}, + {"name",Operator.ENDS_WITH,"customers'_data_name",1}, + + {"name",Operator.STARTS_WITH,"search_with space",2}, + {"name",Operator.STARTS_WITH,"search_with space ",1}, + {"name",Operator.STARTS_WITH,"search_with space 123@",1}, + {"name",Operator.STARTS_WITH,"search_with space 1",1}, + + {"name",Operator.STARTS_WITH,"search_with_special-char#having$and%inthename=",1}, + {"name",Operator.STARTS_WITH,"search_with_special-char#having$and%in",1}, + {"name",Operator.STARTS_WITH,"search_with_special-char#having$",1}, + {"name",Operator.STARTS_WITH,"search_with_special-char#h",1}, + {"name",Operator.STARTS_WITH,"search_with_special",2}, + {"name",Operator.STARTS_WITH,"search_with_spe",2}, + + {"name",Operator.STARTS_WITH,"search_with_specialChar!",1}, + + {"name",Operator.STARTS_WITH,"search_with_star*in",1}, + + {"name",Operator.ENDS_WITH,"5.*5_inname",1}, + {"name",Operator.STARTS_WITH,"search_with_star5.*5_",1}, + + {"name",Operator.STARTS_WITH,"search_quest?n_",1}, + + {"name",Operator.STARTS_WITH,"/warehouse/tablespace/external/hive/name/hortonia",1}, + {"name",Operator.STARTS_WITH,"/warehouse/tablespace/external/hive/name/",2}, + + }; + } + + @DataProvider(name = "specialCharSearchQFName") + private Object[][] specialCharSearchQFName() { + return new Object[][]{ + + {"qualifiedName",Operator.STARTS_WITH,"default.test_dot_",3}, + + {"qualifiedName",Operator.STARTS_WITH,"default.test_dot...@db.test",1}, + {"qualifiedName",Operator.STARTS_WITH,"default.test_dot_qf@db.",1}, + {"qualifiedName",Operator.STARTS_WITH,"default.test_dot_qf",3}, + {"qualifiedName",Operator.ENDS_WITH,"test_db",3}, + + {"qualifiedName",Operator.STARTS_WITH,"default.test_dot_qf_12.col1@db",1}, + {"qualifiedName",Operator.STARTS_WITH,"default.test_dot_qf_12.col1@",1}, + {"qualifiedName",Operator.STARTS_WITH,"default.test_dot_qf_12.col1",1}, + {"qualifiedName",Operator.STARTS_WITH,"default.test_dot_qf_12.col",1}, + {"qualifiedName",Operator.STARTS_WITH,"default.test_dot_qf_12.",1}, + {"qualifiedName",Operator.STARTS_WITH,"default.test_dot_qf_12",1}, + {"qualifiedName",Operator.ENDS_WITH,"col1@db1",1}, + + {"qualifiedName",Operator.STARTS_WITH,"default_.test_dot",1}, + {"qualifiedName",Operator.ENDS_WITH,"test_dot_qf",2}, + + {"qualifiedName",Operator.STARTS_WITH,"default.test_colon_qf:test_",1}, + + {"qualifiedName",Operator.STARTS_WITH,"default.test_colon_qf:-test_",1}, + {"qualifiedName",Operator.STARTS_WITH,"default.test_colon_qf:-",1}, + {"qualifiedName",Operator.STARTS_WITH,"default.test_colon",2}, + + {"qualifiedName",Operator.STARTS_WITH,"crn:def:default:qf-76778-87e7-23@",1}, + {"qualifiedName",Operator.STARTS_WITH,"crn:def:default:qf-76778-87e7-",1}, + {"qualifiedName",Operator.STARTS_WITH,"crn:def:default:",1}, + + {"qualifiedName",Operator.STARTS_WITH,"default.:test_db",1}, + {"qualifiedName",Operator.ENDS_WITH,"test_db_qf",1}, + + {"qualifiedName",Operator.STARTS_WITH,"default.customer's",1}, + {"qualifiedName",Operator.ENDS_WITH,"mer's_qf",1}, + + {"qualifiedName",Operator.STARTS_WITH,"default.customers'_data",1}, + {"qualifiedName",Operator.ENDS_WITH,"customers'_data_qf",1}, + + {"qualifiedName",Operator.STARTS_WITH,"search_with space",2}, + {"qualifiedName",Operator.STARTS_WITH,"search_with space ",1}, + {"qualifiedName",Operator.STARTS_WITH,"search_with space 123@",1}, + {"qualifiedName",Operator.STARTS_WITH,"search_with space 1",1}, + + {"qualifiedName",Operator.STARTS_WITH,"search_with_special-char#having$and%intheqf=",1}, + {"qualifiedName",Operator.STARTS_WITH,"search_with_special-char#having$and%in",1}, + {"qualifiedName",Operator.STARTS_WITH,"search_with_special-char#having$",1}, + {"qualifiedName",Operator.STARTS_WITH,"search_with_special-char#h",1}, + {"qualifiedName",Operator.STARTS_WITH,"search_with_special",2}, + {"qualifiedName",Operator.STARTS_WITH,"search_with_spe",2}, + + {"qualifiedName",Operator.STARTS_WITH,"search_with_specialChar!",1}, + + {"qualifiedName",Operator.STARTS_WITH,"search_with_star*in",1}, + + {"qualifiedName",Operator.ENDS_WITH,"5.*5_inqf",1}, + {"qualifiedName",Operator.STARTS_WITH,"search_with_star5.*5_",1}, + + {"qualifiedName",Operator.STARTS_WITH,"search_quest?n_",1}, + + {"qualifiedName",Operator.STARTS_WITH,"/warehouse/tablespace/external/hive/qf/hortonia",1}, + {"qualifiedName",Operator.STARTS_WITH,"/warehouse/tablespace/external/hive/qf/",2}, + + }; + } + + + @DataProvider(name = "specialCharSearchEQ") + private Object[][] specialCharSearch() { + return new Object[][]{ + {"name",Operator.EQ,spChar1,1}, + {"name",Operator.EQ,spChar2,1}, + {"name",Operator.EQ,spChar3,1}, + {"name",Operator.EQ,spChar4,1}, + {"name",Operator.EQ,spChar5,1}, + {"name",Operator.EQ,spChar6,1}, + {"name",Operator.EQ,spChar7,1}, + {"name",Operator.EQ,spChar8,1}, + {"name",Operator.EQ,spChar9,1}, + {"name",Operator.EQ,spChar10,1}, + {"name",Operator.EQ,spChar11,1}, + {"name",Operator.EQ,spChar12,1}, + {"name",Operator.EQ,spChar13,1}, + {"name",Operator.EQ,spChar14,1}, + {"name",Operator.EQ,spChar15,1}, + {"name",Operator.EQ,spChar16,1}, + {"name",Operator.EQ,spChar17,1}, + {"name",Operator.EQ,spChar18,1}, + {"name",Operator.EQ,spChar19,1}, + + {"qualifiedName",Operator.EQ,spChar1.replace("name","qf"),1}, + {"qualifiedName",Operator.EQ,spChar2.replace("name","qf"),1}, + {"qualifiedName",Operator.EQ,spChar3.replace("name","qf"),1}, + {"qualifiedName",Operator.EQ,spChar4.replace("name","qf"),1}, + {"qualifiedName",Operator.EQ,spChar5.replace("name","qf"),1}, + {"qualifiedName",Operator.EQ,spChar6.replace("name","qf"),1}, + {"qualifiedName",Operator.EQ,spChar7.replace("name","qf"),1}, + {"qualifiedName",Operator.EQ,spChar8.replace("name","qf"),1}, + {"qualifiedName",Operator.EQ,spChar9.replace("name","qf"),1}, + {"qualifiedName",Operator.EQ,spChar10.replace("name","qf"),1}, + {"qualifiedName",Operator.EQ,spChar11.replace("name","qf"),1}, + {"qualifiedName",Operator.EQ,spChar12.replace("name","qf"),1}, + {"qualifiedName",Operator.EQ,spChar13.replace("name","qf"),1}, + {"qualifiedName",Operator.EQ,spChar14.replace("name","qf"),1}, + {"qualifiedName",Operator.EQ,spChar15.replace("name","qf"),1}, + {"qualifiedName",Operator.EQ,spChar16.replace("name","qf"),1}, + {"qualifiedName",Operator.EQ,spChar17.replace("name","qf"),1}, + {"qualifiedName",Operator.EQ,spChar18.replace("name","qf"),1}, + {"qualifiedName",Operator.EQ,spChar19.replace("name","qf"),1}, + }; + } + + + public void createSpecialCharTestEntities() throws AtlasBaseException { + + List<String> nameList = Arrays.asList(spChar1,spChar2,spChar3,spChar4,spChar5,spChar6,spChar7,spChar8,spChar9,spChar10,spChar11,spChar12,spChar13,spChar14,spChar15,spChar16,spChar17,spChar18,spChar19); + for (String nameStr : nameList) { + AtlasEntity entityToDelete = new AtlasEntity(HIVE_TABLE_TYPE); + entityToDelete.setAttribute("name", nameStr); + entityToDelete.setAttribute(AtlasClient.REFERENCEABLE_ATTRIBUTE_NAME, "qualifiedName"+System.currentTimeMillis()); + + //create entity + EntityMutationResponse response = entityStore.createOrUpdate(new AtlasEntityStream(new AtlasEntity.AtlasEntitiesWithExtInfo(entityToDelete)), false); + } + + List<String> qfList = nameList; + + for (String qfStr : qfList) { + qfStr = qfStr.replace("name","qf"); + AtlasEntity entityToDelete = new AtlasEntity(HIVE_TABLE_TYPE); + entityToDelete.setAttribute("name", "name"+System.currentTimeMillis()); + entityToDelete.setAttribute(AtlasClient.REFERENCEABLE_ATTRIBUTE_NAME, qfStr); + + //create entity + EntityMutationResponse response = entityStore.createOrUpdate(new AtlasEntityStream(new AtlasEntity.AtlasEntitiesWithExtInfo(entityToDelete)), false); + + } + } + + @Test(dataProvider = "specialCharSearchEQ") + public void specialCharSearchAssertEq(String attrName, SearchParameters.Operator operator, String attrValue, int expected) throws AtlasBaseException { + SearchParameters params = new SearchParameters(); + params.setTypeName(HIVE_TABLE_TYPE); + SearchParameters.FilterCriteria filterCriteria = getSingleFilterCondition(attrName,operator, attrValue); + params.setEntityFilters(filterCriteria); + params.setLimit(20); + + AtlasSearchResult searchResult = discoveryService.searchWithParameters(params); + assertSearchResult(searchResult,expected, attrValue); + } + + @Test(dataProvider = "specialCharSearchContains") + public void specialCharSearchAssertContains(String attrName, SearchParameters.Operator operator, String attrValue, int expected) throws AtlasBaseException { + SearchParameters params = new SearchParameters(); + params.setTypeName(HIVE_TABLE_TYPE); + SearchParameters.FilterCriteria filterCriteria = getSingleFilterCondition(attrName,operator, attrValue); + params.setEntityFilters(filterCriteria); + params.setLimit(20); + + AtlasSearchResult searchResult = discoveryService.searchWithParameters(params); + assertSearchResult(searchResult,expected, attrValue); + } + + @Test(dataProvider = "specialCharSearchName") + public void specialCharSearchAssertName(String attrName, SearchParameters.Operator operator, String attrValue, int expected) throws AtlasBaseException { + SearchParameters params = new SearchParameters(); + params.setTypeName(HIVE_TABLE_TYPE); + SearchParameters.FilterCriteria filterCriteria = getSingleFilterCondition(attrName,operator, attrValue); + params.setEntityFilters(filterCriteria); + params.setLimit(20); + + AtlasSearchResult searchResult = discoveryService.searchWithParameters(params); + assertSearchResult(searchResult,expected, attrValue); + } + + @Test(dataProvider = "specialCharSearchQFName") + public void specialCharSearchAssertQFName(String attrName, SearchParameters.Operator operator, String attrValue, int expected) throws AtlasBaseException { + SearchParameters params = new SearchParameters(); + params.setTypeName(HIVE_TABLE_TYPE); + SearchParameters.FilterCriteria filterCriteria = getSingleFilterCondition(attrName,operator, attrValue); + params.setEntityFilters(filterCriteria); + params.setLimit(20); + + AtlasSearchResult searchResult = discoveryService.searchWithParameters(params); + assertSearchResult(searchResult,expected, attrValue); + } + + private void assertSearchResult(AtlasSearchResult searchResult, int expected, String query) { + assertNotNull(searchResult); + if(expected == 0) { + assertTrue(searchResult.getAttributes() == null || CollectionUtils.isEmpty(searchResult.getAttributes().getValues())); + assertNull(searchResult.getEntities(), query); + } else if(searchResult.getEntities() != null) { + assertEquals(searchResult.getEntities().size(), expected, query); + } else { + assertNotNull(searchResult.getAttributes()); + assertNotNull(searchResult.getAttributes().getValues()); + assertEquals(searchResult.getAttributes().getValues().size(), expected, query); + } + } + private void createDimensionalTaggedEntity(String name) throws AtlasBaseException { EntityMutationResponse resp = createDummyEntity(name, HIVE_TABLE_TYPE); AtlasEntityHeader entityHeader = resp.getCreatedEntities().get(0);