SEARCH-433 CMIS QL - Support inner and left outer joins from/to a single type and any number of aspects

- Data model and parser changes
This commit is contained in:
ahind
2017-10-02 11:24:23 +01:00
parent 15681330ab
commit cc0a7f8675
8 changed files with 79 additions and 29 deletions

View File

@@ -143,7 +143,7 @@ public class CMISQueryParser
CommonTree queryNode = (CommonTree) parser.query().getTree(); CommonTree queryNode = (CommonTree) parser.query().getTree();
CommonTree sourceNode = (CommonTree) queryNode.getFirstChildWithType(CMISParser.SOURCE); CommonTree sourceNode = (CommonTree) queryNode.getFirstChildWithType(CMISParser.SOURCE);
Source source = buildSource(sourceNode, joinSupport, factory); Source source = buildSource(sourceNode, joinSupport, factory, JoinType.NONE);
Map<String, Selector> selectors = source.getSelectors(); Map<String, Selector> selectors = source.getSelectors();
ArrayList<Column> columns = buildColumns(queryNode, factory, selectors, options.getQuery()); ArrayList<Column> columns = buildColumns(queryNode, factory, selectors, options.getQuery());
@@ -1391,7 +1391,7 @@ public class CMISQueryParser
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private Source buildSource(CommonTree source, CapabilityJoin joinSupport, QueryModelFactory factory) private Source buildSource(CommonTree source, CapabilityJoin joinSupport, QueryModelFactory factory, JoinType lhsJoin)
{ {
if (source.getChildCount() == 1) if (source.getChildCount() == 1)
{ {
@@ -1404,7 +1404,7 @@ public class CMISQueryParser
throw new UnsupportedOperationException("Joins are not supported"); throw new UnsupportedOperationException("Joins are not supported");
} }
CommonTree tableSourceNode = (CommonTree) singleTableNode.getFirstChildWithType(CMISParser.SOURCE); CommonTree tableSourceNode = (CommonTree) singleTableNode.getFirstChildWithType(CMISParser.SOURCE);
return buildSource(tableSourceNode, joinSupport, factory); return buildSource(tableSourceNode, joinSupport, factory, JoinType.NONE);
} }
if (singleTableNode.getType() != CMISParser.TABLE_REF) if (singleTableNode.getType() != CMISParser.TABLE_REF)
@@ -1432,7 +1432,9 @@ public class CMISQueryParser
+ typeDef.getTypeId()); + typeDef.getTypeId());
} }
} }
return factory.createSelector(typeDef.getAlfrescoClass(), alias); Source lhs = factory.createSelector(typeDef.getAlfrescoClass(), alias);
lhs.setJoinType(lhsJoin);
return lhs;
} else } else
{ {
if (joinSupport == CapabilityJoin.NONE) if (joinSupport == CapabilityJoin.NONE)
@@ -1466,15 +1468,20 @@ public class CMISQueryParser
} }
Source lhs = factory.createSelector(typeDef.getAlfrescoClass(), alias); Source lhs = factory.createSelector(typeDef.getAlfrescoClass(), alias);
if(lhsJoin == JoinType.NONE)
{
lhs.setJoinType(JoinType.INNER);
}
else
{
lhs.setJoinType(lhsJoin);
}
List<CommonTree> list = (List<CommonTree>) (source.getChildren()); List<CommonTree> list = (List<CommonTree>) (source.getChildren());
for (CommonTree joinNode : list) for (CommonTree joinNode : list)
{ {
if (joinNode.getType() == CMISParser.JOIN) if (joinNode.getType() == CMISParser.JOIN)
{ {
CommonTree rhsSource = (CommonTree) joinNode.getFirstChildWithType(CMISParser.SOURCE);
Source rhs = buildSource(rhsSource, joinSupport, factory);
JoinType joinType = JoinType.INNER; JoinType joinType = JoinType.INNER;
CommonTree joinTypeNode = (CommonTree) joinNode.getFirstChildWithType(CMISParser.LEFT); CommonTree joinTypeNode = (CommonTree) joinNode.getFirstChildWithType(CMISParser.LEFT);
if (joinTypeNode != null) if (joinTypeNode != null)
@@ -1482,11 +1489,14 @@ public class CMISQueryParser
joinType = JoinType.LEFT; joinType = JoinType.LEFT;
} }
if ((joinType == JoinType.LEFT) && (joinSupport == CapabilityJoin.INNERONLY)) if ((joinType == JoinType.LEFT) && (joinSupport != CapabilityJoin.INNERANDOUTER))
{ {
throw new UnsupportedOperationException("Outer joins are not supported"); throw new UnsupportedOperationException("Outer joins are not supported");
} }
CommonTree rhsSource = (CommonTree) joinNode.getFirstChildWithType(CMISParser.SOURCE);
Source rhs = buildSource(rhsSource, joinSupport, factory, joinType);
Constraint joinCondition = null; Constraint joinCondition = null;
CommonTree joinConditionNode = (CommonTree) joinNode.getFirstChildWithType(CMISParser.ON); CommonTree joinConditionNode = (CommonTree) joinNode.getFirstChildWithType(CMISParser.ON);
if (joinConditionNode != null) if (joinConditionNode != null)

View File

@@ -43,12 +43,6 @@ public interface Join extends Source
*/ */
public Source getRight(); public Source getRight();
/**
* Get the join type
* @return JoinType
*/
public JoinType getJoinType();
/** /**
* Get the join condition. * Get the join condition.
* Not all constraints are valid join conditions * Not all constraints are valid join conditions

View File

@@ -42,5 +42,9 @@ public enum JoinType
/** /**
* Right (outer) join * Right (outer) join
*/ */
RIGHT RIGHT,
/**
* No Join
*/
NONE
} }

View File

@@ -40,4 +40,8 @@ public interface Source
public Selector getSelector(String name); public Selector getSelector(String name);
public List<Set<String>> getSelectorGroups(FunctionEvaluationContext functionContext); public List<Set<String>> getSelectorGroups(FunctionEvaluationContext functionContext);
public JoinType getJoinType();
public void setJoinType(JoinType joinType);
} }

View File

@@ -194,7 +194,7 @@ public class BaseJoin implements Join
} }
} }
if ((getJoinType() == JoinType.INNER) && (lhsSelector != null) && (rhsSelector != null)) if ( ((getJoinType() == JoinType.INNER) || (getJoinType() == JoinType.LEFT)) && (lhsSelector != null) && (rhsSelector != null))
{ {
TOADD: for (Set<String> toAddTo : left) TOADD: for (Set<String> toAddTo : left)
@@ -247,4 +247,10 @@ public class BaseJoin implements Join
return answer; return answer;
} }
@Override
public void setJoinType(JoinType joinType)
{
this.joinType = joinType;
}
} }

View File

@@ -33,6 +33,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext;
import org.alfresco.repo.search.impl.querymodel.JoinType;
import org.alfresco.repo.search.impl.querymodel.Selector; import org.alfresco.repo.search.impl.querymodel.Selector;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
@@ -44,6 +45,8 @@ public class BaseSelector implements Selector
private QName type; private QName type;
private String alias; private String alias;
private JoinType joinType = JoinType.NONE;
public BaseSelector(QName type, String alias) public BaseSelector(QName type, String alias)
{ {
@@ -51,32 +54,41 @@ public class BaseSelector implements Selector
this.alias = alias; this.alias = alias;
} }
/* @Override
* (non-Javadoc)
*
* @see org.alfresco.repo.search.impl.querymodel.Selector#getAlias()
*/
public String getAlias() public String getAlias()
{ {
return alias; return alias;
} }
/*
* (non-Javadoc)
* @Override
* @see org.alfresco.repo.search.impl.querymodel.Selector#getType() public JoinType getJoinType()
*/ {
return joinType;
}
@Override
public void setJoinType(JoinType joinType)
{
this.joinType = joinType;
}
@Override
public QName getType() public QName getType()
{ {
return type; return type;
} }
public String toString() public String toString()
{ {
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
builder.append("BaseSelector["); builder.append("BaseSelector[");
builder.append("alias=").append(getAlias()).append(", "); builder.append("alias=").append(getAlias()).append(", ");
builder.append("type=").append(getType()); builder.append("type=").append(getType()).append(", ");
builder.append("joinType=").append(getJoinType());
builder.append("]"); builder.append("]");
return builder.toString(); return builder.toString();
} }

View File

