mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-31 17:39:05 +00:00
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:
@@ -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)
|
||||||
|
@@ -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
|
||||||
|
@@ -42,5 +42,9 @@ public enum JoinType
|
|||||||
/**
|
/**
|
||||||
* Right (outer) join
|
* Right (outer) join
|
||||||
*/
|
*/
|
||||||
RIGHT
|
RIGHT,
|
||||||
|
/**
|
||||||
|
* No Join
|
||||||
|
*/
|
||||||
|
NONE
|
||||||
}
|
}
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
|
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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();
|
||||||
}
|
}
|
||||||
|
@@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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
|
||||||
|
Reference in New Issue
Block a user