apoc.refactor.rename.Rename Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of apoc-core Show documentation
Show all versions of apoc-core Show documentation
Core package for Neo4j Procedures
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package apoc.refactor.rename;
import apoc.Pools;
import apoc.periodic.BatchAndTotalResult;
import apoc.periodic.Periodic;
import apoc.util.MapUtil;
import apoc.util.Util;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.graphdb.Label;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.RelationshipType;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.schema.ConstraintDefinition;
import org.neo4j.graphdb.schema.IndexDefinition;
import org.neo4j.logging.Log;
import org.neo4j.procedure.Context;
import org.neo4j.procedure.Description;
import org.neo4j.procedure.Mode;
import org.neo4j.procedure.Name;
import org.neo4j.procedure.Procedure;
import org.neo4j.procedure.TerminationGuard;
/**
* @author AgileLARUS
*
* @since 03-04-2017
*/
public class Rename {
@Context
public GraphDatabaseService db;
@Context
public Log log;
@Context
public TerminationGuard terminationGuard;
@Context
public Transaction transaction;
@Context
public Pools pools;
@Context
public Transaction tx;
/**
* Rename the Label of a node by creating a new one and deleting the old.
*/
@Procedure(name = "apoc.refactor.rename.label", mode = Mode.WRITE)
@Description(
"Renames the given label from `oldLabel` to `newLabel` for all `NODE` values.\n"
+ "If a `LIST` is provided, the renaming is applied to the `NODE` values within this `LIST` only.")
public Stream label(
@Name("oldLabel") String oldLabel,
@Name("newLabel") String newLabel,
@Name(value = "nodes", defaultValue = "[]") List nodes) {
nodes = nodes.stream().map(n -> Util.rebind(tx, n)).collect(Collectors.toList());
oldLabel = Util.sanitize(oldLabel);
newLabel = Util.sanitize(newLabel);
String cypherIterate = nodes != null && !nodes.isEmpty()
? "UNWIND $nodes AS n WITH n WHERE n:`" + oldLabel + "` RETURN n"
: "MATCH (n:`" + oldLabel + "`) RETURN n";
String cypherAction = "REMOVE n:`" + oldLabel + "` SET n:`" + newLabel + "`";
Map parameters = MapUtil.map(
"batchSize", 100000, "parallel", true, "iterateList", true, "params", MapUtil.map("nodes", nodes));
return getResultOfBatchAndTotalWithInfo(
newPeriodic().iterate(cypherIterate, cypherAction, parameters), oldLabel, null, null);
}
/**
* Rename the Relationship Type by creating a new one and deleting the old.
*/
@Procedure(name = "apoc.refactor.rename.type", mode = Mode.WRITE)
@Description(
"Renames all `RELATIONSHIP` values with type `oldType` to `newType`.\n"
+ "If a `LIST` is provided, the renaming is applied to the `RELATIONSHIP` values within this `LIST` only.")
public Stream type(
@Name("oldType") String oldType,
@Name("newType") String newType,
@Name(value = "rels", defaultValue = "[]") List rels,
@Name(value = "config", defaultValue = "{}") Map config) {
rels = rels.stream().map(r -> Util.rebind(tx, r)).collect(Collectors.toList());
newType = Util.sanitize(newType);
oldType = Util.sanitize(oldType);
String cypherIterate = rels != null && !rels.isEmpty()
? "UNWIND $rels AS oldRel WITH oldRel WHERE type(oldRel)=\"" + oldType
+ "\" RETURN oldRel,startNode(oldRel) as a,endNode(oldRel) as b"
: "MATCH (a)-[oldRel:`" + oldType + "`]->(b) RETURN oldRel,a,b";
String cypherAction = "CREATE(a)-[newRel:`" + newType + "`]->(b)" + "SET newRel+=oldRel DELETE oldRel";
final Map params = MapUtil.map("rels", rels);
Map parameters = getPeriodicConfig(config, params);
return getResultOfBatchAndTotalWithInfo(
newPeriodic().iterate(cypherIterate, cypherAction, parameters), null, oldType, null);
}
private Map getPeriodicConfig(Map config, Map params) {
if (config == null) {
config = Collections.emptyMap();
}
if (params == null) {
params = Collections.emptyMap();
}
final int batchSize = Util.toInteger(config.getOrDefault("batchSize", 100000));
final int concurrency = Util.toInteger(
config.getOrDefault("concurrency", Runtime.getRuntime().availableProcessors()));
final int retries = Util.toInteger(config.getOrDefault("retries", 0));
final boolean parallel = Util.toBoolean(config.getOrDefault("parallel", true));
final String batchMode = config.getOrDefault("batchMode", "BATCH").toString();
return MapUtil.map(
"batchSize",
batchSize,
"retries",
retries,
"parallel",
parallel,
"batchMode",
batchMode,
"concurrency",
concurrency,
"params",
params);
}
/**
* Rename property of a node by creating a new one and deleting the old.
*/
@Procedure(name = "apoc.refactor.rename.nodeProperty", mode = Mode.WRITE)
@Description(
"Renames the given property from `oldName` to `newName` for all `NODE` values.\n"
+ "If a `LIST` is provided, the renaming is applied to the `NODE` values within this `LIST` only.")
public Stream nodeProperty(
@Name("oldName") String oldName,
@Name("newName") String newName,
@Name(value = "nodes", defaultValue = "[]") List nodes,
@Name(value = "config", defaultValue = "{}") Map config) {
nodes = nodes.stream().map(n -> Util.rebind(tx, n)).collect(Collectors.toList());
oldName = Util.sanitize(oldName);
newName = Util.sanitize(newName);
String cypherIterate = nodes != null && !nodes.isEmpty()
? "UNWIND $nodes AS n WITH n WHERE n.`" + oldName + "` IS NOT NULL return n"
: "match (n) where n.`" + oldName + "` IS NOT NULL return n";
String cypherAction =
"WITH n, n. `" + oldName + "` AS propVal REMOVE n.`" + oldName + "` SET n.`" + newName + "` = propVal";
final Map params = MapUtil.map("nodes", nodes);
Map parameters = getPeriodicConfig(config, params);
return getResultOfBatchAndTotalWithInfo(
newPeriodic().iterate(cypherIterate, cypherAction, parameters), null, null, oldName);
}
/**
* Rename property of a relationship by creating a new one and deleting the old.
*/
@Procedure(name = "apoc.refactor.rename.typeProperty", mode = Mode.WRITE)
@Description(
"Renames the given property from `oldName` to `newName` for all `RELATIONSHIP` values.\n"
+ "If a `LIST` is provided, the renaming is applied to the `RELATIONSHIP` values within this `LIST` only.")
public Stream typeProperty(
@Name("oldName") String oldName,
@Name("newName") String newName,
@Name(value = "rels", defaultValue = "[]") List rels,
@Name(value = "config", defaultValue = "{}") Map config) {
rels = rels.stream().map(r -> Util.rebind(tx, r)).collect(Collectors.toList());
newName = Util.sanitize(newName);
oldName = Util.sanitize(oldName);
String cypherIterate = rels != null && !rels.isEmpty()
? "UNWIND $rels AS r WITH r WHERE r.`" + oldName + "` IS NOT NULL return r"
: "match ()-[r]->() where r.`" + oldName + "` IS NOT NULL return r";
String cypherAction =
"WITH r, r. `" + oldName + "` AS propVal REMOVE r.`" + oldName + "` SET r.`" + newName + "`= propVal";
final Map params = MapUtil.map("rels", rels);
Map parameters = getPeriodicConfig(config, params);
return getResultOfBatchAndTotalWithInfo(
newPeriodic().iterate(cypherIterate, cypherAction, parameters), null, null, oldName);
}
/*
* create a properly initialized Periodic instance by setting all the required @Context attributes
*/
private Periodic newPeriodic() {
Periodic periodic = new Periodic();
periodic.db = this.db;
periodic.log = this.log;
periodic.terminationGuard = this.terminationGuard;
periodic.pools = this.pools;
periodic.tx = this.tx;
return periodic;
}
/*
* Create the response for rename apoc with impacted constraints and indexes
*/
private Stream getResultOfBatchAndTotalWithInfo(
Stream iterate, String label, String rel, String prop) {
List constraints = new ArrayList<>();
List indexes = new ArrayList<>();
if (label != null) {
Iterable constraintsForLabel =
transaction.schema().getConstraints(Label.label(label));
constraintsForLabel.forEach((c) -> {
constraints.add(c.toString());
});
Iterable idxs = Util.getIndexes(transaction, Label.label(label));
idxs.forEach((i) -> {
indexes.add(i.toString());
});
}
if (rel != null) {
Iterable constraintsForRel =
transaction.schema().getConstraints(RelationshipType.withName(rel));
constraintsForRel.forEach((c) -> {
constraints.add(c.toString());
});
}
if (prop != null) {
Iterable constraintsForProps =
transaction.schema().getConstraints();
constraintsForProps.forEach((c) -> {
c.getPropertyKeys().forEach((p) -> {
if (p.equals(prop)) {
constraints.add(c.toString());
}
});
});
Iterable idxs = Util.getIndexes(transaction);
idxs.forEach((i) -> {
i.getPropertyKeys().forEach((p) -> {
if (p.equals(prop)) {
indexes.add(i.toString());
}
});
});
}
Optional targetLongList = iterate.findFirst();
BatchAndTotalResultWithInfo result = new BatchAndTotalResultWithInfo(targetLongList, constraints, indexes);
return Stream.of(result);
}
public class BatchAndTotalResultWithInfo {
public long batches;
public long total;
public long timeTaken;
public long committedOperations;
public long failedOperations;
public long failedBatches;
public long retries;
public Map errorMessages;
public Map batch;
public Map operations;
public List constraints;
public List indexes;
public BatchAndTotalResultWithInfo(
Optional batchAndTotalResult, List constraints, List indexes) {
batchAndTotalResult.ifPresent(a -> {
this.batches = a.batches;
this.total = a.total;
this.timeTaken = a.timeTaken;
this.committedOperations = a.committedOperations;
this.failedOperations = a.failedOperations;
this.failedBatches = a.failedBatches;
this.retries = a.retries;
this.errorMessages = a.errorMessages;
this.batch = a.batch;
this.operations = a.operations;
});
this.constraints = constraints;
this.indexes = indexes;
}
}
}