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

org.neo4j.dbms.database.CommunityTopologyGraphDbmsModel Maven / Gradle / Ivy

/*
 * Copyright (c) "Neo4j"
 * Neo4j Sweden AB [http://neo4j.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.dbms.database;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.NotFoundException;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.Transaction;
import org.neo4j.kernel.database.DatabaseIdFactory;
import org.neo4j.kernel.database.NamedDatabaseId;

public class CommunityTopologyGraphDbmsModel implements TopologyGraphDbmsModel
{
    protected final Transaction tx;

    public CommunityTopologyGraphDbmsModel( Transaction tx )
    {
        this.tx = tx;
    }

    public Map getAllDatabaseAccess()
    {
        return tx.findNodes( DATABASE_LABEL ).stream()
                 .collect( Collectors.toMap( CommunityTopologyGraphDbmsModel::getDatabaseId,
                                             CommunityTopologyGraphDbmsModel::getDatabaseAccess ) );
    }

    private static TopologyGraphDbmsModel.DatabaseAccess getDatabaseAccess( Node databaseNode )
    {
        var accessString = (String) databaseNode.getProperty( DATABASE_ACCESS_PROPERTY, TopologyGraphDbmsModel.DatabaseAccess.READ_WRITE.toString() );
        return Enum.valueOf( TopologyGraphDbmsModel.DatabaseAccess.class, accessString );
    }

    @Override
    public Optional getDatabaseIdByAlias( String databaseName )
    {
        return getDatabaseIdByAlias0( databaseName )
                .or( () -> getDatabaseIdBy( DATABASE_NAME_PROPERTY, databaseName ) );
    }

    @Override
    public Optional getDatabaseIdByUUID( UUID uuid )
    {
        return getDatabaseIdBy( DATABASE_UUID_PROPERTY, uuid.toString() );
    }

    @Override
    public Map getAllDatabaseAliases()
    {
        var allDbNames = getAllDatabaseIds().stream()
                                            .collect( Collectors.toMap( NamedDatabaseId::name, Function.identity() ) );
        var allDbAliases = getAllDatabaseAliases0();

        var all = new HashMap();
        all.putAll( allDbNames );
        all.putAll( allDbAliases );
        return Map.copyOf( all );
    }

    @Override
    public Set getAllDatabaseIds()
    {
        return tx.findNodes( DATABASE_LABEL ).stream()
                     .map( CommunityTopologyGraphDbmsModel::getDatabaseId )
                     .collect( Collectors.toUnmodifiableSet() );
    }

    private Map getAllDatabaseAliases0()
    {
        return tx.findNodes( DATABASE_NAME_LABEL ).stream()
                 .flatMap( alias -> getTargetedDatabase( alias )
                         .flatMap( db -> aliasDatabaseIdPair( alias, db ) ).stream() )
                 .collect( Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue ) );
    }

    private Optional> aliasDatabaseIdPair( Node alias, NamedDatabaseId targetedDatabase )
    {
        return ignoreConcurrentDeletes( () ->
        {
            var aliasName = getPropertyOnNode( DATABASE_NAME, alias, NAME_PROPERTY );
            return Optional.of( Map.entry( aliasName, targetedDatabase ) );
        } );
    }

    private Optional getDatabaseIdByAlias0( String databaseName )
    {
        var node = Optional.ofNullable( tx.findNode( DATABASE_NAME_LABEL, NAME_PROPERTY, databaseName ) );
        return node.flatMap( CommunityTopologyGraphDbmsModel::getTargetedDatabase );
    }

    private Optional getDatabaseIdBy( String propertyKey, String propertyValue )
    {
        try
        {
            var node = tx.findNode( DATABASE_LABEL, propertyKey, propertyValue );

            if ( node == null )
            {
                return Optional.empty();
            }

            var databaseName = getPropertyOnNode( DATABASE_LABEL.name(), node, DATABASE_NAME_PROPERTY );
            var databaseUuid = getPropertyOnNode( DATABASE_LABEL.name(), node, DATABASE_UUID_PROPERTY );

            return Optional.of( DatabaseIdFactory.from( databaseName, UUID.fromString( databaseUuid ) ) );
        }
        catch ( RuntimeException e )
        {
            throw e;
        }
        catch ( Exception e )
        {
            throw new RuntimeException( e );
        }
    }

    /**
     * *Note* may return `Optional.empty`.
     *
     * It s semantically invalid for an alias to *not* have target, but we ignore it because of the possibility of concurrent deletes.
     */
    private static Optional getTargetedDatabase( Node aliasNode )
    {
        return ignoreConcurrentDeletes( () ->
        {
            var targetDatabases = StreamSupport.stream( aliasNode.getRelationships( Direction.OUTGOING, TARGETS_RELATIONSHIP ).spliterator(), false )
                                               .collect( Collectors.toList() ); // Must be collected to exhaust the underlying iterator

            return targetDatabases.stream().findFirst()
                                  .map( Relationship::getEndNode )
                                  .map( CommunityTopologyGraphDbmsModel::getDatabaseId );
        } );
    }

    private static NamedDatabaseId getDatabaseId( Node databaseNode )
    {
        var name = (String) databaseNode.getProperty( DATABASE_NAME_PROPERTY );
        var uuid = UUID.fromString( (String) databaseNode.getProperty( DATABASE_UUID_PROPERTY ) );
        return DatabaseIdFactory.from( name, uuid );
    }

    private static String getPropertyOnNode( String labelName, Node node, String key )
    {
        var value = node.getProperty( key );
        if ( value == null )
        {
            throw new IllegalStateException( String.format( "%s has no property %s.", labelName, key ) );
        }
        if ( !(value instanceof String) )
        {
            throw new IllegalStateException( String.format( "%s has non String property %s.", labelName, key ) );
        }
        return (String) value;
    }

    private static  Optional ignoreConcurrentDeletes( Supplier> operation )
    {
        try
        {
            return operation.get();
        }
        catch ( NotFoundException e )
        {
            return Optional.empty();
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy