Commit 40e2e37d by Madhan Neethiraj

ATLAS-1630: fix incorrect pagination of results in basic search (#3)

parent c572e541
...@@ -37,6 +37,7 @@ import org.apache.atlas.query.SelectExpressionHelper; ...@@ -37,6 +37,7 @@ import org.apache.atlas.query.SelectExpressionHelper;
import org.apache.atlas.repository.Constants; import org.apache.atlas.repository.Constants;
import org.apache.atlas.repository.MetadataRepository; import org.apache.atlas.repository.MetadataRepository;
import org.apache.atlas.repository.graph.AtlasGraphProvider; import org.apache.atlas.repository.graph.AtlasGraphProvider;
import org.apache.atlas.repository.graph.GraphHelper;
import org.apache.atlas.repository.graphdb.AtlasGraph; import org.apache.atlas.repository.graphdb.AtlasGraph;
import org.apache.atlas.repository.graphdb.AtlasIndexQuery; import org.apache.atlas.repository.graphdb.AtlasIndexQuery;
import org.apache.atlas.repository.graphdb.AtlasIndexQuery.Result; import org.apache.atlas.repository.graphdb.AtlasIndexQuery.Result;
...@@ -64,6 +65,7 @@ import java.util.HashMap; ...@@ -64,6 +65,7 @@ import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import static org.apache.atlas.AtlasErrorCode.DISCOVERY_QUERY_FAILED; import static org.apache.atlas.AtlasErrorCode.DISCOVERY_QUERY_FAILED;
import static org.apache.atlas.AtlasErrorCode.UNKNOWN_TYPENAME; import static org.apache.atlas.AtlasErrorCode.UNKNOWN_TYPENAME;
...@@ -166,9 +168,9 @@ public class EntityDiscoveryService implements AtlasDiscoveryService { ...@@ -166,9 +168,9 @@ public class EntityDiscoveryService implements AtlasDiscoveryService {
LOG.debug("Executing basic search query: {} with type: {} and classification: {}", query, typeName, classification); LOG.debug("Executing basic search query: {} with type: {} and classification: {}", query, typeName, classification);
} }
Map<String, Object> bindings = new HashMap<>(); final QueryParams params = validateSearchParams(limit, offset);
QueryParams params = validateSearchParams(limit, offset); Set<String> typeNames = null;
String basicQuery = "g.V()"; Set<String> classificationNames = null;
if (StringUtils.isNotEmpty(typeName)) { if (StringUtils.isNotEmpty(typeName)) {
AtlasEntityType entityType = typeRegistry.getEntityTypeByName(typeName); AtlasEntityType entityType = typeRegistry.getEntityTypeByName(typeName);
...@@ -177,9 +179,7 @@ public class EntityDiscoveryService implements AtlasDiscoveryService { ...@@ -177,9 +179,7 @@ public class EntityDiscoveryService implements AtlasDiscoveryService {
throw new AtlasBaseException(UNKNOWN_TYPENAME, typeName); throw new AtlasBaseException(UNKNOWN_TYPENAME, typeName);
} }
bindings.put("typeNames", entityType.getTypeAndAllSubTypes()); typeNames = entityType.getTypeAndAllSubTypes();
basicQuery += gremlinQueryProvider.getQuery(AtlasGremlinQuery.BASIC_SEARCH_TYPE_FILTER);
ret.setType(typeName); ret.setType(typeName);
} }
...@@ -191,23 +191,76 @@ public class EntityDiscoveryService implements AtlasDiscoveryService { ...@@ -191,23 +191,76 @@ public class EntityDiscoveryService implements AtlasDiscoveryService {
throw new AtlasBaseException(CLASSIFICATION_NOT_FOUND, classification); throw new AtlasBaseException(CLASSIFICATION_NOT_FOUND, classification);
} }
bindings.put("traitNames", classificationType.getTypeAndAllSubTypes()); classificationNames = classificationType.getTypeAndAllSubTypes();
basicQuery += gremlinQueryProvider.getQuery(AtlasGremlinQuery.BASIC_SEARCH_CLASSIFICATION_FILTER);
ret.setClassification(classification); ret.setClassification(classification);
} }
// if query was provided, perform indexQuery and filter for typeName & classification in memory; this approach
// results in a faster and accurate results than using CONTAINS/CONTAINS_PREFIX filter on entityText property
if (StringUtils.isNotEmpty(query)) { if (StringUtils.isNotEmpty(query)) {
bindings.put("queryStr", query); final String idxQuery = String.format("v.\"%s\":(%s)", Constants.ENTITY_TEXT_PROPERTY_KEY, query);
final Iterator<Result<?,?>> qryResult = graph.indexQuery(Constants.FULLTEXT_INDEX, idxQuery).vertices();
final int startIdx = params.offset();
final int resultSize = params.limit();
int resultIdx = 0;
while (qryResult.hasNext()) {
AtlasVertex<?,?> vertex = qryResult.next().getVertex();
String vertexTypeName = GraphHelper.getTypeName(vertex);
// skip non-entity vertices
if (StringUtils.isEmpty(vertexTypeName) || StringUtils.isEmpty(GraphHelper.getGuid(vertex))) {
continue;
}
if (typeNames != null && !typeNames.contains(vertexTypeName)) {
continue;
}
if (classificationNames != null) {
List<String> traitNames = GraphHelper.getTraitNames(vertex);
if (CollectionUtils.isEmpty(traitNames) ||
!CollectionUtils.containsAny(classificationNames, traitNames)) {
continue;
}
}
basicQuery += gremlinQueryProvider.getQuery(AtlasGremlinQuery.BASIC_SEARCH_QUERY_FILTER); resultIdx++;
ret.setQueryText(query); if (resultIdx <= startIdx) {
continue;
} }
bindings.put("offset", params.offset()); AtlasEntityHeader header = entityRetriever.toAtlasEntityHeader(vertex);
bindings.put("limit", params.limit());
ret.addEntity(header);
if (ret.getEntities().size() == resultSize) {
break;
}
}
} else {
final Map<String, Object> bindings = new HashMap<>();
String basicQuery = "g.V()";
if (typeNames != null) {
bindings.put("typeNames", typeNames);
basicQuery += gremlinQueryProvider.getQuery(AtlasGremlinQuery.BASIC_SEARCH_TYPE_FILTER);
}
if (classificationNames != null) {
bindings.put("traitNames", classificationNames);
basicQuery += gremlinQueryProvider.getQuery(AtlasGremlinQuery.BASIC_SEARCH_CLASSIFICATION_FILTER);
}
bindings.put("startIdx", params.offset());
bindings.put("endIdx", params.offset() + params.limit());
basicQuery += gremlinQueryProvider.getQuery(AtlasGremlinQuery.TO_RANGE_LIST); basicQuery += gremlinQueryProvider.getQuery(AtlasGremlinQuery.TO_RANGE_LIST);
...@@ -224,7 +277,6 @@ public class EntityDiscoveryService implements AtlasDiscoveryService { ...@@ -224,7 +277,6 @@ public class EntityDiscoveryService implements AtlasDiscoveryService {
for (Object element : queryResult) { for (Object element : queryResult) {
if (element instanceof AtlasVertex) { if (element instanceof AtlasVertex) {
ret.addEntity(entityRetriever.toAtlasEntityHeader((AtlasVertex) element)); ret.addEntity(entityRetriever.toAtlasEntityHeader((AtlasVertex) element));
} else { } else {
LOG.warn("searchUsingBasicQuery({}): expected an AtlasVertex; found unexpected entry in result {}", basicQuery, element); LOG.warn("searchUsingBasicQuery({}): expected an AtlasVertex; found unexpected entry in result {}", basicQuery, element);
} }
...@@ -236,6 +288,7 @@ public class EntityDiscoveryService implements AtlasDiscoveryService { ...@@ -236,6 +288,7 @@ public class EntityDiscoveryService implements AtlasDiscoveryService {
} finally { } finally {
graph.releaseGremlinScriptEngine(scriptEngine); graph.releaseGremlinScriptEngine(scriptEngine);
} }
}
return ret; return ret;
} }
......
...@@ -65,14 +65,12 @@ public class AtlasGremlin2QueryProvider extends AtlasGremlinQueryProvider { ...@@ -65,14 +65,12 @@ public class AtlasGremlin2QueryProvider extends AtlasGremlinQueryProvider {
"(it.object.'__superTypeNames'.contains('DataSet')) : false)})." + "(it.object.'__superTypeNames'.contains('DataSet')) : false)})." +
"path().toList()"; "path().toList()";
case BASIC_SEARCH_QUERY_FILTER:
return ".has('entityText', com.thinkaurelius.titan.core.attribute.Text.CONTAINS, queryStr)";
case BASIC_SEARCH_TYPE_FILTER: case BASIC_SEARCH_TYPE_FILTER:
return ".has('__typeName', T.in, typeNames)"; return ".has('__typeName', T.in, typeNames)";
case BASIC_SEARCH_CLASSIFICATION_FILTER: case BASIC_SEARCH_CLASSIFICATION_FILTER:
return ".has('__traitNames', T.in, traitNames)"; return ".has('__traitNames', T.in, traitNames)";
case TO_RANGE_LIST: case TO_RANGE_LIST:
return " [offset..<limit].toList()"; return " [startIdx..<endIdx].toList()";
} }
// Should never reach this point // Should never reach this point
return null; return null;
......
...@@ -56,7 +56,6 @@ public abstract class AtlasGremlinQueryProvider { ...@@ -56,7 +56,6 @@ public abstract class AtlasGremlinQueryProvider {
PARTIAL_LINEAGE, PARTIAL_LINEAGE,
// Discovery Queries // Discovery Queries
BASIC_SEARCH_QUERY_FILTER,
BASIC_SEARCH_TYPE_FILTER, BASIC_SEARCH_TYPE_FILTER,
BASIC_SEARCH_CLASSIFICATION_FILTER, BASIC_SEARCH_CLASSIFICATION_FILTER,
TO_RANGE_LIST TO_RANGE_LIST
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment