From: Mathieu Baudier Date: Wed, 26 Apr 2023 16:40:32 +0000 (+0200) Subject: First ACR search experiments X-Git-Tag: v2.3.14~4 X-Git-Url: https://git.argeo.org/?p=gpl%2Fargeo-jcr.git;a=commitdiff_plain;h=576b198d9ca15d640767ec94d9c695ab353a7c91 First ACR search experiments --- 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 38bcd56..3622399 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 @@ -6,20 +6,28 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Objects; +import java.util.Spliterator; +import java.util.function.Consumer; import java.util.function.Function; +import javax.jcr.Node; +import javax.jcr.NodeIterator; import javax.jcr.Repository; import javax.jcr.RepositoryException; import javax.jcr.Session; +import javax.jcr.query.Query; +import javax.jcr.query.QueryResult; import javax.xml.namespace.NamespaceContext; import org.argeo.api.acr.Content; +import org.argeo.api.acr.search.BasicSearch; import org.argeo.api.acr.spi.ContentProvider; import org.argeo.api.acr.spi.ProvidedContent; 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; @@ -133,4 +141,63 @@ public class JcrContentProvider implements ContentProvider, NamespaceContext { } } + /* + * SEARCH + */ + + @Override + public Spliterator search(ProvidedSession session, BasicSearch search, String relPath) { + try { + Session jcrSession = getJcrSession(session, jcrWorkspace); + JcrBasicSearch jcrBasicSearch = new JcrBasicSearch(jcrSession, search, relPath); + Query query = jcrBasicSearch.createQuery(); + QueryResult queryResult = query.execute(); + return new QueryResultSpliterator(session, queryResult.getNodes()); + } catch (RepositoryException e) { + throw new JcrException(e); + } + } + + class QueryResultSpliterator implements Spliterator { + private ProvidedSession providedSession; + private NodeIterator nodeIterator; + + public QueryResultSpliterator(ProvidedSession providedSession, NodeIterator nodeIterator) { + super(); + this.providedSession = providedSession; + this.nodeIterator = nodeIterator; + } + + @Override + public boolean tryAdvance(Consumer action) { + if (!nodeIterator.hasNext()) + return false; + try { + Node node = nodeIterator.nextNode(); + // TODO optimise by reusing the Node + JcrContent jcrContent = new JcrContent(providedSession, JcrContentProvider.this, jcrWorkspace, + node.getPath()); + action.accept(jcrContent); + return true; + } catch (RepositoryException e) { + throw new JcrException(e); + } + } + + @Override + public Spliterator trySplit() { + return null; + } + + @Override + public long estimateSize() { + return nodeIterator.getSize(); + } + + @Override + public int characteristics() { + return NONNULL | SIZED; + } + + } } 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 new file mode 100644 index 0000000..14bb584 --- /dev/null +++ b/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/search/JcrBasicSearch.java @@ -0,0 +1,101 @@ +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; + } +}