All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.streamsets.systemexplorer.api.ExplorerSearcher Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2017 StreamSets Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.streamsets.systemexplorer.api;

import com.streamsets.pipeline.api.ConfigIssue;
import com.streamsets.pipeline.api.impl.Utils;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * 

* Interface that performs an explorer search against the explorer data produced by the explorer loader. *

*/ public interface ExplorerSearcher { /** *

* Initializes the search. Invoked before every search. *

*/ default List init(final ExplorerRequestContext explorerRequestContext) { return Collections.emptyList(); } /** * Does a search on the given {@code LoaderContext}. * * @param explorerLoaderContext - context that defines where to search for the data. * @param searchRequest - search parameters that define what data to search for. * Refer to the {@code SearchRequest} for details on how the search request parameters can be * used to alter the structure of tree. * @return {@code ExplorerSearcher.Tree} that always has root element with NULL as name and type, its children follow * the structure of the explorer schema of the stage. */ Tree search(ExplorerLoaderContext explorerLoaderContext, SearchRequest searchRequest) throws InterruptedException, ExplorerSearcherException; /** * Destroys the search. Invoked after every search. */ default void destroy() { } /** *

* Searches always return a tree. The tree conforms to the explorer schema defined in the stage. *

*

* Tree instances always start with a ROOT node (name=null, type=null). *

*

* The tree nodes have a type (schema element name), a value, and they may have children. *

*

* For Atom elements children is always NULL. *

*

* For Set elements children are: * * NULL if the search didn't return any children for the current element * * A list with a single NULL element to signal that the search didn't retrieve data at the children level so * it does not know if there is data or not, a search at deeper level must be done to find out. * * A non-empty list of trees with children. *

*/ class Tree { private final Map attributes; private final String type; private Map children; /** *

* Creates an empty tree, just the root node. *

*/ public static Tree root() { return new Tree(); } private Tree() { attributes = null; type = null; } /** *

* Create a tree node. *

*/ public Tree(String name, String type) { this(Collections.singletonMap(ExplorerSchema.Element.NAME_ATTR, name), type); } public Tree(Map attributes, String type) { this.type = Utils.checkNotNull(type, "type"); Utils.checkNotNull(attributes, "attributes"); Utils.checkArgument(attributes.get(ExplorerSchema.Element.NAME_ATTR) != null, "At least the name attribute is required"); this.attributes = Collections.unmodifiableMap(new HashMap<>(attributes)); } /** * Returns the node attributes. * * @return unmodifiable map of node attributes. */ public Map getAttributes() { return attributes; } /** *

* The node value. *

*/ public String getName() { return Optional.ofNullable(attributes).map(a -> a.get(ExplorerSchema.Element.NAME_ATTR)).orElse(null); } /** *

* The schema element name. *

*/ public String getType() { return type; } /** *

* Adds a child tree. *

*/ public Tree addChild(Tree tree) { if (children == null) { children = new LinkedHashMap<>(); } children.put(tree.getName(), tree); return this; } /** *

* Returns a child tree, NULL if it does not exist. *

*/ public Tree getChild(String value) { return (children == null) ? null : children.get(value); } /** *

* Return all the children of a node, NULL if none. *

*/ public Collection getChildren() { return (children == null) ? null : children.values(); } @Override public String toString() { return "Tree{" + "name='" + getName() + '\'' + ", type='" + type + '\'' + ", children=" + children + '}'; } } /** *

* Defines an explorer search request and how to produce the result tree. *

*

* For the following explorer data: *

* {@code * database: HR * schema: US * table: EMPLOYEE * column: ID, NAME, EMAIL, SSN * table: OFFICE * column: ID, ADDRESS, STATE * schema: EU * table: EMPLOYEE * column: ID, NAME, EMAIL * table: OFFICE * column: ID, ADDRESS, COUNTRY * database: PRODUCTS * schema: PROD * table: PRODUCT * column: ID, NAME * table: PRICE * column: PRODUCT_ID, PRICE, CURRENCY * table: STOCK * column: PRODUCT_ID, QUANTITY * database: MARKETING * schema: GLOBAL * table: CAMPAIGN * column: ID, NAME * } * *

* A search that returns the whole data would be: *

* * {@code * * { name : null * value : null * children: [ * { name: HR * type: database * children: [ * { name: US * type: schema * children: [ * { name: EMPLOYEE * type: table * children: [ * { name: ID, type: column } * { name: NAME, type: column } * { name: EMAIL, type: column } * { name: SSN, type: column } * ] * } * { name: OFFICE * type: table * children: [ * { name: ID, type: column } * { name: ADDRESS, type: column } * { name: STATE, type: column } * ] * } * ] * } * { name : EU * type : schema * children: [ * { name: EMPLOYEE * type: table * children: [ * { name: ID, type: column } * { name: NAME, type: column } * { name: EMAIL, type: column } * ] * } * { name: OFFICE * type: table * children: [ * { name: ID, type: column } * { name: ADDRESS, type: column } * { name: COUNTRY, type: column } * ] * } * ] * } * ] * } * { name: PRODUCTS * type: database * children: [ * { name: PROD * type: schema * children: [ * { name: PRODUCT * type: table * children: [ * { name: ID, type: column } * { name: NAME, type: column } * ] * } * { name: PRICE * type: table * children: [ * { name: PRODUCT_ID, type: column } * { name: PRICE, type: column } * { name: CURRENCY, type: column } * ] * } * { name: STOCK * type: table * children: [ * { name: PRODUCT_ID, type: column } * { name: QUANTITY, type: column } * ] * } * ] * } * ] * } * { name: MARKETING * type: database * children: [ * { name: GLOBAL * type: schema * children: [ * { name: CAMPAIGN * type: table * children: [ * { name: ID, type: column } * { name: NAME, type: column } * ] * } * ] * } * ] * } * [ * } * } * */ interface SearchRequest { /** *

* The schema element being search. *

*/ String getSchemaElement(); /** *

* Schema elements and values pairs that restrict the search domain to a subset of the explorer data. *

*/ Map getFixedSchemaElements(); /** *

* RSQL expression (using schema elements as RSQL selectors) to search within the search domain. *

*/ String getSearch(); /** *

* How many extra levels from the schema element being search are wanted in the result tree. *

*/ int getExtraDepth(); /** *

* It will truncate the result tree, from root down to the schema element being searched. The returned tree * will have as root children the searched schema element nodes. *

*/ boolean isUpTruncate(); /** *

* The offset of the results. *

*/ int getOffset(); /** *

* The length of the results. *

*/ int getLen(); } final class NoOp implements ExplorerSearcher { public static final NoOp NOOP = new NoOp(); private NoOp() { } @Override public Tree search( final ExplorerLoaderContext explorerLoaderContext, final SearchRequest searchRequest ) { return Tree.root(); } } class ExplorerSearcherException extends Exception { public ExplorerSearcherException() { } public ExplorerSearcherException(final String message) { super(message); } public ExplorerSearcherException(final String message, final Throwable cause) { super(message, cause); } public ExplorerSearcherException(final Throwable cause) { super(cause); } public ExplorerSearcherException(final String message, final Throwable cause, final boolean enableSuppression, final boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } } }