Kerem Sahin f929190e6a metadata-models 50.0.6 -> 54.0.1:
54.0.0: Filter removed documents during browse
   53.0.15: Throwing 404, when no aspects found in DB for a given entity
   53.0.14: add node label when updating relationship
    53.0.8: Handle * character in the directory path for browse
    53.0.4: apply label for add/update graph node&edge
    53.0.0: META-10395: Don't package KafkaAuditHeader and UUID classes in mxe-avro
    52.0.7: Add API in search DAO to support query filters
    52.0.5: META-10073: Refactor remote DAO to use the new Ingest action method
   51.0.16: allow query dao use default order by from neo4j
   51.0.15: enable dataset indexing in graph
   51.0.12: Move EMPTY_FILTER to RestliConstants
   51.0.11: Add KafkaEventProducerFactory to utils
   51.0.10: Create in-memory Neo4j in Neo4jDriverFactory if integration config is set
    51.0.9: Generalized add() in BaseLocalDAO and EbeanLocalDAO
    51.0.4: Move Neo4jTestServerBuilder to metadata test utils
    51.0.3: Move makeRelationshipFilter to neo4j utils
    50.0.7: Implement Neo4jDriverFactory

MP_VERSION=metadata-models:54.0.1
MP_VERSION=wherehows-samza:1.0.56
2019-12-13 11:46:49 -08:00

136 lines
6.0 KiB
Java