@@ -31,6 +31,7 @@ import java.util.Set;
import org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor; import org.alfresco.repo.search.adaptor.lucene.LuceneQueryParserAdaptor;
import org.alfresco.repo.search.impl.querymodel.Argument; import org.alfresco.repo.search.impl.querymodel.Argument;
import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext; import org.alfresco.repo.search.impl.querymodel.FunctionEvaluationContext;
import org.alfresco.repo.search.impl.querymodel.JoinType;
import org.alfresco.repo.search.impl.querymodel.impl.BaseSelector; import org.alfresco.repo.search.impl.querymodel.impl.BaseSelector;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
@@ -55,8 +56,18 @@ public class LuceneSelector<Q, S, E extends Throwable> extends BaseSelector impl
*/ */
public Q addComponent(Set<String> selectors, Map<String, Argument> functionArgs, LuceneQueryBuilderContext<Q, S, E> luceneContext, FunctionEvaluationContext functionContext) throws E public Q addComponent(Set<String> selectors, Map<String, Argument> functionArgs, LuceneQueryBuilderContext<Q, S, E> luceneContext, FunctionEvaluationContext functionContext) throws E
{ {
LuceneQueryParserAdaptor<Q, S, E> lqpa = luceneContext.getLuceneQueryParserAdaptor(); LuceneQueryParserAdaptor<Q, S, E> lqpa = luceneContext.getLuceneQueryParserAdaptor();
return lqpa.getFieldQuery("CLASS", getType().toString()); switch(getJoinType())
{
case INNER:
case NONE:
return lqpa.getFieldQuery("CLASS", getType().toString());
case LEFT:
return lqpa.getMatchAllNodesQuery();
case RIGHT:
default:
throw new IllegalArgumentException();
}
} }

View File

@@ -17,6 +17,15 @@ query:
"SELECT cmis:name,, cmis:typeId FROM cmis:document" FAIL "SELECT cmis:name,, cmis:typeId FROM cmis:document" FAIL
"SELECT * FROM *" FAIL "SELECT * FROM *" FAIL
"SELECT * from cmis:document d JOIN cm:titled t ON d.cmis:objectId = t.cmis:objectId" ->
"(QUERY ALL_COLUMNS (SOURCE (TABLE_REF cmis:document d) (JOIN (SOURCE (TABLE_REF cm:titled t)) (ON (COLUMN_REF cmis:objectId d) = (COLUMN_REF cmis:objectId t)))))"
"SELECT * from cmis:document d LEFT OUTER JOIN cm:titled t ON d.cmis:objectId = t.cmis:objectId" ->
"(QUERY ALL_COLUMNS (SOURCE (TABLE_REF cmis:document d) (JOIN (SOURCE (TABLE_REF cm:titled t)) LEFT (ON (COLUMN_REF cmis:objectId d) = (COLUMN_REF cmis:objectId t)))))"
"SELECT * from cmis:document d JOIN cm:titled t ON d.cmis:objectId = t.cmis:objectId LEFT OUTER JOIN cm:ownable o ON d.cmis:objectId = o.cmis:objectId" ->
"(QUERY ALL_COLUMNS (SOURCE (TABLE_REF cmis:document d) (JOIN (SOURCE (TABLE_REF cm:titled t)) (ON (COLUMN_REF cmis:objectId d) = (COLUMN_REF cmis:objectId t))) (JOIN (SOURCE (TABLE_REF cm:ownable o)) LEFT (ON (COLUMN_REF cmis:objectId d) = (COLUMN_REF cmis:objectId o)))))"
"SELECT d.*, t.*, o.* from cmis:document d JOIN cm:titled t ON d.cmis:objectId = t.cmis:objectId LEFT OUTER JOIN cm:ownable o ON d.cmis:objectId = o.cmis:objectId" ->
"(QUERY (COLUMNS (ALL_COLUMNS d) (ALL_COLUMNS t) (ALL_COLUMNS o)) (SOURCE (TABLE_REF cmis:document d) (JOIN (SOURCE (TABLE_REF cm:titled t)) (ON (COLUMN_REF cmis:objectId d) = (COLUMN_REF cmis:objectId t))) (JOIN (SOURCE (TABLE_REF cm:ownable o)) LEFT (ON (COLUMN_REF cmis:objectId d) = (COLUMN_REF cmis:objectId o)))))"
"SELECT * from FOLDER JOIN RELATIONSHIP ON FOLDER.ID = RELATIONSHIP.ID" OK "SELECT * from FOLDER JOIN RELATIONSHIP ON FOLDER.ID = RELATIONSHIP.ID" OK
"SELECT * from FOLDER F JOIN RELATIONSHIP RL ON F.ID = RL.ID" OK "SELECT * from FOLDER F JOIN RELATIONSHIP RL ON F.ID = RL.ID" OK
"SELECT * from DOCUMENT D JOIN DOCUMENT DD ON (D.ID = DD.ID)" FAIL "SELECT * from DOCUMENT D JOIN DOCUMENT DD ON (D.ID = DD.ID)" FAIL