All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.neo4j.shell.kernel.apps.IndexProviderShellApp Maven / Gradle / Ivy

There is a newer version: 3.3.2
Show newest version
/*
 * Copyright (c) 2002-2016 "Neo Technology,"
 * Network Engine for Objects in Lund AB [http://neotechnology.com]
 *
 * This file is part of Neo4j.
 *
 * Neo4j is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
package org.neo4j.shell.kernel.apps;

import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.regex.Pattern;

import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.PropertyContainer;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.graphdb.index.IndexManager;
import org.neo4j.helpers.Service;
import org.neo4j.shell.App;
import org.neo4j.shell.AppCommandParser;
import org.neo4j.shell.Continuation;
import org.neo4j.shell.OptionDefinition;
import org.neo4j.shell.OptionValueType;
import org.neo4j.shell.Output;
import org.neo4j.shell.Session;
import org.neo4j.shell.ShellException;
import org.neo4j.shell.util.json.JSONException;
import org.neo4j.shell.util.json.JSONObject;

@Service.Implementation( App.class )
public class IndexProviderShellApp extends TransactionProvidingApp
{
    {
        addOptionDefinition( "g", new OptionDefinition( OptionValueType.NONE,
                "Get entities for the given key and value" ) );
        addOptionDefinition( "q", new OptionDefinition( OptionValueType.NONE,
                "Get entities for the given query" ) );
        addOptionDefinition( "i", new OptionDefinition( OptionValueType.NONE,
                "Index the current entity with a key and (optionally) value. " +
                "If no value is given the property value for the key is " +
                "used" ) );
        addOptionDefinition( "r", new OptionDefinition( OptionValueType.NONE,
                "Removes a key-value pair for the current entity from the index. " +
                "Key and value are optional" ) );
        addOptionDefinition( "c", OPTION_DEF_FOR_C );
        addOptionDefinition( "cd", new OptionDefinition( OptionValueType.NONE,
                "Does a 'cd' command to the returned node. " +
                "Could also be done using the -c option. (Implies -g)" ) );
        addOptionDefinition( "ls", new OptionDefinition( OptionValueType.NONE,
                "Does a 'ls' command on the returned entities. " +
                "Could also be done using the -c option. (Implies -g)" ) );
        addOptionDefinition( "create", new OptionDefinition( OptionValueType.NONE,
                "Creates a new index with a set of configuration parameters" ) );
        addOptionDefinition( "get-config", new OptionDefinition( OptionValueType.NONE,
                "Displays the configuration for an index" ) );
        addOptionDefinition( "set-config", new OptionDefinition( OptionValueType.NONE,
                "EXPERT, USE WITH CARE: Set one configuration parameter for an index (remove if no value)" ) );
        addOptionDefinition( "t", new OptionDefinition( OptionValueType.MUST,
                "The type of index, either Node or Relationship" ) );
        addOptionDefinition( "indexes", new OptionDefinition( OptionValueType.NONE, "Lists all index names" ) );
        addOptionDefinition( "delete", new OptionDefinition( OptionValueType.NONE, "Deletes an index" ) );
    }

    @Override
    public String getName()
    {
        return "index";
    }

    @Override
    public String getDescription()
    {
        return "Access the legacy indexes for your Neo4j graph database. " +
        "Use -g for getting nodes, -i and -r to manipulate.\nExamples:\n" +
        "$ index -i persons name  (will index property 'name' with its value for current node in the 'persons' index)\n" +
        "$ index -g persons name \"Thomas A. Anderson\"  (will get nodes matching that name from the 'persons' index)\n" +
        "$ index -q persons \"name:'Thomas*'\"  (will get nodes with names that start with Thomas)\n" +
        "$ index --cd persons name \"Agent Smith\"  (will 'cd' to the 'Agent Smith' node from the 'persons' index).\n\n" +
        "EXPERT, USE WITH CARE. NOTE THAT INDEX DATA MAY BECOME INVALID AFTER CONFIGURATION CHANGES:\n" +
        "$ index --set-config accounts type fulltext  (will set parameter 'type'='fulltext' for 'accounts' index).\n" +
        "$ index --set-config accounts to_lower_case  (will remove parameter 'to_lower_case' from 'accounts' index).\n" +
        "$ index -t Relationship --delete friends  (will delete the 'friends' relationship index).";
    }

    @Override
    protected Continuation exec( AppCommandParser parser, Session session, Output out )
            throws ShellException, RemoteException
    {
        boolean doCd = parser.options().containsKey( "cd" );
        boolean doLs = parser.options().containsKey( "ls" );
        boolean query = parser.options().containsKey( "q" );
        boolean get = parser.options().containsKey( "g" ) || query || doCd || doLs;
        boolean index = parser.options().containsKey( "i" );
        boolean remove = parser.options().containsKey( "r" );
        boolean getConfig = parser.options().containsKey( "get-config" );
        boolean create = parser.options().containsKey( "create" );
        boolean setConfig = parser.options().containsKey( "set-config" );
        boolean delete = parser.options().containsKey( "delete" );
        boolean indexes = parser.options().containsKey( "indexes" );
        int count = boolCount( get, index, remove, getConfig, create, setConfig, delete, indexes );
        if ( count != 1 )
        {
            throw new ShellException( "Supply one of: -g, -i, -r, --get-config, --set-config, --create, --delete, --indexes" );
        }

        if ( get )
        {
            String commandToRun = parser.options().get( "c" );
            Collection commandsToRun = new ArrayList();
            boolean specialCommand = false;
            if ( doCd || doLs )
            {
                specialCommand = true;
                if ( doCd )
                {
                    commandsToRun.add( "cd -a $i" );
                }
                else if ( doLs )
                {
                    commandsToRun.add( "ls $i" );
                }
            }
            else if ( commandToRun != null )
            {
                commandsToRun.addAll( Arrays.asList( commandToRun.split( Pattern.quote( "&&" ) ) ) );
            }

            if ( getIndex( getIndexName( parser ), getEntityType( parser ), out ) == null )
            {
                return Continuation.INPUT_COMPLETE;
            }

            IndexHits result = query ? query( parser, out ) : get( parser, out );
            try
            {
                for ( PropertyContainer hit : result )
                {
                    printAndInterpretTemplateLines( commandsToRun, false, !specialCommand, NodeOrRelationship.wrap( hit ),
                            getServer(), session, out );
                }
            }
            finally
            {
                result.close();
            }
        }
        else if ( index )
        {
            index( parser, session, out );
        }
        else if ( remove )
        {
            if ( getIndex( getIndexName( parser ), Node.class, out ) == null )
            {
                return null;
            }
            remove( parser, session, out );
        }
        else if ( getConfig )
        {
            displayConfig( parser, out );
        }
        else if ( create )
        {
            createIndex( parser, out );
        }
        else if ( setConfig )
        {
            setConfig( parser, out );
        }
        else if ( delete )
        {
            deleteIndex( parser, out );
        }

        if ( indexes )
        {
            listIndexes( out );
        }

        return Continuation.INPUT_COMPLETE;
    }

    private String getIndexName( AppCommandParser parser ) throws ShellException
    {
        return parser.argument( 0, "Index name not supplied" );
    }

    private void listIndexes( Output out ) throws RemoteException
    {
        out.println( "Node indexes:" );
        for ( String name : getServer().getDb().index().nodeIndexNames() )
        {
            out.println( "  " + name );
        }
        out.println( "" );
        out.println( "Relationship indexes:" );
        for ( String name : getServer().getDb().index().relationshipIndexNames() )
        {
            out.println( "  " + name );
        }
    }

    private void deleteIndex( AppCommandParser parser, Output out ) throws RemoteException, ShellException
    {
        Index index = getIndex( getIndexName( parser ), getEntityType( parser ), out );
        if ( index != null )
        {
            index.delete();
        }
    }

    private void setConfig( AppCommandParser parser, Output out ) throws ShellException, RemoteException
    {
        String indexName = getIndexName( parser );
        String key = parser.argument( 1, "Key not supplied" );
        String value = parser.arguments().size() > 2 ? parser.arguments().get( 2 ) : null;

        Class entityType = getEntityType( parser );
        Index index = getIndex( indexName, entityType, out );
        if ( index == null )
        {
            return;
        }
        String oldValue = value != null ?
                getServer().getDb().index().setConfiguration( index, key, value ) :
                getServer().getDb().index().removeConfiguration( index, key );
        printWarning( out );
    }

    private void printWarning( Output out ) throws RemoteException
    {
        out.println( "INDEX CONFIGURATION CHANGED, INDEX DATA MAY BE INVALID" );
    }

    private void createIndex( AppCommandParser parser, Output out ) throws RemoteException, ShellException
    {
        String indexName = getIndexName( parser );
        Class entityType = getEntityType( parser );
        if ( getIndex( indexName, entityType, null ) != null )
        {
            out.println( entityType.getClass().getSimpleName() + " index '" + indexName + "' already exists" );
            return;
        }

        Map config;
        try
        {
            config = parser.arguments().size() >= 2 ? parseJSONMap( parser.arguments().get( 1 ) ) : null;
        }
        catch ( JSONException e )
        {
            throw ShellException.wrapCause( e );
        }

        if ( entityType.equals( Node.class ) )
        {
            Index index = config != null ? getServer().getDb().index().forNodes( indexName, config ) :
                    getServer().getDb().index().forNodes( indexName );
        }
        else
        {
            Index index = config != null ? getServer().getDb().index().forRelationships( indexName, config ) :
                getServer().getDb().index().forRelationships( indexName );
        }
    }

    private  Index getIndex( String indexName, Class type, Output out )
            throws RemoteException
    {
        IndexManager index = getServer().getDb().index();
        boolean exists = (type.equals( Node.class ) && index.existsForNodes( indexName )) ||
                (type.equals( Relationship.class ) && index.existsForRelationships( indexName ));
        if ( !exists )
        {
            if ( out != null )
            {
                out.println( "No such " + type.getSimpleName().toLowerCase() + " index '" + indexName + "'" );
            }
            return null;
        }
        return (Index) (type.equals( Node.class ) ? index.forNodes( indexName ) : index.forRelationships( indexName ));
    }

    private void displayConfig( AppCommandParser parser, Output out )
            throws RemoteException, ShellException
    {
        String indexName = getIndexName( parser );
        Index index = getIndex( indexName, getEntityType( parser ), out );
        if ( index == null )
        {
            return;
        }
        try
        {
            out.println( new JSONObject( getServer().getDb().index().getConfiguration( index ) ).toString( 4 ) );
        }
        catch ( JSONException e )
        {
            throw ShellException.wrapCause( e );
        }
    }

    private Class getEntityType( AppCommandParser parser ) throws ShellException
    {
        String type = parser.options().get( "t" );
        type = type != null ? type.toLowerCase() : null;
        if ( type == null || type.equals( "node" ) )
        {
            return Node.class;
        }
        else if ( type.equals( "relationship" ) )
        {
            return Relationship.class;
        }
        throw new ShellException( "'type' expects one of [Node, Relationship]" );
    }

    private int boolCount( boolean... bools )
    {
        int count = 0;
        for ( boolean bool : bools )
        {
            if ( bool )
            {
                count++;
            }
        }
        return count;
    }

    private IndexHits get( AppCommandParser parser, Output out ) throws ShellException, RemoteException
    {
        String index = getIndexName( parser );
        String key = parser.argument( 1, "Key not supplied" );
        String value = parser.argument( 2, "Value not supplied" );
        Index theIndex = getIndex( index, getEntityType( parser ), out );
        return theIndex.get( key, value );
    }

    private IndexHits query( AppCommandParser parser, Output out ) throws RemoteException, ShellException
    {
        String index = getIndexName( parser );
        String query1 = parser.argument( 1, "Key not supplied" );
        String query2 = parser.argumentWithDefault( 2, null );
        Index theIndex = getIndex( index, getEntityType( parser ), out );
        return query2 != null ? theIndex.query( query1, query2 ) : theIndex.query( query1 );
    }

    private void index( AppCommandParser parser, Session session, Output out ) throws ShellException, RemoteException
    {
        NodeOrRelationship current = getCurrent( session );
        String index = getIndexName( parser );
        String key = parser.argument( 1, "Key not supplied" );
        Object value = parser.arguments().size() > 2 ? parser.arguments().get( 2 ) : current.getProperty( key, null );
        if ( value == null )
        {
            throw new ShellException( "No value to index" );
        }
        Index theIndex = current.isNode() ? getServer().getDb().index().forNodes( index ) :
                getServer().getDb().index().forRelationships( index );
        theIndex.add( current.asPropertyContainer(), key, value );
    }

    private void remove( AppCommandParser parser, Session session, Output out ) throws ShellException, RemoteException
    {
        NodeOrRelationship current = getCurrent( session );
        String index = getIndexName( parser );
        String key = parser.argumentWithDefault( 1, null );
        Object value = null;
        if ( key != null )
        {
            value = parser.argumentWithDefault( 2, null );
        }
        Index theIndex;
        if ( current.isNode() )
        {
            theIndex = getIndex( index, Node.class, out );
        }
        else
        {
            theIndex = getIndex( index, Relationship.class, out );
        }
        if ( theIndex != null )
        {
            if ( key != null && value != null )
            {
                theIndex.remove( current.asPropertyContainer(), key, value );
            }
            else if ( key != null )
            {
                theIndex.remove( current.asPropertyContainer(), key );
            }
            else
            {
                theIndex.remove( current.asPropertyContainer() );
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy