mirror of
https://github.com/datahub-project/datahub.git
synced 2025-10-18 04:16:28 +00:00

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
136 lines
6.0 KiB
Java
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())))
|
|
);
|
|
}
|
|
}
|