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 <sarath@apache.org>
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 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 @@
sb.append('\\');
}
- if (!quoteValue) {
+ if (shouldQuote && !quoteValue) {
quoteValue = shouldQuoteIndexQueryForChar(c);
}
@@ -987,7 +991,7 @@
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 @@
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.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 @@
}
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 @@
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 @@
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.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 @@
ApplicationProperties.get().setProperty(ApplicationProperties.ENABLE_FREETEXT_SEARCH_CONF, true);
setupTestData();
createDimensionalTaggedEntity("sales");
+ createSpecialCharTestEntities();
}
/* TermSearchProcessor(TSP),
@@ -322,6 +325,345 @@
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_name@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_qf@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);