package com.linkedin.metadata.restli;
import com.linkedin.common.UrnArray;
import com.linkedin.common.urn.Urn;
import com.linkedin.data.template.RecordTemplate;
import com.linkedin.data.template.UnionTemplate;
import com.linkedin.metadata.dao.BaseSearchDAO;
import com.linkedin.metadata.dao.SearchResult;
import com.linkedin.metadata.dao.utils.ModelUtils;
import com.linkedin.metadata.query.AutoCompleteResult;
import com.linkedin.metadata.query.Filter;
import com.linkedin.metadata.query.SearchResultMetadata;
import com.linkedin.metadata.query.SortCriterion;
import com.linkedin.metadata.query.SortOrder;
import com.linkedin.parseq.Task;
import com.linkedin.restli.server.CollectionResult;
import com.linkedin.restli.server.PagingContext;
import com.linkedin.restli.server.annotations.Action;
import com.linkedin.restli.server.annotations.ActionParam;
import com.linkedin.restli.server.annotations.Finder;
import com.linkedin.restli.server.annotations.Optional;
import com.linkedin.restli.server.annotations.PagingContextParam;
import com.linkedin.restli.server.annotations.QueryParam;
import com.linkedin.restli.server.annotations.RestMethod;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import static com.linkedin.metadata.restli.RestliConstants.*;
/**
* A base class for the entity rest.li resource that supports CRUD + search methods
*
* See http://go/gma for more details
*
* @param <KEY> the resource's key type
* @param <VALUE> the resource's value type
* @param <URN> must be a valid {@link Urn} type for the snapshot
* @param <SNAPSHOT> must be a valid snapshot type defined in com.linkedin.metadata.snapshot
* @param <ASPECT_UNION> must be a valid aspect union type supported by the snapshot
* @param <DOCUMENT> must be a valid search document type defined in com.linkedin.metadata.search
*/
public abstract class BaseSearchableEntityResource<
// @formatter:off
KEY extends RecordTemplate,
VALUE extends RecordTemplate,
URN extends Urn,
SNAPSHOT extends RecordTemplate,
ASPECT_UNION extends UnionTemplate,
DOCUMENT extends RecordTemplate>
// @formatter:on
extends BaseEntityResource<KEY, VALUE, URN, SNAPSHOT, ASPECT_UNION> {
private static final String DEFAULT_SORT_CRITERION_FIELD = "urn";
public BaseSearchableEntityResource(@Nonnull Class<SNAPSHOT> snapshotClass,
@Nonnull Class<ASPECT_UNION> aspectUnionClass) {
super(snapshotClass, aspectUnionClass);
}
/**
* Returns a document-specific {@link BaseSearchDAO}.
*/
@Nonnull
protected abstract BaseSearchDAO<DOCUMENT> getSearchDAO();
/**
* Returns all {@link VALUE} objects from DB which are also available in the search index for the corresponding entity.
* By default the list is sorted in ascending order of urn
*
* @param pagingContext pagination context
* @param aspectNames list of aspect names that need to be returned
* @param filter {@link Filter} to filter the search results
* @param sortCriterion {@link SortCriterion} to sort the search results
* @return list of all {@link VALUE} objects obtained from DB
*/
@RestMethod.GetAll
@Nonnull
public Task<List<VALUE>> getAll(@Nonnull PagingContext pagingContext,
@QueryParam(PARAM_ASPECTS) @Optional("[]") @Nonnull String[] aspectNames,
@QueryParam(PARAM_FILTER) @Optional @Nullable Filter filter,
@QueryParam(PARAM_SORT) @Optional @Nullable SortCriterion sortCriterion) {
final Filter searchFilter = filter != null ? filter : EMPTY_FILTER;
final SortCriterion searchSortCriterion = sortCriterion != null ? sortCriterion
: new SortCriterion().setField(DEFAULT_SORT_CRITERION_FIELD).setOrder(SortOrder.ASCENDING);
final SearchResult<DOCUMENT> filterResult =
getSearchDAO().filter(searchFilter, searchSortCriterion, pagingContext.getStart(), pagingContext.getCount());
return RestliUtils.toTask(
() -> getSearchQueryCollectionResult(filterResult, aspectNames).getElements());
}
@Finder(FINDER_SEARCH)
@Nonnull
public Task<CollectionResult<VALUE, SearchResultMetadata>> search(@QueryParam(PARAM_INPUT) @Nonnull String input,
@QueryParam(PARAM_ASPECTS) @Optional("[]") @Nonnull String[] aspectNames,
@QueryParam(PARAM_FILTER) @Optional @Nullable Filter filter,
@QueryParam(PARAM_SORT) @Optional @Nullable SortCriterion sortCriterion,
@PagingContextParam @Nonnull PagingContext pagingContext) {
final Filter searchFilter = filter != null ? filter : EMPTY_FILTER;
final SearchResult<DOCUMENT> searchResult =
getSearchDAO().search(input, searchFilter, sortCriterion, pagingContext.getStart(), pagingContext.getCount());
return RestliUtils.toTask(
() -> getSearchQueryCollectionResult(searchResult, aspectNames));
}
@Action(name = ACTION_AUTOCOMPLETE)
@Nonnull
public Task<AutoCompleteResult> autocomplete(@ActionParam(PARAM_QUERY) @Nonnull String query,
@ActionParam(PARAM_FIELD) @Nullable String field, @ActionParam(PARAM_FILTER) @Nullable Filter filter,
@ActionParam(PARAM_LIMIT) int limit) {
return RestliUtils.toTask(() -> getSearchDAO().autoComplete(query, field, filter, limit));
}
@Nonnull
private CollectionResult<VALUE, SearchResultMetadata> getSearchQueryCollectionResult(@Nonnull SearchResult<DOCUMENT> searchResult,
@Nonnull String[] aspectNames) {
final List<URN> matchedUrns = searchResult.getDocumentList()
.stream()
.map(d -> (URN) ModelUtils.getUrnFromDocument(d))
.collect(Collectors.toList());
final Map<URN, VALUE> urnValueMap = getInternalNonEmpty(matchedUrns, parseAspectsParam(aspectNames));
final List<URN> existingUrns = matchedUrns.stream().filter(urn -> urnValueMap.containsKey(urn)).collect(Collectors.toList());
return new CollectionResult<>(
existingUrns.stream().map(urn -> urnValueMap.get(urn)).collect(Collectors.toList()),
searchResult.getTotalCount(),
searchResult.getSearchResultMetadata().setUrns(new UrnArray(existingUrns.stream().map(urn -> (Urn) urn).collect(Collectors.toList())))
);
}
}