Uploaded image for project: 'ModeShape'
  1. ModeShape
  2. MODE-2307

Index and Joins not working as expected / Index and subselects not working

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Done
    • Icon: Blocker Blocker
    • 4.0.0.Beta2
    • 4.0.0.Beta1
    • Query
    • None

      While trying to understand how indexes are working I found one new bug and one behaviour I would also consider a bug.

      Please take a look at the attached testcase, I commented the failing statements.

      Behaviour bug is that IMHO the following two queries should both use both defined indexes but only the subselect query is using both because of a different access order. The subselect query - and this is the real bug - doesn't return any rows. If I don't use indexes the subselect query is working.

      Indexes have been defined on B.sysName and A.referenceId.

      Query 1 using subselect: both indexes used but no results

      SELECT A.* FROM [nt:typeWithReference] AS A
      WHERE A.referenceId  IN (
      
      SELECT B.[jcr:uuid] FROM [nt:typeWithSysName] AS B 
      WHERE B.sysName = $sysName 
      )
      

      Query 2 using join: only one index is used, correct result but slow because the first access goes to A which is very slow without index. Imho the query optimizer should rewrite the query to first access B

      SELECT A.* FROM [nt:typeWithReference] AS A
      JOIN [nt:typeWithSysName] AS B ON A.referenceId  = B.[jcr:uuid] 
      WHERE B.sysName = $sysName 
      

      Testcase

      /*
       * To change this license header, choose License Headers in Project Properties.
       * To change this template file, choose Tools | Templates
       * and open the template in the editor.
       */
      package org.erratic.rm.test;
      
      import java.util.regex.Matcher;
      import java.util.regex.Pattern;
      import javax.enterprise.context.RequestScoped;
      import javax.inject.Inject;
      import javax.jcr.Node;
      import javax.jcr.PropertyType;
      import javax.jcr.RepositoryException;
      import javax.jcr.Session;
      import javax.jcr.nodetype.NodeTypeManager;
      import javax.jcr.nodetype.NodeTypeTemplate;
      import javax.jcr.query.Query;
      import static org.apache.deltaspike.core.api.projectstage.ProjectStage.UnitTest;
      import org.apache.deltaspike.testcontrol.api.TestControl;
      import org.apache.deltaspike.testcontrol.api.junit.CdiTestRunner;
      import org.junit.Assert;
      import org.junit.FixMethodOrder;
      import org.junit.Test;
      import org.junit.runner.RunWith;
      import org.junit.runners.MethodSorters;
      import org.modeshape.jcr.api.index.IndexColumnDefinition;
      import org.modeshape.jcr.api.index.IndexDefinition;
      import org.modeshape.jcr.api.index.IndexDefinitionTemplate;
      import org.modeshape.jcr.api.index.IndexManager;
      import javax.jcr.query.QueryResult;
      import org.erratic.cr.commons.CrConstants;
      import org.slf4j.Logger;
      
      /**
       *
       * @author bschmidt
       */
      @RunWith(CdiTestRunner.class)
      @TestControl(startScopes = RequestScoped.class, projectStage = UnitTest.class)
      @FixMethodOrder(MethodSorters.NAME_ASCENDING)
      //@Ignore
      public class WmModeshapeIndex4Test {
      
      	
      	@Inject
      	private Session session;
      
      	@Inject
      	protected transient Logger logger;
      
      	@Test
      	public void test001CheckSomethingWithIndexes() throws RepositoryException, InterruptedException {
      		
      		regNodeType(session,"nt:typeWithReference");
      		ensureIndex(indexManager(), "referenceId", IndexDefinition.IndexKind.VALUE, "local", "nt:typeWithReference", "", null, "referenceId", PropertyType.STRING);
      		
      		regNodeType(session,"nt:typeWithSysName");
      		ensureIndex(indexManager(), "sysName", IndexDefinition.IndexKind.VALUE, "local", "nt:typeWithSysName", "", null, "sysName", PropertyType.STRING);
      		
      		Node newNode1 = session.getRootNode().addNode("nodeWithSysName","nt:typeWithSysName");
      		newNode1.setProperty("sysName", "X");
      		newNode1.addMixin("mix:referenceable");
      		
      		Node newNode2 = session.getRootNode().addNode("nodeWithReference","nt:typeWithReference");
      		newNode2.setProperty("referenceId", newNode1.getIdentifier());
      		
      		session.save();
      		
      		Thread.sleep(500);
      
      		
      		Query q = session.getWorkspace().getQueryManager().createQuery(
      		"SELECT A.* FROM [nt:typeWithReference] AS A\n" +
      		"WHERE A.referenceId  IN (\n" +
      		"\n" +
      		"SELECT B.[jcr:uuid] FROM [nt:typeWithSysName] AS B \n" +
      		"WHERE B.sysName = $sysName \n" +
      		")",Query.JCR_SQL2);
      		q.bindValue("sysName", session.getValueFactory().createValue("X"));
      		
      		logger.info(q.getStatement());
      		
      		QueryResult result = q.execute();
      		
      		String plan = ((org.modeshape.jcr.api.query.QueryResult)result).getPlan();
      		logger.info(plan);
      		
      		Assert.assertEquals(2,countIndexes(plan));
      		Assert.assertEquals(result.getNodes().hasNext(),true); // FAILS, so nothing found using subselect and indexes
      		
      		
      		Query q2 = session.getWorkspace().getQueryManager().createQuery(
      		"SELECT A.* FROM [nt:typeWithReference] AS A\n" +
      		"JOIN [nt:typeWithSysName] AS B ON A.referenceId  = B.[jcr:uuid] \n" +
      		"WHERE B.sysName = $sysName \n"
      		,Query.JCR_SQL2);
      		q2.bindValue("sysName", session.getValueFactory().createValue("X"));
      		
      		logger.info(q2.getStatement());
      		
      		QueryResult result2 = q2.execute();
      		
      		String plan2 = ((org.modeshape.jcr.api.query.QueryResult)result2).getPlan();
      		logger.info(plan2);
      		
      		Assert.assertEquals(2,countIndexes(plan2)); // FAILS, so index that should be used is not because of wrong access order
      		Assert.assertEquals(true,result2.getNodes().hasNext());
      	}
      	
      	private int countIndexes(String plan){
      		
      		Pattern p = Pattern.compile("INDEX_USED=true");
      		Matcher m = p.matcher(plan);
      		int count = 0;
      		while (m.find()){
      			count +=1;
      		}
      		return count;
      	}
      	
      	protected IndexManager indexManager(){
      		
      		if (!(session instanceof org.modeshape.jcr.api.Session)){
      			return null;
      		}
      		
      		try {
      			return ((org.modeshape.jcr.api.Session) session).getWorkspace().getIndexManager();
      		} catch (RepositoryException ex) {
      			logger.error(ex.toString(), ex);
      		}
      		return null;
      	}
      	
      	protected void ensureIndex(IndexManager manager, String indexName, IndexDefinition.IndexKind kind, String providerName, String indexedNodeType, String desc, String workspaceNamePattern, String propertyName, int propertyType) throws RepositoryException {
      
      
      		if (manager.getIndexDefinitions().containsKey(indexName)){
      			return;
      		}
      		
      		logger.info("registering index on property "+propertyName+", type "+indexedNodeType);
      		
      		// Create the index template ...
      		IndexDefinitionTemplate template = manager.createIndexDefinitionTemplate();
      		template.setName(indexName);
      		template.setKind(kind);
      		template.setNodeTypeName(indexedNodeType);
      		template.setProviderName(providerName);
      		if (workspaceNamePattern != null) {
      			template.setWorkspaceNamePattern(workspaceNamePattern);
      		} else {
      			template.setAllWorkspaces();
      		}
      		if (desc != null) {
      			template.setDescription(desc);
      		}
      		
      		// Set up the columns ...
      		IndexColumnDefinition colDefn = manager.createIndexColumnDefinitionTemplate().setPropertyName(propertyName).setColumnType(propertyType);
      		template.setColumnDefinitions(colDefn);
      
      		// Register the index ...
      		manager.registerIndex(template, true);
      	}
      	
      	private void regNodeType(Session repoSession, String typeName) throws RepositoryException{
      			NodeTypeManager mgr = repoSession.getWorkspace().getNodeTypeManager();
      
      			// Create a template for the node type ...
      			NodeTypeTemplate type = mgr.createNodeTypeTemplate();
      			type.setName(typeName);
      			type.setDeclaredSuperTypeNames(new String[]{CrConstants.JCR_NT_UNSTRUCTURED});
      			type.setAbstract(false);
      			type.setOrderableChildNodes(true);
      			type.setMixin(false);
      			type.setQueryable(true);
      			mgr.registerNodeType(type, true);
      	}
      }
      
      

              rhauch Randall Hauch (Inactive)
              bes82 Bjoern Schmidt (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

                Created:
                Updated:
                Resolved: