Skip to content

Commit

Permalink
Composite support for indexProcedures
Browse files Browse the repository at this point in the history
  • Loading branch information
ragadeeshu committed Mar 21, 2017
1 parent 4798f68 commit 265800e
Show file tree
Hide file tree
Showing 18 changed files with 122 additions and 61 deletions.
Expand Up @@ -45,7 +45,7 @@ class TransactionBoundPlanContext(tc: TransactionalContextWrapper)
val labelId = tc.statement.readOperations().labelGetForName(labelName)
val propertyKeyId = tc.statement.readOperations().propertyKeyGetForName(propertyKey)

getOnlineIndex(tc.statement.readOperations().indexGetForLabelAndPropertyKey(SchemaDescriptorFactory.forLabel(labelId, propertyKeyId)))
getOnlineIndex(tc.statement.readOperations().indexGetForSchema(SchemaDescriptorFactory.forLabel(labelId, propertyKeyId)))
}

def hasIndexRule(labelName: String): Boolean = {
Expand Down
Expand Up @@ -457,7 +457,7 @@ final class TransactionBoundQueryContext(tc: TransactionalContextWrapper)
} catch {
case _: AlreadyIndexedException =>

val indexDescriptor = tc.statement.readOperations().indexGetForLabelAndPropertyKey( SchemaDescriptorFactory.forLabel(labelId, propertyKeyId))
val indexDescriptor = tc.statement.readOperations().indexGetForSchema( SchemaDescriptorFactory.forLabel(labelId, propertyKeyId))

if (tc.statement.readOperations().indexGetState(indexDescriptor) == InternalIndexState.FAILED)
throw new FailedIndexException(indexDescriptor.userDescription(tokenNameLookup))
Expand Down
Expand Up @@ -52,7 +52,7 @@ class TransactionBoundPlanContext(tc: TransactionalContextWrapper, logger: Inter
val labelId = tc.statement.readOperations().labelGetForName(labelName)
val propertyKeyId = tc.statement.readOperations().propertyKeyGetForName(propertyKey)

getOnlineIndex(tc.statement.readOperations().indexGetForLabelAndPropertyKey(SchemaDescriptorFactory.forLabel(labelId, propertyKeyId)))
getOnlineIndex(tc.statement.readOperations().indexGetForSchema(SchemaDescriptorFactory.forLabel(labelId, propertyKeyId)))
}

def hasIndexRule(labelName: String): Boolean = {
Expand Down
Expand Up @@ -462,7 +462,7 @@ final class TransactionBoundQueryContext(txContext: TransactionalContextWrapper)
IdempotentResult(txContext.statement.schemaWriteOperations().indexCreate( SchemaDescriptorFactory.forLabel(labelId, propertyKeyId)))
} catch {
case _: AlreadyIndexedException =>
val indexDescriptor = txContext.statement.readOperations().indexGetForLabelAndPropertyKey(SchemaDescriptorFactory.forLabel(labelId, propertyKeyId))
val indexDescriptor = txContext.statement.readOperations().indexGetForSchema(SchemaDescriptorFactory.forLabel(labelId, propertyKeyId))
if(txContext.statement.readOperations().indexGetState(indexDescriptor) == InternalIndexState.FAILED)
throw new FailedIndexException(indexDescriptor.userDescription(tokenNameLookup))
IdempotentResult(indexDescriptor, wasCreated = false)
Expand Down
Expand Up @@ -48,7 +48,7 @@ class TransactionBoundPlanContext(tc: TransactionalContextWrapper, logger: Inter

def indexGet(labelName: String, propertyKeys: Seq[String]): Option[IndexDescriptor] = evalOrNone {
val descriptor = toLabelSchemaDescriptor(tc, labelName, propertyKeys)
getOnlineIndex(tc.statement.readOperations().indexGetForLabelAndPropertyKey(descriptor))
getOnlineIndex(tc.statement.readOperations().indexGetForSchema(descriptor))
}

def indexExistsForLabel(labelName: String): Boolean = {
Expand Down
Expand Up @@ -471,7 +471,7 @@ final class TransactionBoundQueryContext(val transactionalContext: Transactional
IdempotentResult(transactionalContext.statement.schemaWriteOperations().indexCreate( descriptor ))
} catch {
case _: AlreadyIndexedException =>
val indexDescriptor = transactionalContext.statement.readOperations().indexGetForLabelAndPropertyKey (SchemaDescriptorFactory.forLabel(descriptor.getLabelId, descriptor.getPropertyId));
val indexDescriptor = transactionalContext.statement.readOperations().indexGetForSchema (SchemaDescriptorFactory.forLabel(descriptor.getLabelId, descriptor.getPropertyId));
if(transactionalContext.statement.readOperations().indexGetState(indexDescriptor) == InternalIndexState.FAILED)
throw new FailedIndexException(indexDescriptor.userDescription(tokenNameLookup))
IdempotentResult(indexDescriptor, wasCreated = false)
Expand Down
Expand Up @@ -224,8 +224,8 @@ <EXCEPTION extends Exception> void relationshipVisit( long relId, RelationshipVi
//== SCHEMA OPERATIONS ======================
//===========================================

/** Returns the index rule for the given labelId and propertyKey. */
NewIndexDescriptor indexGetForLabelAndPropertyKey( LabelSchemaDescriptor descriptor )
/** Returns the index rule for the given LabelSchemaDescriptor. */
NewIndexDescriptor indexGetForSchema( LabelSchemaDescriptor descriptor )
throws SchemaRuleNotFoundException;

/** Get all indexes for a label. */
Expand Down
Expand Up @@ -54,20 +54,18 @@ public void awaitIndex( String indexSpecification, long timeout, TimeUnit timeou
{
IndexSpecifier index = parse( indexSpecification );
int labelId = getLabelId( index.label() );
int propertyKeyId = getPropertyId( index.property() );
//TODO: Support composite indexes
waitUntilOnline( getIndex( labelId, propertyKeyId, index ), index, timeout, timeoutUnits );
int[] propertyKeyIds = getPropertyIds( index.properties() );
waitUntilOnline( getIndex( labelId, propertyKeyIds, index ), index, timeout, timeoutUnits );
}

public void resampleIndex( String indexSpecification ) throws ProcedureException
{
IndexSpecifier index = parse( indexSpecification );
int labelId = getLabelId( index.label() );
int propertyKeyId = getPropertyId( index.property() );
//TODO: Support composite indexes
int[] propertyKeyIds = getPropertyIds( index.properties() );
try
{
triggerSampling( getIndex( labelId, propertyKeyId, index ) );
triggerSampling( getIndex( labelId, propertyKeyIds, index ) );
}
catch ( IndexNotFoundKernelException e )
{
Expand Down Expand Up @@ -95,24 +93,30 @@ private int getLabelId( String labelName ) throws ProcedureException
return labelId;
}

private int getPropertyId( String propertyKeyName ) throws ProcedureException
private int[] getPropertyIds( String[] propertyKeyNames ) throws ProcedureException
{
int propertyKeyId = operations.propertyKeyGetForName( propertyKeyName );
if ( propertyKeyId == ReadOperations.NO_SUCH_PROPERTY_KEY )
int[] propertyKeyIds = new int[propertyKeyNames.length];
for ( int i = 0; i < propertyKeyIds.length; i++ )
{
throw new ProcedureException( Status.Schema.PropertyKeyAccessFailed,
"No such property key %s", propertyKeyName );

int propertyKeyId = operations.propertyKeyGetForName( propertyKeyNames[i] );
if ( propertyKeyId == ReadOperations.NO_SUCH_PROPERTY_KEY )
{
throw new ProcedureException( Status.Schema.PropertyKeyAccessFailed, "No such property key %s",
propertyKeyNames );
}
propertyKeyIds[i] = propertyKeyId;
}
return propertyKeyId;
return propertyKeyIds;
}

private NewIndexDescriptor getIndex( int labelId, int propertyKeyId, IndexSpecifier index ) throws
private NewIndexDescriptor getIndex( int labelId, int[] propertyKeyIds, IndexSpecifier index ) throws
ProcedureException
{
try
{
return operations
.indexGetForLabelAndPropertyKey( SchemaDescriptorFactory.forLabel( labelId, propertyKeyId ) );
.indexGetForSchema( SchemaDescriptorFactory.forLabel( labelId, propertyKeyIds ) );
}
catch ( SchemaRuleNotFoundException e )
{
Expand Down
Expand Up @@ -19,9 +19,11 @@
*/
package org.neo4j.kernel.builtinprocs;

import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.neo4j.helpers.ArrayUtil;
import org.neo4j.helpers.collection.Pair;

import static java.lang.String.format;
Expand All @@ -31,25 +33,24 @@ public class IndexSpecifier

private final String specification;
private final String label;
private final String property;
private final String[] properties;

public IndexSpecifier( String specification )
{
this.specification = specification;
Pair<String, String> components = parse();
Pair<String,String[]> components = parse();
label = components.first();
//TODO: Support composite indexes
property = components.other();
properties = components.other();
}

public String label()
{
return label;
}

public String property()
public String[] properties()
{
return property;
return properties;
}

@Override
Expand All @@ -58,21 +59,28 @@ public String toString()
return specification;
}

private Pair<String, String> parse()
private Pair<String,String[]> parse()
{
Pattern pattern = Pattern.compile( "" +
":" + or( simpleIdentifier( "simpleLabel" ), complexIdentifier( "complexLabel" ) ) +
"\\(" + or( simpleIdentifier( "simpleProperty" ), complexIdentifier( "complexProperty" ) ) + "\\)"
// Note that this now matches all properties in a single group, in order to split them later.
Pattern pattern = Pattern.compile(
":" + or( identifier(true), qoutedIdentifier(true) ) + // Match the label
"\\((" + or( identifier(false), qoutedIdentifier(false) ) + // Match the first property
"(?:\\,\\s" + or( identifier(false), qoutedIdentifier(false) ) + ")*)\\)" // Match following properties
);
Matcher matcher = pattern.matcher( specification );
if ( !matcher.find() )
{
throw new IllegalArgumentException( "Cannot parse index specification " + specification );
}
return Pair.of(
either( matcher.group( "simpleLabel" ), matcher.group( "complexLabel" ) ),
either( matcher.group( "simpleProperty" ), matcher.group( "complexProperty" ) )
);
String label = either( matcher.group( 1 ), matcher.group( 2 ) );
String propertyString = matcher.group( 3 );
//Split string on commas, but ignore commas in quotes
String[] properties = propertyString.split(",\\s(?=(?:`[^`]*`)|[^`]*$)", -1);
for ( int i = 0; i < properties.length ; i++ )
{
properties[i] = properties[i].replace( "`", "" );
}
return Pair.of( label, properties );
}

private String either( String first, String second )
Expand All @@ -82,16 +90,30 @@ private String either( String first, String second )

private static String or( String first, String second )
{
return "(:?" + first + "|" + second + ")";
return "(?:" + first + "|" + second + ")";
}

private static String simpleIdentifier( String name )
private static String identifier( boolean capture )
{
return format( "(?<%s>[A-Za-z0-9_]+)", name );
if ( capture )
{
return "([A-Za-z0-9_]+)";
}
else
{
return "(?:[A-Za-z0-9_]+)";
}
}

private static String complexIdentifier( String name )
private static String qoutedIdentifier( boolean capture )
{
return format( "(?:`(?<%s>[^`]+)`)", name );
if ( capture )
{
return "(?:`([^`]+)`)";
}
else
{
return "(?:`(?:[^`]+)`)";
}
}
}
Expand Up @@ -570,7 +570,7 @@ public Cursor<PropertyItem> relationshipGetProperties( RelationshipItem relation

// <SchemaRead>
@Override
public NewIndexDescriptor indexGetForLabelAndPropertyKey( LabelSchemaDescriptor descriptor )
public NewIndexDescriptor indexGetForSchema( LabelSchemaDescriptor descriptor )
throws SchemaRuleNotFoundException
{
statement.assertOpen();
Expand Down
Expand Up @@ -325,7 +325,7 @@ private static NewIndexDescriptor getIndexDescriptor( ReadOperations readOperati
int[] propertyKeyIds = PropertyNameUtils.getPropertyIds( readOperations, index.getPropertyKeys() );
assertValidLabel( index.getLabel(), labelId );
assertValidProperties( index.getPropertyKeys(), propertyKeyIds );
return readOperations.indexGetForLabelAndPropertyKey( SchemaDescriptorFactory.forLabel( labelId, propertyKeyIds ) );
return readOperations.indexGetForSchema( SchemaDescriptorFactory.forLabel( labelId, propertyKeyIds ) );
}

private static void assertValidLabel( Label label, int labelId )
Expand Down
Expand Up @@ -71,7 +71,6 @@
import org.neo4j.kernel.api.legacyindex.AutoIndexing;
import org.neo4j.kernel.api.properties.Property;
import org.neo4j.kernel.api.schema_new.IndexQuery;
import org.neo4j.kernel.api.schema_new.LabelSchemaDescriptor;
import org.neo4j.kernel.api.schema_new.SchemaDescriptorFactory;
import org.neo4j.kernel.api.schema_new.index.NewIndexDescriptor;
import org.neo4j.kernel.api.security.SecurityContext;
Expand Down Expand Up @@ -619,7 +618,7 @@ private NewIndexDescriptor findAnyIndexByLabelAndProperty( ReadOperations readOp
final int labelId1 = labelId;
final int[] propertyKeyIds = new int[]{propertyId};
NewIndexDescriptor descriptor =
readOps.indexGetForLabelAndPropertyKey( SchemaDescriptorFactory.forLabel( labelId, propertyId ) );
readOps.indexGetForSchema( SchemaDescriptorFactory.forLabel( labelId, propertyId ) );

if ( readOps.indexGetState( descriptor ) == InternalIndexState.ONLINE )
{
Expand Down
Expand Up @@ -616,7 +616,7 @@ private NewIndexDescriptor indexDescriptor( ReadOperations readOperations, Index
int[] propertyKeyIds = getPropertyIds( readOperations, index.getPropertyKeys() );

LabelSchemaDescriptor descriptor = SchemaDescriptorFactory.forLabel( labelId, propertyKeyIds );
return readOperations.indexGetForLabelAndPropertyKey( descriptor );
return readOperations.indexGetForSchema( descriptor );
}

private Statement getStatement( GraphDatabaseAPI db )
Expand Down
Expand Up @@ -103,12 +103,12 @@ public void shouldLookUpTheIndexByLabelIdAndPropertyKeyId()
{
when( operations.labelGetForName( anyString() ) ).thenReturn( descriptor.getLabelId() );
when( operations.propertyKeyGetForName( anyString() ) ).thenReturn( descriptor.getPropertyId() );
when( operations.indexGetForLabelAndPropertyKey( anyObject() ) ).thenReturn( anyIndex );
when( operations.indexGetForSchema( anyObject() ) ).thenReturn( anyIndex );
when( operations.indexGetState( any( NewIndexDescriptor.class ) ) ).thenReturn( ONLINE );

procedure.awaitIndex( ":Person(name)", timeout, timeoutUnits );

verify( operations ).indexGetForLabelAndPropertyKey( descriptor );
verify( operations ).indexGetForSchema( descriptor );
}

@Test
Expand All @@ -118,7 +118,7 @@ public void shouldThrowAnExceptionIfTheIndexHasFailed()
{
when( operations.labelGetForName( anyString() ) ).thenReturn( 0 );
when( operations.propertyKeyGetForName( anyString() ) ).thenReturn( 0 );
when( operations.indexGetForLabelAndPropertyKey( anyObject() ) ).thenReturn( anyIndex );
when( operations.indexGetForSchema( anyObject() ) ).thenReturn( anyIndex );
when( operations.indexGetState( any( NewIndexDescriptor.class ) ) ).thenReturn( FAILED );

try
Expand All @@ -139,7 +139,7 @@ public void shouldThrowAnExceptionIfTheIndexDoesNotExist()
{
when( operations.propertyKeyGetForName( anyString() ) ).thenReturn( 0 );
when( operations.labelGetForName( anyString() ) ).thenReturn( 0 );
when( operations.indexGetForLabelAndPropertyKey( any() ) ).thenThrow(
when( operations.indexGetForSchema( any() ) ).thenThrow(
new SchemaRuleNotFoundException( INDEX_RULE, SchemaDescriptorFactory.forLabel( 0, 0 ) ) );

try
Expand All @@ -159,7 +159,7 @@ public void shouldBlockUntilTheIndexIsOnline() throws SchemaRuleNotFoundExceptio
{
when( operations.labelGetForName( anyString() ) ).thenReturn( 0 );
when( operations.propertyKeyGetForName( anyString() ) ).thenReturn( 0 );
when( operations.indexGetForLabelAndPropertyKey( anyObject() ) ).thenReturn( anyIndex );
when( operations.indexGetForSchema( anyObject() ) ).thenReturn( anyIndex );

AtomicReference<InternalIndexState> state = new AtomicReference<>( POPULATING );
when( operations.indexGetState( any( NewIndexDescriptor.class ) ) ).then( new Answer<InternalIndexState>()
Expand Down Expand Up @@ -198,7 +198,7 @@ public void shouldTimeoutIfTheIndexTakesTooLongToComeOnline()
{
when( operations.labelGetForName( anyString() ) ).thenReturn( 0 );
when( operations.propertyKeyGetForName( anyString() ) ).thenReturn( 0 );
when( operations.indexGetForLabelAndPropertyKey( anyObject() ) ).thenReturn( anyIndex );
when( operations.indexGetForSchema( anyObject() ) ).thenReturn( anyIndex );
when( operations.indexGetState( any( NewIndexDescriptor.class ) ) ).thenReturn( POPULATING );

AtomicReference<ProcedureException> exception = new AtomicReference<>();
Expand Down
Expand Up @@ -21,7 +21,11 @@

import org.junit.Test;

import java.util.Arrays;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.array;
import static org.hamcrest.Matchers.arrayContaining;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.fail;

Expand All @@ -42,7 +46,23 @@ public void shouldParseASimpleLabel()
@Test
public void shouldParseASimpleProperty()
{
assertThat( new IndexSpecifier( ":Person(a_Name_123)" ).property(), is( "a_Name_123" ) );
assertThat( new IndexSpecifier( ":Person(a_Name_123)" ).properties(), is( arrayContaining( "a_Name_123" ) ) );
}

@Test
public void shouldParseTwoProperties()
{
assertThat( new IndexSpecifier( ":Person(name, lastName)" ).properties(),
is( arrayContaining( "name", "lastName" ) ) );
}

@Test
public void shouldParseManyProperties()
{
String[] properties = new IndexSpecifier( ":Person(1, 2, 3, 4, 5, 6)" ).properties();
System.out.println(Arrays.toString(properties));
assertThat( properties,
is( arrayContaining( "1", "2", "3", "4", "5", "6" ) ) );
}

@Test
Expand All @@ -54,7 +74,8 @@ public void shouldParseANastyLabel()
@Test
public void shouldParseANastyProperty()
{
assertThat( new IndexSpecifier( ":Person(`(:!\"£$%^&*( )`)" ).property(), is( "(:!\"£$%^&*( )" ) );
assertThat( new IndexSpecifier( ":Person(`(:!\"£$%^&*( )`)" ).properties(),
is( arrayContaining( "(:!\"£$%^&*( )" ) ) );
}

@Test
Expand Down

0 comments on commit 265800e

Please sign in to comment.