/*
Copyright [2013-2014] eBay Software Foundation
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.
*/
/*
Copyright 2012 eBay Software Foundation
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.ebay.cloud.cms.query.executor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import com.ebay.cloud.cms.dal.entity.JsonEntity;
import com.ebay.cloud.cms.dal.persistence.PersistenceContext;
import com.ebay.cloud.cms.dal.search.ISearchService;
import com.ebay.cloud.cms.dal.search.ISearchStrategy;
import com.ebay.cloud.cms.dal.search.SearchCriteria;
import com.ebay.cloud.cms.dal.search.SearchOption;
import com.ebay.cloud.cms.dal.search.SearchResult;
import com.ebay.cloud.cms.dal.search.impl.criteria.FieldSearchCriteria;
import com.ebay.cloud.cms.dal.search.impl.criteria.FieldSearchCriteria.FieldOperatorEnum;
import com.ebay.cloud.cms.dal.search.impl.query.EmbedSearchQuery;
import com.ebay.cloud.cms.dal.search.impl.query.IEmbedQuery;
import com.ebay.cloud.cms.metadata.model.InternalFieldEnum;
import com.ebay.cloud.cms.metadata.model.InternalFieldFactory;
import com.ebay.cloud.cms.metadata.model.InternalFieldFactory.StatusEnum;
import com.ebay.cloud.cms.metadata.model.MetaRelationship;
import com.ebay.cloud.cms.query.executor.AbstractAction.ActionResult;
import com.ebay.cloud.cms.query.executor.AbstractAction.QueryActionTypeEnum;
import com.ebay.cloud.cms.query.executor.AbstractAction.QueryCostEnum;
import com.ebay.cloud.cms.query.executor.QueryExecutor.ExecuteContext;
import com.ebay.cloud.cms.query.parser.ParseQueryNode;
import com.ebay.cloud.cms.query.service.QueryContext;
/**
*
* @author xjiang
*
*/
public class EmbedActionContainer {
private final QueryContext queryContext;
private final Set<AbstractAction> containedActions;
private int embedSearchCost;
private IEmbedQuery rootEmbedQuery;
private SearchAction rootSearchAction;
private SearchCriteria parentJoinCriteria;
private boolean embedDone;
public EmbedActionContainer(QueryContext context, SearchAction searchAction) {
queryContext = context;
embedSearchCost = QueryCostEnum.FullScan.getValue();
containedActions = new HashSet<AbstractAction>();
embedDone = false;
rootSearchAction = searchAction;
}
public SearchAction getRootSearchAction() {
return rootSearchAction;
}
private void createJoinCriteria() {
AbstractAction parentAction = rootSearchAction.getParentAction();
if (parentAction != null) {
ParseQueryNode parseNode = rootSearchAction.getParseNode();
MetaRelationship metaRef = parseNode.getMetaReference();
parentJoinCriteria = parentAction.getTailJoinCriteria(metaRef);
}
rootEmbedQuery = rootSearchAction.getHeadJoinQuery(null, null, new LinkedList<MetaRelationship>(), false);
}
public boolean isExecutable(ExecuteContext context) {
// query hint: hint action must be executed at the first
if (queryContext.hasHint() && !context.hintExecuted) {
for (AbstractAction action : containedActions) {
if (action instanceof SearchAction) {
int hint = ((SearchAction)action).getParseNode().getQuerySequence();
if (hint == queryContext.getHint()) {
context.hintExecuted = true;
break;
}
}
}
return context.hintExecuted;
}
if (rootSearchAction.getChildrenActions().get(0).isChildrenCriteriaReady()
|| embedSearchCost <= rootSearchAction.getSubTreeCost()
|| parentJoinCriteria != null) {
return true;
}
return false;
}
public ISearchStrategy getQueryStrategy() {
return queryContext.getRegistration().searchStrategy;
}
private void execEmbedQuery(ExecuteContext context) {
if (parentJoinCriteria == EmptySearchCriteria.EMPTY_CRITERIA) {
return;
}
// rewrite the query with addtional criteria
rewriteQuery();
// execute embed query
executeEmbedQuery(context);
// split SearchResult to internal SearchAction
splitEmbedResult(rootSearchAction, context);
context.searchExecSeq++;
}
private void rewriteQuery() {
// append internal status criteria
SearchCriteria statusCriteria = new FieldSearchCriteria(
InternalFieldFactory.getInternalMetaField(InternalFieldEnum.STATUS),
getQueryStrategy(),
FieldOperatorEnum.EQ,
StatusEnum.ACTIVE.toString());
rootEmbedQuery.appendSearchCriteria(statusCriteria);
// append parent criteria
rootEmbedQuery.appendSearchCriteria(parentJoinCriteria);
// append cursor criteria
SearchCursor rootCursor = rootSearchAction.getParseNode().getCursor();
SearchOption option = rootSearchAction.getSearchOption();
SearchCriteria criteria = rootCursor.getCursorCriteria(option, rootSearchAction.getParseNode(), queryContext);
rootEmbedQuery.appendSearchCriteria(criteria);
}
private void executeEmbedQuery(ExecuteContext context) {
ISearchService searchService = queryContext.getSearchService();
PersistenceContext persistContext = queryContext.getPersistenceContext(rootEmbedQuery.getMetadata());
SearchOption searchOption = rootSearchAction.getSearchOption();
SearchResult searchResult = searchService.search(rootEmbedQuery, searchOption, persistContext);
// update pagination setting on root
rootSearchAction.setHasMore(SearchAction.checkHasMore(searchOption, queryContext.isSubQuery(), searchResult));
SearchCursor cursor = rootSearchAction.getParseNode().getCursor();
JsonEntity cursorValue = SearchAction.createNextCursorValue(searchOption, searchResult);
int limit = cursor.hasLimit() ? cursor.getLimit() : searchOption.getDisplayLimit();
SearchCursor nextCursor = new SearchCursor(cursor.getSkip() + limit, limit, cursorValue);
rootSearchAction.setNextCursor(nextCursor);
// add explanation
if (queryContext.needExplain()) {
context.explanations.addAll(searchResult.getQueryExplanations());
}
}
private void splitEmbedResult(AbstractAction action, ExecuteContext context) {
// set search result on action
if (action instanceof SearchAction) {
SearchAction searchAction = (SearchAction)action;
EmbedSearchQuery embedQuery = searchAction.getEmbedQuery();
SearchResult searchResult = embedQuery.getSearchResult();
ParseQueryNode parseNode = searchAction.getParseNode();
ActionResult actionResult = new ActionResult(searchResult, parseNode);
searchAction.getActionResults().add(actionResult);
searchAction.state.selfDone = true;
((SearchAction)action).setExecSeq(context.searchExecSeq);
}
// traversal embed children actions
for (AbstractAction childAction : action.childrenActions) {
if (containedActions.contains(childAction)) {
splitEmbedResult(childAction, context);
}
}
}
public void addContainedActions(Set<AbstractAction> actionSet) {
containedActions.addAll(actionSet);
}
public Set<AbstractAction> getContainedActions() {
return containedActions;
}
public void optimize() {
// recursive traversal embed actions
embedSearchCost = optimizeAction(rootSearchAction);
}
private int optimizeAction(AbstractAction action) {
// traversal embed children actions
int childCost = QueryCostEnum.FullScan.getValue();
List<Integer> childrenCosts = new ArrayList<Integer>(2);
for (AbstractAction childAction : action.getChildrenActions()) {
if (containedActions.contains(childAction)) {
childCost = optimizeAction(childAction);
childrenCosts.add(childCost);
}
}
// post-order : calculate the cost
int embedTreeCost = QueryCostEnum.FullScan.getValue();
QueryActionTypeEnum actionType = action.getType();
switch (actionType) {
case SEARCH:
int searchCost = ((SearchAction)action).getSearchCost();
embedTreeCost = searchCost < childCost ? searchCost : childCost;
break;
case INNERJOIN:
embedTreeCost = childrenCosts.get(0);
break;
case INTERSECTION:
embedTreeCost = Collections.min(childrenCosts);
break;
case UNION:
embedTreeCost = Collections.max(childrenCosts);
break;
default :
break;
}
return embedTreeCost;
}
public boolean doAction(ExecuteContext context, boolean postOrderVisit) {
if (embedDone) {
return true;
}
createJoinCriteria();
if (isExecutable(context)) {
execEmbedQuery(context);
embedDone = true;
return true;
}
return false;
}
}