From: Mathieu Baudier Date: Fri, 28 Apr 2023 03:38:06 +0000 (+0200) Subject: Improve JCR ACR search X-Git-Tag: v2.3.14~3 X-Git-Url: https://git.argeo.org/?p=gpl%2Fargeo-jcr.git;a=commitdiff_plain;h=bec133708e5694bc602c571a4d5ceddb7f43160a Improve JCR ACR search --- diff --git a/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/BasicSearchToQom.java b/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/BasicSearchToQom.java new file mode 100644 index 0000000..99b8392 --- /dev/null +++ b/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/BasicSearchToQom.java @@ -0,0 +1,112 @@ +package org.argeo.cms.jcr.acr; + +import java.util.ArrayList; +import java.util.List; + +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.query.QueryManager; +import javax.jcr.query.qom.DynamicOperand; +import javax.jcr.query.qom.QueryObjectModel; +import javax.jcr.query.qom.QueryObjectModelConstants; +import javax.jcr.query.qom.QueryObjectModelFactory; +import javax.jcr.query.qom.Selector; +import javax.jcr.query.qom.StaticOperand; +import javax.xml.namespace.QName; + +import org.apache.jackrabbit.commons.query.sql2.QOMFormatter; +import org.argeo.api.acr.NamespaceUtils; +import org.argeo.api.acr.search.BasicSearch; +import org.argeo.api.acr.search.Constraint; +import org.argeo.api.acr.search.ContentFilter; +import org.argeo.api.acr.search.ContentFilter.Eq; +import org.argeo.api.acr.search.ContentFilter.IsContentClass; +import org.argeo.api.acr.search.ContentFilter.Not; +import org.argeo.api.cms.CmsLog; + +/** Convert an ACR basic search to a JCR query. */ +class BasicSearchToQom { + private final static CmsLog log = CmsLog.getLog(BasicSearchToQom.class); + + private Session session; + private QueryManager queryManager; + private BasicSearch basicSearch; + QueryObjectModelFactory factory; + + QName contentClass = null; + + String selectorName = "content"; + + public BasicSearchToQom(Session session, BasicSearch basicSearch, String relPath) throws RepositoryException { + this.session = session; + this.queryManager = session.getWorkspace().getQueryManager(); + this.basicSearch = basicSearch; + factory = queryManager.getQOMFactory(); + } + + public QueryObjectModel createQuery() throws RepositoryException { + ContentFilter where = basicSearch.getWhere(); + // scan for content classes + // TODO deal with complex cases of multiple types + + javax.jcr.query.qom.Constraint qomConstraint = toQomConstraint(where); + if (contentClass == null) + throw new IllegalArgumentException("No content class specified"); + + Selector source = factory.selector(NamespaceUtils.toPrefixedName(contentClass), selectorName); + + QueryObjectModel qom = factory.createQuery(source, qomConstraint, null, null); + if (log.isDebugEnabled()) { + String sql2 = QOMFormatter.format(qom); + log.debug("JCR query:\n" + sql2 + "\n"); + } + return qom; + } + + private javax.jcr.query.qom.Constraint toQomConstraint(Constraint constraint) throws RepositoryException { + javax.jcr.query.qom.Constraint qomConstraint; + if (constraint instanceof ContentFilter where) { + List constraints = new ArrayList<>(); + for (Constraint c : where.getConstraints()) { + if (c instanceof IsContentClass icc) { + if (icc.getContentClasses().length > 1 || contentClass != null) + throw new IllegalArgumentException("Multiple content class is not supported"); + contentClass = icc.getContentClasses()[0]; + } else { + constraints.add(c); + } + } + + if (constraints.isEmpty()) { + qomConstraint = null; + } else if (constraints.size() == 1) { + qomConstraint = toQomConstraint(constraints.get(0)); + } else { + javax.jcr.query.qom.Constraint currQomConstraint = toQomConstraint(constraints.get(0)); + for (int i = 1; i < constraints.size(); i++) { + Constraint c = constraints.get(i); + if (where.isUnion()) { + currQomConstraint = factory.or(currQomConstraint, toQomConstraint(c)); + } else { + currQomConstraint = factory.and(currQomConstraint, toQomConstraint(c)); + } + } + qomConstraint = currQomConstraint; + } + + } else if (constraint instanceof Eq comp) { + DynamicOperand dynamicOperand = factory.propertyValue(selectorName, + NamespaceUtils.toPrefixedName(comp.getProp())); + // TODO better convert attribute value + StaticOperand staticOperand = factory + .literal(session.getValueFactory().createValue(comp.getValue().toString())); + qomConstraint = factory.comparison(dynamicOperand, QueryObjectModelConstants.JCR_OPERATOR_EQUAL_TO, + staticOperand); + } else if (constraint instanceof Not not) { + qomConstraint = factory.not(toQomConstraint(not.getNegated())); + } else { + throw new IllegalArgumentException("Constraint " + constraint.getClass() + " is not supported"); + } + return qomConstraint; + } +} diff --git a/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContentProvider.java b/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContentProvider.java index 3622399..bc4aa5b 100644 --- a/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContentProvider.java +++ b/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContentProvider.java @@ -27,7 +27,6 @@ import org.argeo.api.acr.spi.ProvidedSession; import org.argeo.api.cms.CmsConstants; import org.argeo.cms.acr.ContentUtils; import org.argeo.cms.jcr.CmsJcrUtils; -import org.argeo.cms.jcr.acr.search.JcrBasicSearch; import org.argeo.jcr.JcrException; import org.argeo.jcr.JcrUtils; @@ -149,7 +148,7 @@ public class JcrContentProvider implements ContentProvider, NamespaceContext { public Spliterator search(ProvidedSession session, BasicSearch search, String relPath) { try { Session jcrSession = getJcrSession(session, jcrWorkspace); - JcrBasicSearch jcrBasicSearch = new JcrBasicSearch(jcrSession, search, relPath); + BasicSearchToQom jcrBasicSearch = new BasicSearchToQom(jcrSession, search, relPath); Query query = jcrBasicSearch.createQuery(); QueryResult queryResult = query.execute(); return new QueryResultSpliterator(session, queryResult.getNodes()); diff --git a/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/search/JcrBasicSearch.java b/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/search/JcrBasicSearch.java deleted file mode 100644 index 14bb584..0000000 --- a/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/search/JcrBasicSearch.java +++ /dev/null @@ -1,101 +0,0 @@ -package org.argeo.cms.jcr.acr.search; - -import java.util.ArrayList; -import java.util.List; - -import javax.jcr.RepositoryException; -import javax.jcr.Session; -import javax.jcr.query.Query; -import javax.jcr.query.QueryManager; -import javax.jcr.query.qom.DynamicOperand; -import javax.jcr.query.qom.QueryObjectModelConstants; -import javax.jcr.query.qom.QueryObjectModelFactory; -import javax.jcr.query.qom.Selector; -import javax.jcr.query.qom.StaticOperand; -import javax.xml.namespace.QName; - -import org.argeo.api.acr.NamespaceUtils; -import org.argeo.api.acr.search.BasicSearch; -import org.argeo.api.acr.search.Constraint; -import org.argeo.api.acr.search.ContentFilter; -import org.argeo.api.acr.search.ContentFilter.Eq; -import org.argeo.api.acr.search.ContentFilter.IsContentClass; -import org.argeo.api.acr.search.ContentFilter.Not; - -public class JcrBasicSearch { - private Session session; - private QueryManager queryManager; - private BasicSearch basicSearch; - QueryObjectModelFactory factory; - - QName contentClass = null; - - String selectorName = "content"; - - public JcrBasicSearch(Session session, BasicSearch basicSearch, String relPath) throws RepositoryException { - this.queryManager = session.getWorkspace().getQueryManager(); - this.basicSearch = basicSearch; - factory = queryManager.getQOMFactory(); - } - - public Query createQuery() throws RepositoryException { - ContentFilter where = basicSearch.getWhere(); - // scan for content classes - // TODO deal with complex cases of multiple types - - javax.jcr.query.qom.Constraint qomConstraint = toQomConstraint(where); - if (contentClass == null) - throw new IllegalArgumentException("No content class specified"); - - Selector source = factory.selector(NamespaceUtils.toPrefixedName(contentClass), selectorName); - - return factory.createQuery(source, qomConstraint, null, null); - } - - private javax.jcr.query.qom.Constraint toQomConstraint(Constraint constraint) throws RepositoryException { - javax.jcr.query.qom.Constraint qomConstraint; - if (constraint instanceof ContentFilter where) { - List constraints = new ArrayList<>(); - for (Constraint c : where.getConstraints()) { - if (c instanceof IsContentClass icc) { - if (icc.getContentClasses().length > 1 || contentClass != null) - throw new IllegalArgumentException("Multiple content class is not supported"); - contentClass = icc.getContentClasses()[0]; - } else { - constraints.add(c); - } - } - - if (constraints.isEmpty()) { - qomConstraint = null; - } else if (constraints.size() == 1) { - qomConstraint = toQomConstraint(constraints.get(0)); - } else { - javax.jcr.query.qom.Constraint currQomConstraint = toQomConstraint(constraints.get(0)); - for (int i = 1; i < constraints.size(); i++) { - Constraint c = constraints.get(i); - if (where.isUnion()) { - currQomConstraint = factory.or(currQomConstraint, toQomConstraint(c)); - } else { - currQomConstraint = factory.and(currQomConstraint, toQomConstraint(c)); - } - } - qomConstraint = currQomConstraint; - } - - } else if (constraint instanceof Eq comp) { - DynamicOperand dynamicOperand = factory.propertyValue(selectorName, - NamespaceUtils.toPrefixedName(comp.getProp())); - // TODO better convert attribute value - StaticOperand staticOperand = factory - .literal(session.getValueFactory().createValue(comp.getValue().toString())); - qomConstraint = factory.comparison(dynamicOperand, QueryObjectModelConstants.JCR_OPERATOR_EQUAL_TO, - staticOperand); - } else if (constraint instanceof Not not) { - qomConstraint = factory.not(toQomConstraint(not.getNegated())); - } else { - throw new IllegalArgumentException("Constraint " + constraint.getClass() + " is not supported"); - } - return qomConstraint; - } -}